diff --git a/.github/workflows/ci_pipeline.yml b/.github/workflows/ci_pipeline.yml index 0b84a1f..8052bb3 100644 --- a/.github/workflows/ci_pipeline.yml +++ b/.github/workflows/ci_pipeline.yml @@ -1,9 +1,9 @@ name: cute-cat-gallery-ci-pipeline on: pull_request: - branches: [main] + branches: [main, develop] push: - branches: [main] + branches: [main, develop] jobs: build: runs-on: ubuntu-latest @@ -22,6 +22,11 @@ jobs: with: arguments: build gradle-version: 7.6 + - name: Run KLint + uses: gradle/gradle-build-action@v2.9.0 + with: + arguments: ktlintCheck + gradle-version: 7.6 - name: Run Tests uses: gradle/gradle-build-action@v2.9.0 with: diff --git a/app/build.gradle b/app/build.gradle index f433448..c81773d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,19 +1,15 @@ plugins { - id 'com.android.application' - id 'kotlin-android' + id "com.android.application" + id "kotlin-android" } android { - compileSdk 33 - - viewBinding { - enabled = true - } + compileSdk 34 defaultConfig { applicationId "com.luishenrique.cutecatsgallery" - minSdk 21 - targetSdkVersion 33 + minSdkVersion 21 + targetSdkVersion 34 versionCode 1 versionName "1.0" @@ -21,9 +17,16 @@ android { } buildTypes { - release { + debug { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + debuggable = true + buildConfigField "String", "API_URL", "\"https://api.imgur.com/3/\"" + proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" + } + release { + minifyEnabled true + buildConfigField "String", "API_URL", "\"https://api.imgur.com/3/\"" + proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" } } compileOptions { @@ -31,35 +34,58 @@ android { targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '17' + jvmTarget = "17" + } + composeOptions { + kotlinCompilerExtensionVersion = compose_compiler_version + } + namespace "com.luishenrique.cutecatsgallery" + + buildFeatures { + compose = true } - namespace 'com.luishenrique.cutecatsgallery' } dependencies { - api project(path: ':domain') + // Retrofit + implementation "com.squareup.retrofit2:retrofit:2.9.0" + implementation "com.squareup.retrofit2:converter-gson:2.9.0" + + // Compose + implementation platform("androidx.compose:compose-bom:2023.10.00") + implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" + implementation "androidx.activity:activity-compose:1.8.0" + implementation "androidx.compose.material3:material3:1.1.2" + implementation "androidx.compose.material:material-android:1.5.3" + implementation "io.coil-kt:coil-compose:2.4.0" + implementation "androidx.navigation:navigation-compose:2.7.4" + debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" + debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" + androidTestImplementation platform("androidx.compose:compose-bom:2023.10.00") + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" + + implementation 'com.google.accompanist:accompanist-swiperefresh:0.32.0' // progressbar cat - implementation 'com.roger.catloadinglibrary:catloadinglibrary:1.0.9' + implementation "com.roger.catloadinglibrary:catloadinglibrary:1.0.9" // glide - implementation 'com.github.bumptech.glide:glide:4.12.0' + implementation "com.github.bumptech.glide:glide:4.15.1" // coroutines - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-RC' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" // koin - implementation 'org.koin:koin-android-viewmodel:2.1.6' + implementation "io.insert-koin:koin-android:$koin_version" - // viewmodel - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0' + // viewModel + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2" - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.4.0' - implementation 'com.google.android.material:material:1.4.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.2' - testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + implementation "androidx.core:core-ktx:1.12.0" + implementation "com.google.android.material:material:1.10.0" + implementation "androidx.appcompat:appcompat:1.6.1" + testImplementation "junit:junit:4.13.2" + androidTestImplementation "androidx.test.ext:junit:1.1.5" + androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1" } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e07327e..73e9f23 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,7 @@ android:supportsRtl="true" android:theme="@style/Theme.CuteCatsGallery"> diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/App.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/App.kt index 1a6527e..04c0f60 100644 --- a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/App.kt +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/App.kt @@ -1,10 +1,7 @@ package com.luishenrique.cutecatsgallery import android.app.Application -import com.luishenrique.cutecatsgallery.di.viewModelModule -import com.luishenrique.domain.di.apiServiceModule -import com.luishenrique.domain.di.repositoryModule -import com.luishenrique.domain.di.useCaseModule +import com.luishenrique.cutecatsgallery.di.DependencyInjection import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -15,14 +12,7 @@ class App : Application() { startKoin { androidContext(this@App) - modules( - listOf( - viewModelModule, - apiServiceModule, - repositoryModule, - useCaseModule, - ) - ) + modules(DependencyInjection.modules) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/base/BaseViewModel.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/base/BaseViewModel.kt new file mode 100644 index 0000000..ff093e4 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/base/BaseViewModel.kt @@ -0,0 +1,29 @@ +package com.luishenrique.cutecatsgallery.base + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +abstract class BaseViewModel : ViewModel() { + protected fun request( + block: suspend () -> T, + onStartRequest: () -> Unit = {}, + onSuccess: (T) -> Unit, + onError: (ErrorContent) -> Unit, + onFinally: () -> Unit = {} + ) : Job { + return viewModelScope.launch { + onStartRequest.invoke() + runCatching { + val response = withContext(Dispatchers.IO) { block.invoke() } + onSuccess.invoke(response) + }.onFailure { + onError.invoke(ErrorContent(it.message.toString(), it)) + } + onFinally.invoke() + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/base/ErrorContent.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/base/ErrorContent.kt new file mode 100644 index 0000000..9f6c968 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/base/ErrorContent.kt @@ -0,0 +1,6 @@ +package com.luishenrique.cutecatsgallery.base + +data class ErrorContent( + val message: String, + val throwable: Throwable +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/commonComponents/Toolbar.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/commonComponents/Toolbar.kt new file mode 100644 index 0000000..a9c5c74 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/commonComponents/Toolbar.kt @@ -0,0 +1,25 @@ +package com.luishenrique.cutecatsgallery.commonComponents + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Toolbar( + colors: TopAppBarColors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + titleContentColor = MaterialTheme.colorScheme.primary, + ), + title: @Composable () -> Unit +) { + TopAppBar( + colors = colors, + title = { + title.invoke() + } + ) +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/AppModule.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/AppModule.kt deleted file mode 100644 index bc334fa..0000000 --- a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/AppModule.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.luishenrique.cutecatsgallery.di - -import com.luishenrique.cutecatsgallery.ui.viewmodel.HomeViewModel -import org.koin.android.viewmodel.dsl.viewModel -import org.koin.dsl.module - -val viewModelModule = module { - viewModel { HomeViewModel(useCase = get()) } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/DependencyInjection.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/DependencyInjection.kt new file mode 100644 index 0000000..eab4760 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/DependencyInjection.kt @@ -0,0 +1,10 @@ +package com.luishenrique.cutecatsgallery.di + +import com.luishenrique.cutecatsgallery.home.di.HomeModule + +object DependencyInjection { + val modules = listOf( + RetrofitModule.instance, + HomeModule.instance + ) +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/RetrofitModule.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/RetrofitModule.kt new file mode 100644 index 0000000..63c3564 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/di/RetrofitModule.kt @@ -0,0 +1,29 @@ +package com.luishenrique.cutecatsgallery.di + +import com.luishenrique.cutecatsgallery.BuildConfig +import com.luishenrique.cutecatsgallery.network.CuteCatsInterceptor +import okhttp3.OkHttpClient +import org.koin.core.module.dsl.factoryOf +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +object RetrofitModule { + val instance = module { + factoryOf(::CuteCatsInterceptor) + factoryOf(::provideOkHttpClient) + singleOf(::provideRetrofit) + } + + private fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { + return Retrofit.Builder() + .baseUrl(BuildConfig.API_URL) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()).build() + } + + private fun provideOkHttpClient(interceptor: CuteCatsInterceptor): OkHttpClient { + return OkHttpClient().newBuilder().addInterceptor(interceptor).build() + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/dataSource/GalleryDataSource.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/dataSource/GalleryDataSource.kt new file mode 100644 index 0000000..07275b5 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/dataSource/GalleryDataSource.kt @@ -0,0 +1,8 @@ +package com.luishenrique.cutecatsgallery.home.data.dataSource + +import com.luishenrique.cutecatsgallery.home.data.network.response.GalleryResponse + +interface GalleryDataSource { + suspend fun finAllCats(page: Int): GalleryResponse + +} diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/dataSource/GalleryDataSourceImpl.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/dataSource/GalleryDataSourceImpl.kt new file mode 100644 index 0000000..2d48c01 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/dataSource/GalleryDataSourceImpl.kt @@ -0,0 +1,11 @@ +package com.luishenrique.cutecatsgallery.home.data.dataSource + +import com.luishenrique.cutecatsgallery.home.data.network.HomeApiService +import com.luishenrique.cutecatsgallery.home.data.network.response.GalleryResponse + +class GalleryDataSourceImpl(private val service: HomeApiService) : GalleryDataSource { + + override suspend fun finAllCats(page: Int): GalleryResponse { + return service.finAllCats(page = page) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/mapper/GalleryMapper.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/mapper/GalleryMapper.kt new file mode 100644 index 0000000..e7e6f65 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/mapper/GalleryMapper.kt @@ -0,0 +1,43 @@ +package com.luishenrique.cutecatsgallery.home.data.mapper + +import com.luishenrique.cutecatsgallery.home.data.network.response.CardImageInfoResponse +import com.luishenrique.cutecatsgallery.home.data.network.response.CardImageResponse +import com.luishenrique.cutecatsgallery.home.data.network.response.GalleryResponse +import com.luishenrique.cutecatsgallery.home.domain.model.CardImage +import com.luishenrique.cutecatsgallery.home.domain.model.CardImageInfo +import com.luishenrique.cutecatsgallery.home.domain.model.Gallery + +object GalleryMapper { + + fun mapToModel(response: GalleryResponse) : Gallery { + return Gallery( + data = getCards(response.data) + ) + } + + private fun getCards(data: List?): List { + return data?.map { getCardImage(it) }.orEmpty() + } + + private fun getCardImage(cardImage: CardImageResponse?) : CardImage { + return CardImage( + title = cardImage?.title.orEmpty(), + username = cardImage?.username.orEmpty(), + score = cardImage?.score ?: 0, + images = getImages(cardImage?.images).orEmpty() + ) + } + + private fun getImages(images: List?): List? { + return images?.map { getCardImageInfo(it) } + } + + private fun getCardImageInfo(cardImageInfo: CardImageInfoResponse?) : CardImageInfo { + return CardImageInfo( + type = cardImageInfo?.type.orEmpty(), + link = cardImageInfo?.link.orEmpty(), + gifv = cardImageInfo?.gifv.orEmpty(), + mp4 = cardImageInfo?.mp4.orEmpty() + ) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/HomeApiService.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/HomeApiService.kt new file mode 100644 index 0000000..0e60b4e --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/HomeApiService.kt @@ -0,0 +1,18 @@ +package com.luishenrique.cutecatsgallery.home.data.network + +import com.luishenrique.cutecatsgallery.home.data.network.response.GalleryResponse +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.Query + +interface HomeApiService { + + @GET("gallery/search/?q=cats") + suspend fun finAllCats( + @Header("Authorization") authHeader: String = "Client-ID 1ceddedc03a5d71", + @Query("sort") sort: String = "viral", + @Query("q_type") type: String = "jpg", + @Query("q_size_px") size: String = "big", + @Query("page") page: Int = 1 + ): GalleryResponse +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/CardImageInfoResponse.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/CardImageInfoResponse.kt new file mode 100644 index 0000000..46f2d4e --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/CardImageInfoResponse.kt @@ -0,0 +1,10 @@ +package com.luishenrique.cutecatsgallery.home.data.network.response + +import com.google.gson.annotations.SerializedName + +data class CardImageInfoResponse( + @SerializedName("type") val type: String?, + @SerializedName("link") val link: String?, + @SerializedName("gifv") val gifv: String?, + @SerializedName("mp4") val mp4: String?, +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/CardImageResponse.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/CardImageResponse.kt new file mode 100644 index 0000000..018db36 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/CardImageResponse.kt @@ -0,0 +1,10 @@ +package com.luishenrique.cutecatsgallery.home.data.network.response + +import com.google.gson.annotations.SerializedName + +data class CardImageResponse( + @SerializedName("title") val title: String?, + @SerializedName("account_url") val username: String?, + @SerializedName("score") val score: Int?, + @SerializedName("images") val images: List? +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/GalleryResponse.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/GalleryResponse.kt new file mode 100644 index 0000000..a238592 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/network/response/GalleryResponse.kt @@ -0,0 +1,7 @@ +package com.luishenrique.cutecatsgallery.home.data.network.response + +import com.google.gson.annotations.SerializedName + +data class GalleryResponse( + @SerializedName("data") val data: List? +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/repository/GalleryRepository.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/repository/GalleryRepository.kt new file mode 100644 index 0000000..add6d62 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/repository/GalleryRepository.kt @@ -0,0 +1,7 @@ +package com.luishenrique.cutecatsgallery.home.data.repository + +import com.luishenrique.cutecatsgallery.home.domain.model.Gallery + +interface GalleryRepository { + suspend fun findAllCats(page: Int): Gallery +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/repository/GalleryRepositoryImpl.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/repository/GalleryRepositoryImpl.kt new file mode 100644 index 0000000..be1495c --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/data/repository/GalleryRepositoryImpl.kt @@ -0,0 +1,12 @@ +package com.luishenrique.cutecatsgallery.home.data.repository + +import com.luishenrique.cutecatsgallery.home.data.dataSource.GalleryDataSource +import com.luishenrique.cutecatsgallery.home.data.mapper.GalleryMapper +import com.luishenrique.cutecatsgallery.home.domain.model.Gallery + +class GalleryRepositoryImpl(private val dataSource: GalleryDataSource) : GalleryRepository { + + override suspend fun findAllCats(page: Int): Gallery { + return GalleryMapper.mapToModel(dataSource.finAllCats(page = page)) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/di/HomeModule.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/di/HomeModule.kt new file mode 100644 index 0000000..43d905e --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/di/HomeModule.kt @@ -0,0 +1,31 @@ +package com.luishenrique.cutecatsgallery.home.di + +import com.luishenrique.cutecatsgallery.home.data.dataSource.GalleryDataSource +import com.luishenrique.cutecatsgallery.home.data.dataSource.GalleryDataSourceImpl +import com.luishenrique.cutecatsgallery.home.data.network.HomeApiService +import com.luishenrique.cutecatsgallery.home.data.repository.GalleryRepository +import com.luishenrique.cutecatsgallery.home.data.repository.GalleryRepositoryImpl +import com.luishenrique.cutecatsgallery.home.domain.GalleryUseCase +import com.luishenrique.cutecatsgallery.home.domain.GalleryUseCaseImpl +import com.luishenrique.cutecatsgallery.home.presentation.HomeViewModel +import kotlinx.coroutines.Job +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.module.dsl.factoryOf +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.bind +import org.koin.dsl.module +import retrofit2.Retrofit + +object HomeModule { + val instance = module { + singleOf(::provideHomeApi) + factoryOf(::GalleryDataSourceImpl) bind GalleryDataSource::class + factoryOf(::GalleryRepositoryImpl) bind GalleryRepository::class + factoryOf(::GalleryUseCaseImpl) bind GalleryUseCase::class + viewModel { HomeViewModel(get(), Job()) } + } + + private fun provideHomeApi(retrofit: Retrofit): HomeApiService { + return retrofit.create(HomeApiService::class.java) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/GalleryUseCase.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/GalleryUseCase.kt new file mode 100644 index 0000000..a025ba2 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/GalleryUseCase.kt @@ -0,0 +1,8 @@ +package com.luishenrique.cutecatsgallery.home.domain + +import com.luishenrique.cutecatsgallery.home.domain.model.Gallery + + +interface GalleryUseCase { + suspend fun findAllCats(page: Int) : Gallery +} diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/GalleryUseCaseImpl.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/GalleryUseCaseImpl.kt new file mode 100644 index 0000000..65da203 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/GalleryUseCaseImpl.kt @@ -0,0 +1,11 @@ +package com.luishenrique.cutecatsgallery.home.domain + +import com.luishenrique.cutecatsgallery.home.data.repository.GalleryRepository +import com.luishenrique.cutecatsgallery.home.domain.model.Gallery + +class GalleryUseCaseImpl(private val repository: GalleryRepository) : GalleryUseCase { + + override suspend fun findAllCats(page: Int) : Gallery { + return repository.findAllCats(page) + } +} diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/CardImage.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/CardImage.kt new file mode 100644 index 0000000..6b83740 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/CardImage.kt @@ -0,0 +1,8 @@ +package com.luishenrique.cutecatsgallery.home.domain.model + +data class CardImage( + val title: String, + val username: String, + val score: Int, + val images: List +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/CardImageInfo.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/CardImageInfo.kt new file mode 100644 index 0000000..a81a154 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/CardImageInfo.kt @@ -0,0 +1,8 @@ +package com.luishenrique.cutecatsgallery.home.domain.model + +data class CardImageInfo( + val type: String, + val link: String, + val gifv: String, + val mp4: String, +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/Gallery.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/Gallery.kt new file mode 100644 index 0000000..40ffb82 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/domain/model/Gallery.kt @@ -0,0 +1,5 @@ +package com.luishenrique.cutecatsgallery.home.domain.model + +data class Gallery( + val data: List +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/GalleryUiState.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/GalleryUiState.kt new file mode 100644 index 0000000..6db4999 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/GalleryUiState.kt @@ -0,0 +1,10 @@ +package com.luishenrique.cutecatsgallery.home.presentation + +import com.luishenrique.cutecatsgallery.base.ErrorContent +import com.luishenrique.cutecatsgallery.home.domain.model.Gallery + +sealed class GalleryUiState { + data class Content(val gallery: Gallery) : GalleryUiState() + data class Error(val errorContent: ErrorContent) : GalleryUiState() + object Loading : GalleryUiState() +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeActivity.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeActivity.kt new file mode 100644 index 0000000..79cea6a --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeActivity.kt @@ -0,0 +1,35 @@ +package com.luishenrique.cutecatsgallery.home.presentation + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.ui.Modifier +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.luishenrique.cutecatsgallery.routes.Routes +import org.koin.android.ext.android.inject + +class HomeActivity : ComponentActivity() { + + private val viewModel by inject() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + Surface(modifier = Modifier.fillMaxSize()) { + MaterialTheme { + val navController = rememberNavController() + NavHost(navController = navController, startDestination = Routes.Home.route) { + composable(Routes.Home.route) { + HomeScreen(viewModel.uiState) { viewModel.getCats() } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeScreen.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeScreen.kt new file mode 100644 index 0000000..727be68 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeScreen.kt @@ -0,0 +1,254 @@ +package com.luishenrique.cutecatsgallery.home.presentation + +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.luishenrique.cutecatsgallery.R +import com.luishenrique.cutecatsgallery.base.ErrorContent +import com.luishenrique.cutecatsgallery.commonComponents.Toolbar +import com.luishenrique.cutecatsgallery.home.domain.model.CardImage +import com.luishenrique.cutecatsgallery.home.domain.model.Gallery +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) +@Composable +fun HomeScreen( + state: StateFlow, + onRefresh: () -> Unit = {} +) { + val homeUiState by state.collectAsState() + val refreshing = remember { mutableStateOf(false) } + val pullRefreshState = rememberPullRefreshState(refreshing.value, { + refreshing.value = true + onRefresh.invoke() + }) + + Scaffold( + topBar = { + Toolbar { Text("Cute Cats Gallery") } + } + ) { + Box( + modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState), + contentAlignment = Alignment.Center + ) { + when (homeUiState) { + is GalleryUiState.Loading -> { + LoadingIndicator() + } + + is GalleryUiState.Content -> { + GalleryList( + items = (homeUiState as GalleryUiState.Content).gallery.data, + modifier = Modifier.padding(it) + ) + refreshing.value = false + } + + is GalleryUiState.Error -> { + Image( + painter = painterResource(id = R.drawable.ic_cat), + contentDescription = stringResource(R.string.sad_cat) + ) + refreshing.value = false + } + } + } + } +} + +@Composable +fun GalleryList(items: List, modifier: Modifier = Modifier) { + LazyVerticalGrid( + columns = GridCells.Adaptive(minSize = 128.dp), + contentPadding = PaddingValues(8.dp), + modifier = modifier + ) { + items(items) { + GalleryCard(item = it) + } + } +} + +@Composable +fun LoadingIndicator() { + CircularProgressIndicator( + modifier = Modifier.width(64.dp), + color = MaterialTheme.colorScheme.surfaceVariant, + trackColor = MaterialTheme.colorScheme.secondary + ) +} + +@Composable +fun GalleryCard(item: CardImage) { + val showShimmer = remember { mutableStateOf(true) } + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.primaryContainer + ), + elevation = CardDefaults.cardElevation( + defaultElevation = 8.dp + ), + modifier = Modifier + .size(width = 64.dp, height = 256.dp) + .padding(8.dp), + ) { + AsyncImage( + model = item.images.firstOrNull()?.link, + contentDescription = item.title, + contentScale = ContentScale.Crop, + error = painterResource(id = R.drawable.ic_cat_loading), + onSuccess = { showShimmer.value = false }, + onError = { showShimmer.value = false }, + modifier = Modifier + .background( + shimmerBrush( + targetValue = 1300f, + showShimmer = showShimmer.value + ) + ) + ) + } +} + +@Composable +fun shimmerBrush(showShimmer: Boolean = true, targetValue: Float = 1000f): Brush { + return if (showShimmer) { + val shimmerColors = listOf( + Color.LightGray.copy(alpha = 0.6f), + Color.LightGray.copy(alpha = 0.2f), + Color.LightGray.copy(alpha = 0.6f), + ) + + val transition = rememberInfiniteTransition(label = "") + val translateAnimation = transition.animateFloat( + initialValue = 0f, + targetValue = targetValue, + animationSpec = infiniteRepeatable( + animation = tween(800), repeatMode = RepeatMode.Reverse + ), + label = "" + ) + Brush.linearGradient( + colors = shimmerColors, + start = Offset.Zero, + end = Offset(x = translateAnimation.value, y = translateAnimation.value) + ) + } else { + Brush.linearGradient( + colors = listOf(Color.Transparent, Color.Transparent), + start = Offset.Zero, + end = Offset.Zero + ) + } +} + +@Preview +@Composable +fun PreviewErrorHome() { + + val cards = mutableListOf() + for (i in 0..10) { + val card = CardImage( + title = "title", + username = "", + score = 0, + listOf() + ) + cards.add(card) + } + val stateMock = MutableStateFlow(GalleryUiState.Error(ErrorContent("", Throwable()))) + Surface(modifier = Modifier.fillMaxSize()) { + HomeScreen(stateMock) + } +} + +@Preview +@Composable +fun PreviewLoadingHome() { + + val cards = mutableListOf() + for (i in 0..10) { + val card = CardImage( + title = "title", + username = "", + score = 0, + listOf() + ) + cards.add(card) + } + val stateMock = MutableStateFlow(GalleryUiState.Loading) + Surface(modifier = Modifier.fillMaxSize()) { + HomeScreen(stateMock) + } +} + +@Preview +@Composable +fun PreviewContentHome() { + + val cards = mutableListOf() + for (i in 0..10) { + val card = CardImage( + title = "title", + username = "", + score = 0, + listOf() + ) + cards.add(card) + } + val gallery = Gallery( + listOf( + CardImage("", "", 0, emptyList()), + CardImage("", "", 0, emptyList()), + CardImage("", "", 0, emptyList()), + CardImage("", "", 0, emptyList()), + CardImage("", "", 0, emptyList()), + ) + ) + val stateMock = MutableStateFlow(GalleryUiState.Content(gallery)) + Surface(modifier = Modifier.fillMaxSize()) { + HomeScreen(stateMock) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeViewModel.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeViewModel.kt new file mode 100644 index 0000000..754eeaf --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/presentation/HomeViewModel.kt @@ -0,0 +1,31 @@ +package com.luishenrique.cutecatsgallery.home.presentation + +import com.luishenrique.cutecatsgallery.base.BaseViewModel +import com.luishenrique.cutecatsgallery.home.domain.GalleryUseCase +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class HomeViewModel( + private val useCase: GalleryUseCase, + private var catsJob: Job +): BaseViewModel() { + + private val _uiState = MutableStateFlow(GalleryUiState.Loading) + val uiState: StateFlow = _uiState.asStateFlow() + + init { + getCats() + } + + fun getCats() { + catsJob.cancel() + catsJob = request( + block = { useCase.findAllCats(1) }, + onStartRequest = { _uiState.value = GalleryUiState.Loading }, + onSuccess = { _uiState.value = GalleryUiState.Content(it) }, + onError = { _uiState.value = GalleryUiState.Error(it) }, + ) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/utils/FunctionExt.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/utils/FunctionExt.kt new file mode 100644 index 0000000..e77cf36 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/home/utils/FunctionExt.kt @@ -0,0 +1 @@ +package com.luishenrique.cutecatsgallery.home.utils diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/network/CuteCatsInterceptor.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/network/CuteCatsInterceptor.kt new file mode 100644 index 0000000..cfcc538 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/network/CuteCatsInterceptor.kt @@ -0,0 +1,24 @@ +package com.luishenrique.cutecatsgallery.network + +import android.util.Log +import okhttp3.Interceptor +import okhttp3.Response + +class CuteCatsInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + Log.v(TAG, "${request.method}: ${request.url}") + request.headers.forEachIndexed { _, pair -> + Log.v(TAG, "Header --> ${pair.first} : ${pair.second}") + } + if (request.method == POST_METHOD) { + Log.v(TAG, "Body: ${request.body}") + } + return chain.proceed(request) + } + + companion object { + private const val TAG = "RequestInterceptor" + private const val POST_METHOD = "POST" + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/routes/Routes.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/routes/Routes.kt new file mode 100644 index 0000000..5f5a965 --- /dev/null +++ b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/routes/Routes.kt @@ -0,0 +1,6 @@ +package com.luishenrique.cutecatsgallery.routes + +sealed class Routes(val route: String) { + object Home : Routes("HOME") + object Details : Routes("DETAILS") +} diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/adapter/HomeAdapter.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/adapter/HomeAdapter.kt deleted file mode 100644 index 6325bfb..0000000 --- a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/adapter/HomeAdapter.kt +++ /dev/null @@ -1,171 +0,0 @@ -package com.luishenrique.cutecatsgallery.ui.adapter - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.bumptech.glide.Glide -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.luishenrique.domain.entity.Image -import android.widget.Toast -import android.graphics.drawable.Drawable -import com.luishenrique.cutecatsgallery.R -import java.io.File -import android.graphics.Bitmap -import android.graphics.drawable.BitmapDrawable -import android.os.Environment -import android.widget.ImageView -import android.widget.TextView -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import java.io.FileOutputStream -import java.io.OutputStream -import java.lang.Exception -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.CustomTarget -import com.bumptech.glide.request.target.Target -import com.bumptech.glide.request.transition.Transition -import com.google.android.material.progressindicator.CircularProgressIndicator - -class HomeAdapter( - private val context: Context, - private val verifyPermissions: () -> Boolean -) : RecyclerView.Adapter() { - - var images: List = listOf() - set(value) { - field = value - notifyDataSetChanged() - } - - inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { - - private val icon = view.findViewById(R.id.xIcon) - private val username = view.findViewById(R.id.xUsername) - private val score = view.findViewById(R.id.xScore) - private val downloadImage = view.findViewById(R.id.xDownloadImage) - private val progressBar = view.findViewById(R.id.xProgressBar) - - fun bind(image: Image) { - username.text = image.username - score.text = image.score.toString() - - Glide.with(context) - .load(image.images?.get(0)?.link) - .override(200, 240) - .diskCacheStrategy(DiskCacheStrategy.DATA) - .listener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ): Boolean { - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - progressBar.visibility = View.GONE - return false - } - - }) - .error(R.drawable.ic_cat_loading) - .into(icon) - - downloadImage.setOnClickListener { - CoroutineScope(Dispatchers.IO).launch { - downloadImage(image.images?.get(0)?.link) - } - } - } - - private fun downloadImage(imageURL: String?) { - if (imageURL.isNullOrBlank() || !verifyPermissions()) return - val dirPath: String = run { - Environment.getExternalStorageDirectory().absolutePath + - "/" + context.getString(R.string.app_name_storage) - } - val dir = File(dirPath) - val fileName: String = imageURL.substring( - imageURL.lastIndexOf('/') + 1 - ) - - Glide.with(context) - .load(imageURL) - .into(object : CustomTarget() { - override fun onResourceReady( - resource: Drawable, - transition: Transition? - ) { - val bitmap: Bitmap = (resource as BitmapDrawable).bitmap - Toast.makeText( - context, - "Salvando imagem...", - Toast.LENGTH_SHORT - ).show() - saveImage(bitmap, dir, fileName) - } - override fun onLoadCleared(placeholder: Drawable?) {} - override fun onLoadFailed(errorDrawable: Drawable?) {} - }) - } - - private fun saveImage(image: Bitmap, storageDir: File, imageFileName: String) { - val theFolderExists = storageDir.exists() - if (!storageDir.exists()) { - val successDirCreated = storageDir.mkdir() - if (!successDirCreated) { - Toast.makeText( - context, - "Falha ao criar pasta!", - Toast.LENGTH_SHORT - ).show() - return - } - } - if (theFolderExists) { - val imageFile = File(storageDir, imageFileName) - val savedImagePath = imageFile.absolutePath - try { - val fOut: OutputStream = FileOutputStream(imageFile) - image.compress(Bitmap.CompressFormat.JPEG, 100, fOut) - fOut.close() - Toast.makeText(context, "Imagem Salva!\n$savedImagePath", Toast.LENGTH_SHORT).show() - } catch (e: Exception) { - Toast.makeText( - context, - "Erro ao salvar imagem!", - Toast.LENGTH_SHORT - ).show() - e.printStackTrace() - } - } - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view: View = LayoutInflater.from(parent.context).inflate( - R.layout.item_list, - parent, - false - ) - return ViewHolder(view) - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(images[position]) - } - - override fun getItemCount() = images.size -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/view/HomeActivity.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/view/HomeActivity.kt deleted file mode 100644 index d93edbb..0000000 --- a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/view/HomeActivity.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.luishenrique.cutecatsgallery.ui.view - -import android.Manifest -import android.content.pm.PackageManager -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle -import android.view.View -import androidx.core.app.ActivityCompat -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.luishenrique.cutecatsgallery.R -import com.luishenrique.cutecatsgallery.databinding.ActivityHomeBinding -import com.luishenrique.cutecatsgallery.ui.adapter.HomeAdapter -import com.luishenrique.cutecatsgallery.ui.viewmodel.HomeViewModel -import com.luishenrique.domain.entity.Image -import org.koin.android.viewmodel.ext.android.viewModel -import com.roger.catloadinglibrary.CatLoadingView - -class HomeActivity : AppCompatActivity() { - - private val mViewModel: HomeViewModel by viewModel() - private lateinit var mBinding: ActivityHomeBinding - private val mProgressBarCat: CatLoadingView = CatLoadingView() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - mBinding = ActivityHomeBinding.inflate(layoutInflater) - setContentView(mBinding.root) - - mProgressBarCat.setBackgroundColor(R.color.primary_color) - mViewModel.findAllCats() - setObservables() - - with(mBinding) { - xTryAgain.setOnClickListener { - mViewModel.findAllCats() - } - xRefresh.setOnClickListener { - mViewModel.findAllCats() - } - xList.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - super.onScrollStateChanged(recyclerView, newState) - - if (!recyclerView.canScrollVertically(1)) { - this@with.xRefresh.visibility = View.VISIBLE - } else { - this@with.xRefresh.visibility = View.GONE - } - } - }) - } - } - - private fun setObservables() { - mViewModel.gallery.observe(this) { - setRecyclerView(it.filterToPhoto()) - } - mViewModel.loading.observe(this) { - if (it) { - mProgressBarCat.show(supportFragmentManager, "cat") - } else { - mProgressBarCat.dismiss() - } - } - mViewModel.stateHome.observe(this) { - mBinding.xList.visibility = it - if (it == View.INVISIBLE) { - mBinding.xMessage.visibility = View.VISIBLE - mBinding.xTryAgain.visibility = View.VISIBLE - mBinding.xIconErrorConnection.visibility = View.VISIBLE - mBinding.xRefresh.visibility = View.GONE - } else { - mBinding.xMessage.visibility = View.GONE - mBinding.xTryAgain.visibility = View.GONE - mBinding.xIconErrorConnection.visibility = View.GONE - } - } - } - - private fun setRecyclerView(images: List) { - with(mBinding.xList) { - layoutManager = GridLayoutManager(this@HomeActivity, 2) - adapter = HomeAdapter(this@HomeActivity, ::verifyPermissions).apply { - this.images = images - } - } - } - - private fun verifyPermissions(): Boolean { - val permissionExternalMemory = ActivityCompat.checkSelfPermission( - this, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - if (permissionExternalMemory != PackageManager.PERMISSION_GRANTED) { - val storagePermission = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) - ActivityCompat.requestPermissions(this, storagePermission, 1) - return false - } - return true - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/viewmodel/HomeViewModel.kt b/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/viewmodel/HomeViewModel.kt deleted file mode 100644 index 90b2f07..0000000 --- a/app/src/main/kotlin/com/luishenrique/cutecatsgallery/ui/viewmodel/HomeViewModel.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.luishenrique.cutecatsgallery.ui.viewmodel - -import android.view.View -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.luishenrique.domain.entity.Gallery -import androidx.lifecycle.viewModelScope -import com.luishenrique.domain.usecase.GalleryUseCase -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch - -class HomeViewModel(private val useCase: GalleryUseCase): ViewModel() { - - private val _loading: MutableLiveData = MutableLiveData() - val loading: LiveData = _loading - - private val _gallery: MutableLiveData = MutableLiveData() - val gallery: LiveData = _gallery - - private val _stateHome: MutableLiveData = MutableLiveData() - val stateHome: LiveData = _stateHome - - private val _page: MutableLiveData = MutableLiveData(1) - - fun findAllCats() { - _loading.postValue(true) - useCase.findAllCats(page = _page.value!!, onSuccess = { - _page.postValue(_page.value?.plus(1)) - _gallery.postValue(it) - _loading.postValue(false) - _stateHome.postValue(View.VISIBLE) - }, { - _stateHome.postValue(View.INVISIBLE) - delay(500) - _loading.postValue(false) - }) - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml deleted file mode 100644 index a050049..0000000 --- a/app/src/main/res/layout/activity_home.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - -