Skip to content

Commit

Permalink
Merge pull request #4 from asrovnov/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
asrovnov authored Mar 1, 2020
2 parents 25d1922 + a024d01 commit 03a8346
Show file tree
Hide file tree
Showing 32 changed files with 486 additions and 72 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
incredible-pets
Incredible Pets
======================

[![Build Status](https://travis-ci.org/asrovnov/incredible-pets-android.svg?branch=master)](https://travis-ci.org/asrovnov/incredible-pets-android)
[![Build Status](https://travis-ci.org/asrovnov/incredible-pets-android.svg?branch=master)](https://travis-ci.org/asrovnov/incredible-pets-android)

---
![incredible-pets-start-screen](https://user-images.githubusercontent.com/28740150/75627008-6f370c00-5bdd-11ea-9cd0-7c625017fdbe.gif)
---

***Stack:***
* Kotlin
* Clean Architecture
* Single-Activity
* RxPM
* RxJava
* Koin
* Retrofit
* Moshi



3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,7 @@ dependencies {

// Glide
implementation 'com.github.bumptech.glide:glide:4.10.0'

// File Downloader
implementation 'com.liulishuo.filedownloader:library:1.7.6'
}
15 changes: 11 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package="ru.app.incredible.pets">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<application
android:name=".App"
Expand All @@ -15,13 +17,18 @@
android:theme="@style/SplashTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name=".AppActivity">
android:name=".AppActivity"
android:launchMode="singleInstance"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</application>

</manifest>
15 changes: 15 additions & 0 deletions app/src/main/java/ru/app/incredible/pets/App.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ru.app.incredible.pets

import android.app.Application
import com.liulishuo.filedownloader.FileDownloader
import com.liulishuo.filedownloader.connection.FileDownloadUrlConnection
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import org.koin.core.module.Module
Expand All @@ -15,6 +17,7 @@ class App : Application() {

initKoin()
initLogger()
initFileDownloader()
}

private fun initKoin() {
Expand All @@ -39,4 +42,16 @@ class App : Application() {
PmModule.create()
)
}

private fun initFileDownloader() {
FileDownloader.setupOnApplicationOnCreate(this)
.connectionCreator(
FileDownloadUrlConnection.Creator(
FileDownloadUrlConnection.Configuration()
.connectTimeout(15000)
.readTimeout(15000)
)
)
.commit()
}
}
7 changes: 2 additions & 5 deletions app/src/main/java/ru/app/incredible/pets/AppActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ class AppActivity : AppCompatActivity(), NavigationMessageHandler {
private lateinit var navigator: FragmentNavigator

override fun onCreate(savedInstanceState: Bundle?) {

val instanceState = null

super.onCreate(instanceState)
super.onCreate(savedInstanceState)

setTheme(R.style.AppTheme)

Expand All @@ -28,7 +25,7 @@ class AppActivity : AppCompatActivity(), NavigationMessageHandler {
navigator = FragmentNavigator(R.id.screenContainer, supportFragmentManager, this)

@Suppress("SENSELESS_COMPARISON")
if (instanceState == null) {
if (savedInstanceState == null) {
navigator.setRoot(MainBottomBarScreen())
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package ru.app.incredible.pets.data.gateway

import android.content.Context
import com.jakewharton.rxrelay2.BehaviorRelay
import com.liulishuo.filedownloader.BaseDownloadTask
import com.liulishuo.filedownloader.FileDownloadSampleListener
import com.liulishuo.filedownloader.FileDownloader
import com.liulishuo.filedownloader.model.FileDownloadStatus
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import ru.app.incredible.pets.domain.ImageDownloadState
import ru.app.incredible.pets.domain.Pet
import ru.app.incredible.pets.domain.exceptions.InternetUnavailableException
import java.io.File

class DownloadImageGateway(
private val context: Context,
private val networkStateGateway: NetworkStateGateway
) {

companion object {
private const val DIR_IMAGE = "images"
private const val BOTTOM_SPACE = "_"
}

private val downloadImageIds = mutableMapOf<Pet, Int>()
private val fileDownloader = FileDownloader.getImpl()

private val imageDownloadStates: BehaviorRelay<MutableMap<Pet, ImageDownloadState>> =
BehaviorRelay.createDefault(
mutableMapOf()
)

fun getDownloadState(pet: Pet, imageUrl: String): Observable<ImageDownloadState> {
return imageDownloadStates.hide()
.map { it[pet] ?: ImageDownloadState.IDLE }
.doOnSubscribe { checkLoadImage(pet, imageUrl) }
}

fun startDownload(pet: Pet, imageUrl: String): Completable {
return networkStateGateway
.isNetworkEnabled()
.firstOrError()
.flatMapCompletable {
if (it) {
Completable.fromAction {
imageDownload(pet, imageUrl)
}
} else {
Completable.error(InternetUnavailableException())
}
}
}

fun removeImage(imageUrl: String): Single<Boolean> {
return Single
.fromCallable { getImageFile(imageUrl).delete() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}

private fun imageDownload(pet: Pet, imageUrl: String) {
val downloadStart = fileDownloader.create(imageUrl)
.setPath(getImageFile(imageUrl).toString())
.setListener(object : FileDownloadSampleListener() {
override fun started(task: BaseDownloadTask?) {
updateDownloadState(pet, ImageDownloadState.PROGRESS)
}

override fun completed(task: BaseDownloadTask?) {
updateDownloadState(pet, ImageDownloadState.FINISHED)
}

override fun error(task: BaseDownloadTask?, e: Throwable?) {
updateDownloadState(pet, ImageDownloadState.ERROR)
}
})

downloadImageIds[pet] = downloadStart.start()
}

private fun getImageFile(imageUrl: String): File {
val imageName =
"${imageUrl.substringBeforeLast("/").substringAfterLast("/")}$BOTTOM_SPACE${imageUrl.substringAfterLast("/")}"

return File(context.getExternalFilesDir(DIR_IMAGE), imageName)
}

private fun checkLoadImage(pet: Pet, imageUrl: String) {
val downloadStatus = downloadImageIds[pet]?.let {
fileDownloader.getStatusIgnoreCompleted(it)
}

when {
getImageFile(imageUrl).exists() -> updateDownloadState(pet, ImageDownloadState.FINISHED)
downloadStatus == FileDownloadStatus.progress -> updateDownloadState(
pet,
ImageDownloadState.PROGRESS
)
else -> updateDownloadState(pet, ImageDownloadState.IDLE)
}
}

private fun updateDownloadState(pet: Pet, state: ImageDownloadState) {
imageDownloadStates.accept(
imageDownloadStates.value.apply {
this!![pet] = state
}!!
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.app.incredible.pets.data.gateway

import com.jakewharton.rxrelay2.BehaviorRelay
import io.reactivex.Observable

class PetGateway {

companion object {
const val PET_DOG = 0
const val PET_CAT = 1
}

private val selectedPet: BehaviorRelay<Int> = BehaviorRelay.createDefault(PET_DOG)

fun getSelectedPet(): Observable<Int> = selectedPet.hide()

fun setSelectedPet(petType: Int) {
if (selectedPet.hasValue().not() || selectedPet.value != petType) {
selectedPet.accept(petType)
}
}
}
6 changes: 3 additions & 3 deletions app/src/main/java/ru/app/incredible/pets/di/GatewayModule.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package ru.app.incredible.pets.di

import org.koin.dsl.module
import ru.app.incredible.pets.data.gateway.NetworkStateGateway
import ru.app.incredible.pets.data.gateway.RandomCatGateway
import ru.app.incredible.pets.data.gateway.RandomDogGateway
import ru.app.incredible.pets.data.gateway.*

object GatewayModule {

fun create() = module {
single { RandomDogGateway(get()) }
single { RandomCatGateway(get()) }
single { NetworkStateGateway(get()) }
single { DownloadImageGateway(get(), get()) }
single { PetGateway() }
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package ru.app.incredible.pets.di

import org.koin.dsl.module
import ru.app.incredible.pets.domain.RandomCatInteractor
import ru.app.incredible.pets.domain.RandomDogInteractor
import ru.app.incredible.pets.domain.*

object InteractorModule {

fun create() = module {
factory { RandomDogInteractor(get(), get()) }
factory { RandomCatInteractor(get(), get()) }
factory { DownloadImageInteractor(get()) }
factory { GetDownloadStateInteractor(get()) }
factory { RemoveImageInteractor(get()) }
}
}
6 changes: 3 additions & 3 deletions app/src/main/java/ru/app/incredible/pets/di/PmModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import ru.app.incredible.pets.ui.main.MainBottomBarPm
object PmModule {

fun create() = module {
factory { MainBottomBarPm() }
factory { DogPm(get(), get()) }
factory { CatPm(get(), get()) }
factory { MainBottomBarPm(get()) }
factory { DogPm(get(), get(), get(), get(), get(), get(), get()) }
factory { CatPm(get(), get(), get()) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.app.incredible.pets.domain

import io.reactivex.Completable
import ru.app.incredible.pets.data.gateway.DownloadImageGateway

class DownloadImageInteractor(
private val downloadImageGateway: DownloadImageGateway
) {

fun execute(pet: Pet, imageUrl: String): Completable {
return downloadImageGateway.startDownload(pet, imageUrl)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.app.incredible.pets.domain

import io.reactivex.Observable
import ru.app.incredible.pets.data.gateway.DownloadImageGateway

class GetDownloadStateInteractor(
private val downloadImageGateway: DownloadImageGateway
) {

fun execute(pet: Pet, imageUrl: String): Observable<ImageDownloadState> {
return downloadImageGateway.getDownloadState(pet, imageUrl)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ru.app.incredible.pets.domain

enum class ImageDownloadState {
IDLE, FINISHED, ERROR, PROGRESS
}
3 changes: 3 additions & 0 deletions app/src/main/java/ru/app/incredible/pets/domain/Pet.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package ru.app.incredible.pets.domain

data class Pet(val petId: Int)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.app.incredible.pets.domain

import io.reactivex.Single
import ru.app.incredible.pets.data.gateway.DownloadImageGateway

class RemoveImageInteractor(
private val downloadImageGateway: DownloadImageGateway
) {

fun execute(imageUrl: String): Single<Boolean> {
return downloadImageGateway.removeImage(imageUrl)
}
}
Loading

0 comments on commit 03a8346

Please sign in to comment.