From ffa0814a1d8cd6ba209d47d35878e53c158bd483 Mon Sep 17 00:00:00 2001 From: SergeyKozhukhov Date: Wed, 12 Jul 2023 23:58:14 +0300 Subject: [PATCH 1/7] First step: from callbacks to suspend functions and coroutines --- app/build.gradle | 8 +++- app/src/main/AndroidManifest.xml | 1 + .../otus/homework/coroutines/CatsPresenter.kt | 41 ++++++++++++------- .../otus/homework/coroutines/CatsService.kt | 3 +- .../java/otus/homework/coroutines/CatsView.kt | 11 ++++- .../otus/homework/coroutines/CrashMonitor.kt | 7 +++- .../homework/coroutines/CustomApplication.kt | 20 +++++++++ .../otus/homework/coroutines/DiContainer.kt | 7 +++- .../java/otus/homework/coroutines/Fact.kt | 25 ++++------- .../otus/homework/coroutines/MainActivity.kt | 11 +++-- .../homework/coroutines/PresenterScope.kt | 13 ++++++ .../homework/coroutines/StringProvider.kt | 8 ++++ .../homework/coroutines/StringProviderImpl.kt | 10 +++++ app/src/main/res/values/strings.xml | 2 + 14 files changed, 122 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/otus/homework/coroutines/CustomApplication.kt create mode 100644 app/src/main/java/otus/homework/coroutines/PresenterScope.kt create mode 100644 app/src/main/java/otus/homework/coroutines/StringProvider.kt create mode 100644 app/src/main/java/otus/homework/coroutines/StringProviderImpl.kt diff --git a/app/build.gradle b/app/build.gradle index 679dbba4..5d8825e0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,13 +4,13 @@ plugins { } android { - compileSdkVersion 30 + compileSdkVersion 33 buildToolsVersion "30.0.3" defaultConfig { applicationId "otus.homework.coroutines" minSdkVersion 23 - targetSdkVersion 30 + targetSdkVersion 33 versionCode 1 versionName "1.0" @@ -42,4 +42,8 @@ dependencies { implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'com.squareup.picasso:picasso:2.71828' + + + // coroutines + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fe34985b..cf25e683 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ { + private var job: Job? = null - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful && response.body() != null) { - _catsView?.populate(response.body()!!) + fun onInitComplete() { + if (job?.isActive == true) return + + job = scope.launch { + try { + val fact = catsService.getCatFact() + _catsView?.populate(fact) + } catch (e: Exception) { + if (e is SocketTimeoutException) { + _catsView?.warn(message = stringProvider.getString(R.string.timeout_server_error)) + } else { + CrashMonitor.trackWarning(e) + _catsView?.warn(message = e.messageOrDefault()) } } - - override fun onFailure(call: Call, t: Throwable) { - CrashMonitor.trackWarning() - } - }) + } } + private fun Exception.messageOrDefault() = + this.message ?: stringProvider.getString(R.string.default_request_error) + fun attachView(catsView: ICatsView) { _catsView = catsView } fun detachView() { _catsView = null + job?.cancel() + job = null } } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CatsService.kt b/app/src/main/java/otus/homework/coroutines/CatsService.kt index 479b2cfb..829b930c 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsService.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsService.kt @@ -1,10 +1,9 @@ package otus.homework.coroutines -import retrofit2.Call import retrofit2.http.GET interface CatsService { @GET("fact") - fun getCatFact() : Call + suspend fun getCatFact(): Fact } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CatsView.kt b/app/src/main/java/otus/homework/coroutines/CatsView.kt index 30ac2531..0fe17681 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsView.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsView.kt @@ -4,6 +4,7 @@ import android.content.Context import android.util.AttributeSet import android.widget.Button import android.widget.TextView +import android.widget.Toast import androidx.constraintlayout.widget.ConstraintLayout class CatsView @JvmOverloads constructor( @@ -12,7 +13,7 @@ class CatsView @JvmOverloads constructor( defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr), ICatsView { - var presenter :CatsPresenter? = null + var presenter: CatsPresenter? = null override fun onFinishInflate() { super.onFinishInflate() @@ -22,11 +23,17 @@ class CatsView @JvmOverloads constructor( } override fun populate(fact: Fact) { - findViewById(R.id.fact_textView).text = fact.text + findViewById(R.id.fact_textView).text = fact.fact + } + + override fun warn(message: String) { + Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } } interface ICatsView { fun populate(fact: Fact) + + fun warn(message: String) } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt b/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt index 32e6b018..77b8b708 100644 --- a/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt +++ b/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt @@ -1,10 +1,15 @@ package otus.homework.coroutines +import android.util.Log + object CrashMonitor { /** * Pretend this is Crashlytics/AppCenter */ - fun trackWarning() { + fun trackWarning(error: Exception) { + Log.e(TAG, "trackWarning: $error") } + + private const val TAG = "CrashMonitor" } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CustomApplication.kt b/app/src/main/java/otus/homework/coroutines/CustomApplication.kt new file mode 100644 index 00000000..b936b39a --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/CustomApplication.kt @@ -0,0 +1,20 @@ +package otus.homework.coroutines + +import android.app.Application +import android.content.Context + +class CustomApplication : Application() { + + lateinit var diContainer: DiContainer + + override fun onCreate() { + super.onCreate() + diContainer = DiContainer(this) + } + + companion object { + + fun diContainer(context: Context) = + (context.applicationContext as CustomApplication).diContainer + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/DiContainer.kt b/app/src/main/java/otus/homework/coroutines/DiContainer.kt index 23ddc3b2..1d00ffb9 100644 --- a/app/src/main/java/otus/homework/coroutines/DiContainer.kt +++ b/app/src/main/java/otus/homework/coroutines/DiContainer.kt @@ -1,9 +1,10 @@ package otus.homework.coroutines +import android.content.Context import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory -class DiContainer { +class DiContainer(private val context: Context) { private val retrofit by lazy { Retrofit.Builder() @@ -12,5 +13,7 @@ class DiContainer { .build() } - val service by lazy { retrofit.create(CatsService::class.java) } + val service: CatsService by lazy { retrofit.create(CatsService::class.java) } + + val stringProvider: StringProvider by lazy { StringProviderImpl(context) } } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/Fact.kt b/app/src/main/java/otus/homework/coroutines/Fact.kt index 15c6c7ae..4b2473aa 100644 --- a/app/src/main/java/otus/homework/coroutines/Fact.kt +++ b/app/src/main/java/otus/homework/coroutines/Fact.kt @@ -2,23 +2,12 @@ package otus.homework.coroutines import com.google.gson.annotations.SerializedName +// https://catfact.ninja/#/Facts/getFacts data class Fact( - @field:SerializedName("createdAt") - val createdAt: String, - @field:SerializedName("deleted") - val deleted: Boolean, - @field:SerializedName("_id") - val id: String, - @field:SerializedName("text") - val text: String, - @field:SerializedName("source") - val source: String, - @field:SerializedName("used") - val used: Boolean, - @field:SerializedName("type") - val type: String, - @field:SerializedName("user") - val user: String, - @field:SerializedName("updatedAt") - val updatedAt: String + + @field:SerializedName("fact") + val fact: String, + + @field:SerializedName("length") + val length: String ) \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/MainActivity.kt b/app/src/main/java/otus/homework/coroutines/MainActivity.kt index a9dafb3b..419031c1 100644 --- a/app/src/main/java/otus/homework/coroutines/MainActivity.kt +++ b/app/src/main/java/otus/homework/coroutines/MainActivity.kt @@ -5,9 +5,7 @@ import android.os.Bundle class MainActivity : AppCompatActivity() { - lateinit var catsPresenter: CatsPresenter - - private val diContainer = DiContainer() + private lateinit var catsPresenter: CatsPresenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -15,12 +13,17 @@ class MainActivity : AppCompatActivity() { val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView setContentView(view) - catsPresenter = CatsPresenter(diContainer.service) + catsPresenter = createCatsPresenter() view.presenter = catsPresenter catsPresenter.attachView(view) catsPresenter.onInitComplete() } + private fun createCatsPresenter(): CatsPresenter { + val diContainer = CustomApplication.diContainer(this) + return CatsPresenter(diContainer.service, diContainer.stringProvider, PresenterScope()) + } + override fun onStop() { if (isFinishing) { catsPresenter.detachView() diff --git a/app/src/main/java/otus/homework/coroutines/PresenterScope.kt b/app/src/main/java/otus/homework/coroutines/PresenterScope.kt new file mode 100644 index 00000000..b0c4033b --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/PresenterScope.kt @@ -0,0 +1,13 @@ +package otus.homework.coroutines + +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlin.coroutines.CoroutineContext + +class PresenterScope : CoroutineScope { + + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + CoroutineName("CatsCoroutine") + +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/StringProvider.kt b/app/src/main/java/otus/homework/coroutines/StringProvider.kt new file mode 100644 index 00000000..dc9c12ca --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/StringProvider.kt @@ -0,0 +1,8 @@ +package otus.homework.coroutines + +import androidx.annotation.StringRes + +interface StringProvider { + + fun getString(@StringRes res: Int): String +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/StringProviderImpl.kt b/app/src/main/java/otus/homework/coroutines/StringProviderImpl.kt new file mode 100644 index 00000000..2ca7c803 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/StringProviderImpl.kt @@ -0,0 +1,10 @@ +package otus.homework.coroutines + +import android.content.Context + +class StringProviderImpl( + private val context: Context +) : StringProvider { + + override fun getString(res: Int) = context.getString(res) +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c270d5d..2275d893 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,6 @@ Cat Facts More Facts + Не удалось получить ответ от сервера + Произошла ошибка во время выполнения запроса \ No newline at end of file From 21557eb5b7e63495da0d5fab2ade8aa85ba5a8af Mon Sep 17 00:00:00 2001 From: SergeyKozhukhov Date: Thu, 13 Jul 2023 23:47:28 +0300 Subject: [PATCH 2/7] Second step: random image request --- app/src/main/AndroidManifest.xml | 2 +- .../otus/homework/coroutines/CatsPresenter.kt | 21 +++++--- .../otus/homework/coroutines/CatsService.kt | 9 ---- .../java/otus/homework/coroutines/CatsView.kt | 23 +++++++-- .../otus/homework/coroutines/DiContainer.kt | 19 ------- .../otus/homework/coroutines/MainActivity.kt | 6 +-- .../homework/coroutines/PresenterScope.kt | 13 ----- .../coroutines/data/CatRepositoryImpl.kt | 20 ++++++++ .../homework/coroutines/data/FactService.kt | 10 ++++ .../homework/coroutines/data/ImagesService.kt | 10 ++++ .../coroutines/data/converter/CatConverter.kt | 11 ++++ .../homework/coroutines/di/DiContainer.kt | 50 +++++++++++++++++++ .../coroutines/domain/CatRepository.kt | 8 +++ .../otus/homework/coroutines/models/Cat.kt | 6 +++ .../homework/coroutines/{ => models}/Fact.kt | 2 +- .../otus/homework/coroutines/models/Image.kt | 19 +++++++ .../coroutines/{ => utils}/CrashMonitor.kt | 2 +- .../{ => utils}/CustomApplication.kt | 3 +- .../coroutines/{ => utils}/StringProvider.kt | 2 +- .../{ => utils}/StringProviderImpl.kt | 2 +- .../coroutines/utils/coroutines/Dispatcher.kt | 8 +++ .../utils/coroutines/DispatcherImpl.kt | 8 +++ .../utils/coroutines/PresenterScope.kt | 12 +++++ app/src/main/res/drawable/question_mark.xml | 5 ++ app/src/main/res/layout/activity_main.xml | 27 +++++++--- app/src/main/res/values/dimens.xml | 5 ++ app/src/main/res/values/strings.xml | 1 + 27 files changed, 237 insertions(+), 67 deletions(-) delete mode 100644 app/src/main/java/otus/homework/coroutines/CatsService.kt delete mode 100644 app/src/main/java/otus/homework/coroutines/DiContainer.kt delete mode 100644 app/src/main/java/otus/homework/coroutines/PresenterScope.kt create mode 100644 app/src/main/java/otus/homework/coroutines/data/CatRepositoryImpl.kt create mode 100644 app/src/main/java/otus/homework/coroutines/data/FactService.kt create mode 100644 app/src/main/java/otus/homework/coroutines/data/ImagesService.kt create mode 100644 app/src/main/java/otus/homework/coroutines/data/converter/CatConverter.kt create mode 100644 app/src/main/java/otus/homework/coroutines/di/DiContainer.kt create mode 100644 app/src/main/java/otus/homework/coroutines/domain/CatRepository.kt create mode 100644 app/src/main/java/otus/homework/coroutines/models/Cat.kt rename app/src/main/java/otus/homework/coroutines/{ => models}/Fact.kt (84%) create mode 100644 app/src/main/java/otus/homework/coroutines/models/Image.kt rename app/src/main/java/otus/homework/coroutines/{ => utils}/CrashMonitor.kt (86%) rename app/src/main/java/otus/homework/coroutines/{ => utils}/CustomApplication.kt (82%) rename app/src/main/java/otus/homework/coroutines/{ => utils}/StringProvider.kt (74%) rename app/src/main/java/otus/homework/coroutines/{ => utils}/StringProviderImpl.kt (81%) create mode 100644 app/src/main/java/otus/homework/coroutines/utils/coroutines/Dispatcher.kt create mode 100644 app/src/main/java/otus/homework/coroutines/utils/coroutines/DispatcherImpl.kt create mode 100644 app/src/main/java/otus/homework/coroutines/utils/coroutines/PresenterScope.kt create mode 100644 app/src/main/res/drawable/question_mark.xml create mode 100644 app/src/main/res/values/dimens.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cf25e683..da030c7b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ (R.id.button).setOnClickListener { presenter?.onInitComplete() + picasso.cancelRequest(imageView) } } - override fun populate(fact: Fact) { - findViewById(R.id.fact_textView).text = fact.fact + override fun populate(cat: Cat) { + textView.text = cat.fact + picasso + .load(cat.image) + .placeholder(R.drawable.question_mark) + .into(imageView) } override fun warn(message: String) { @@ -32,8 +48,7 @@ class CatsView @JvmOverloads constructor( } interface ICatsView { - - fun populate(fact: Fact) + fun populate(cat: Cat) fun warn(message: String) } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/DiContainer.kt b/app/src/main/java/otus/homework/coroutines/DiContainer.kt deleted file mode 100644 index 1d00ffb9..00000000 --- a/app/src/main/java/otus/homework/coroutines/DiContainer.kt +++ /dev/null @@ -1,19 +0,0 @@ -package otus.homework.coroutines - -import android.content.Context -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory - -class DiContainer(private val context: Context) { - - private val retrofit by lazy { - Retrofit.Builder() - .baseUrl("https://catfact.ninja/") - .addConverterFactory(GsonConverterFactory.create()) - .build() - } - - val service: CatsService by lazy { retrofit.create(CatsService::class.java) } - - val stringProvider: StringProvider by lazy { StringProviderImpl(context) } -} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/MainActivity.kt b/app/src/main/java/otus/homework/coroutines/MainActivity.kt index 419031c1..9fd5c04f 100644 --- a/app/src/main/java/otus/homework/coroutines/MainActivity.kt +++ b/app/src/main/java/otus/homework/coroutines/MainActivity.kt @@ -2,6 +2,7 @@ package otus.homework.coroutines import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import otus.homework.coroutines.utils.CustomApplication class MainActivity : AppCompatActivity() { @@ -19,9 +20,8 @@ class MainActivity : AppCompatActivity() { catsPresenter.onInitComplete() } - private fun createCatsPresenter(): CatsPresenter { - val diContainer = CustomApplication.diContainer(this) - return CatsPresenter(diContainer.service, diContainer.stringProvider, PresenterScope()) + private fun createCatsPresenter() = with(CustomApplication.diContainer(this)) { + CatsPresenter(catRepository, stringProvider, dispatcher) } override fun onStop() { diff --git a/app/src/main/java/otus/homework/coroutines/PresenterScope.kt b/app/src/main/java/otus/homework/coroutines/PresenterScope.kt deleted file mode 100644 index b0c4033b..00000000 --- a/app/src/main/java/otus/homework/coroutines/PresenterScope.kt +++ /dev/null @@ -1,13 +0,0 @@ -package otus.homework.coroutines - -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlin.coroutines.CoroutineContext - -class PresenterScope : CoroutineScope { - - override val coroutineContext: CoroutineContext - get() = Dispatchers.Main + CoroutineName("CatsCoroutine") - -} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/data/CatRepositoryImpl.kt b/app/src/main/java/otus/homework/coroutines/data/CatRepositoryImpl.kt new file mode 100644 index 00000000..089bded0 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/data/CatRepositoryImpl.kt @@ -0,0 +1,20 @@ +package otus.homework.coroutines.data + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import otus.homework.coroutines.data.converter.CatConverter +import otus.homework.coroutines.domain.CatRepository +import otus.homework.coroutines.models.Cat + +class CatRepositoryImpl( + private val factService: FactService, + private val imageService: ImagesService, + private val converter: CatConverter, +) : CatRepository { + + override suspend fun getCatInfo(): Cat = coroutineScope { + val factDeferred = async { factService.getCatFact() } + val imagesDeferred = async { imageService.getCatImages() } + converter.convert(factDeferred.await(), imagesDeferred.await()) + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/data/FactService.kt b/app/src/main/java/otus/homework/coroutines/data/FactService.kt new file mode 100644 index 00000000..083dac07 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/data/FactService.kt @@ -0,0 +1,10 @@ +package otus.homework.coroutines.data + +import otus.homework.coroutines.models.Fact +import retrofit2.http.GET + +interface FactService { + + @GET("fact") + suspend fun getCatFact(): Fact +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/data/ImagesService.kt b/app/src/main/java/otus/homework/coroutines/data/ImagesService.kt new file mode 100644 index 00000000..5cbf5a82 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/data/ImagesService.kt @@ -0,0 +1,10 @@ +package otus.homework.coroutines.data + +import otus.homework.coroutines.models.Image +import retrofit2.http.GET + +interface ImagesService { + + @GET("search") + suspend fun getCatImages(): List +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/data/converter/CatConverter.kt b/app/src/main/java/otus/homework/coroutines/data/converter/CatConverter.kt new file mode 100644 index 00000000..2de1517c --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/data/converter/CatConverter.kt @@ -0,0 +1,11 @@ +package otus.homework.coroutines.data.converter + +import otus.homework.coroutines.models.Fact +import otus.homework.coroutines.models.Image +import otus.homework.coroutines.models.Cat + +class CatConverter { + + fun convert(fact: Fact, images: List) = + Cat(fact.fact, images.firstOrNull()?.url) +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/di/DiContainer.kt b/app/src/main/java/otus/homework/coroutines/di/DiContainer.kt new file mode 100644 index 00000000..a5c360db --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/di/DiContainer.kt @@ -0,0 +1,50 @@ +package otus.homework.coroutines.di + +import android.content.Context +import com.squareup.picasso.Picasso +import otus.homework.coroutines.data.converter.CatConverter +import otus.homework.coroutines.data.FactService +import otus.homework.coroutines.data.ImagesService +import otus.homework.coroutines.domain.CatRepository +import otus.homework.coroutines.data.CatRepositoryImpl +import otus.homework.coroutines.utils.StringProvider +import otus.homework.coroutines.utils.StringProviderImpl +import otus.homework.coroutines.utils.coroutines.Dispatcher +import otus.homework.coroutines.utils.coroutines.DispatcherImpl +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class DiContainer(private val context: Context) { + + val catRepository: CatRepository by lazy(LazyThreadSafetyMode.NONE) { + CatRepositoryImpl(provideFactService(), provideImagesService(), CatConverter()) + } + + private fun provideFactService() = provideFactRetrofit().create(FactService::class.java) + + private fun provideFactRetrofit() = Retrofit.Builder() + .baseUrl(FACT_BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + private fun provideImagesService() = provideImageRetrofit().create(ImagesService::class.java) + + private fun provideImageRetrofit() = Retrofit.Builder() + .baseUrl(IMAGES_BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + + val stringProvider: StringProvider by lazy(LazyThreadSafetyMode.NONE) { + StringProviderImpl(context) + } + + val picasso: Picasso by lazy(LazyThreadSafetyMode.NONE) { Picasso.Builder(context).build() } + + val dispatcher: Dispatcher by lazy(LazyThreadSafetyMode.NONE) { DispatcherImpl() } + + private companion object { + const val FACT_BASE_URL = "https://catfact.ninja/" + const val IMAGES_BASE_URL = "https://api.thecatapi.com/v1/images/" + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/domain/CatRepository.kt b/app/src/main/java/otus/homework/coroutines/domain/CatRepository.kt new file mode 100644 index 00000000..a0482cee --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/domain/CatRepository.kt @@ -0,0 +1,8 @@ +package otus.homework.coroutines.domain + +import otus.homework.coroutines.models.Cat + +interface CatRepository { + + suspend fun getCatInfo(): Cat +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/models/Cat.kt b/app/src/main/java/otus/homework/coroutines/models/Cat.kt new file mode 100644 index 00000000..025afedf --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/models/Cat.kt @@ -0,0 +1,6 @@ +package otus.homework.coroutines.models + +data class Cat( + val fact: String, + val image: String? +) diff --git a/app/src/main/java/otus/homework/coroutines/Fact.kt b/app/src/main/java/otus/homework/coroutines/models/Fact.kt similarity index 84% rename from app/src/main/java/otus/homework/coroutines/Fact.kt rename to app/src/main/java/otus/homework/coroutines/models/Fact.kt index 4b2473aa..4f28df00 100644 --- a/app/src/main/java/otus/homework/coroutines/Fact.kt +++ b/app/src/main/java/otus/homework/coroutines/models/Fact.kt @@ -1,4 +1,4 @@ -package otus.homework.coroutines +package otus.homework.coroutines.models import com.google.gson.annotations.SerializedName diff --git a/app/src/main/java/otus/homework/coroutines/models/Image.kt b/app/src/main/java/otus/homework/coroutines/models/Image.kt new file mode 100644 index 00000000..90f9f095 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/models/Image.kt @@ -0,0 +1,19 @@ +package otus.homework.coroutines.models + +import com.google.gson.annotations.SerializedName + +// https://developers.thecatapi.com/view-account/ylX4blBYT9FaoVd6OhvR?report=bOoHBz-8t +data class Image( + + @field:SerializedName("id") + val id: String, + + @field:SerializedName("url") + val url: String, + + @field:SerializedName("width") + val width: String, + + @field:SerializedName("height") + val height: String +) diff --git a/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt b/app/src/main/java/otus/homework/coroutines/utils/CrashMonitor.kt similarity index 86% rename from app/src/main/java/otus/homework/coroutines/CrashMonitor.kt rename to app/src/main/java/otus/homework/coroutines/utils/CrashMonitor.kt index 77b8b708..476febd7 100644 --- a/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt +++ b/app/src/main/java/otus/homework/coroutines/utils/CrashMonitor.kt @@ -1,4 +1,4 @@ -package otus.homework.coroutines +package otus.homework.coroutines.utils import android.util.Log diff --git a/app/src/main/java/otus/homework/coroutines/CustomApplication.kt b/app/src/main/java/otus/homework/coroutines/utils/CustomApplication.kt similarity index 82% rename from app/src/main/java/otus/homework/coroutines/CustomApplication.kt rename to app/src/main/java/otus/homework/coroutines/utils/CustomApplication.kt index b936b39a..31908360 100644 --- a/app/src/main/java/otus/homework/coroutines/CustomApplication.kt +++ b/app/src/main/java/otus/homework/coroutines/utils/CustomApplication.kt @@ -1,7 +1,8 @@ -package otus.homework.coroutines +package otus.homework.coroutines.utils import android.app.Application import android.content.Context +import otus.homework.coroutines.di.DiContainer class CustomApplication : Application() { diff --git a/app/src/main/java/otus/homework/coroutines/StringProvider.kt b/app/src/main/java/otus/homework/coroutines/utils/StringProvider.kt similarity index 74% rename from app/src/main/java/otus/homework/coroutines/StringProvider.kt rename to app/src/main/java/otus/homework/coroutines/utils/StringProvider.kt index dc9c12ca..78073c70 100644 --- a/app/src/main/java/otus/homework/coroutines/StringProvider.kt +++ b/app/src/main/java/otus/homework/coroutines/utils/StringProvider.kt @@ -1,4 +1,4 @@ -package otus.homework.coroutines +package otus.homework.coroutines.utils import androidx.annotation.StringRes diff --git a/app/src/main/java/otus/homework/coroutines/StringProviderImpl.kt b/app/src/main/java/otus/homework/coroutines/utils/StringProviderImpl.kt similarity index 81% rename from app/src/main/java/otus/homework/coroutines/StringProviderImpl.kt rename to app/src/main/java/otus/homework/coroutines/utils/StringProviderImpl.kt index 2ca7c803..8eb089da 100644 --- a/app/src/main/java/otus/homework/coroutines/StringProviderImpl.kt +++ b/app/src/main/java/otus/homework/coroutines/utils/StringProviderImpl.kt @@ -1,4 +1,4 @@ -package otus.homework.coroutines +package otus.homework.coroutines.utils import android.content.Context diff --git a/app/src/main/java/otus/homework/coroutines/utils/coroutines/Dispatcher.kt b/app/src/main/java/otus/homework/coroutines/utils/coroutines/Dispatcher.kt new file mode 100644 index 00000000..a95b8000 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/utils/coroutines/Dispatcher.kt @@ -0,0 +1,8 @@ +package otus.homework.coroutines.utils.coroutines + +import kotlinx.coroutines.CoroutineDispatcher + +interface Dispatcher { + + val main: CoroutineDispatcher +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/utils/coroutines/DispatcherImpl.kt b/app/src/main/java/otus/homework/coroutines/utils/coroutines/DispatcherImpl.kt new file mode 100644 index 00000000..dd137d1a --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/utils/coroutines/DispatcherImpl.kt @@ -0,0 +1,8 @@ +package otus.homework.coroutines.utils.coroutines + +import kotlinx.coroutines.Dispatchers + +class DispatcherImpl : Dispatcher { + + override val main = Dispatchers.Main +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/utils/coroutines/PresenterScope.kt b/app/src/main/java/otus/homework/coroutines/utils/coroutines/PresenterScope.kt new file mode 100644 index 00000000..cfc597dc --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/utils/coroutines/PresenterScope.kt @@ -0,0 +1,12 @@ +package otus.homework.coroutines.utils.coroutines + +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlin.coroutines.CoroutineContext + +class PresenterScope(private val dispatcher: Dispatcher) : CoroutineScope { + + override val coroutineContext: CoroutineContext + get() = dispatcher.main + CoroutineName("CatsCoroutine") + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/question_mark.xml b/app/src/main/res/drawable/question_mark.xml new file mode 100644 index 00000000..9c97e2d5 --- /dev/null +++ b/app/src/main/res/drawable/question_mark.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 9508066d..6a0a5df7 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -3,20 +3,33 @@ 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"> + + + app:layout_constraintTop_toBottomOf="@id/image_view" />