Skip to content
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

Kotlin flow (Demo) #7

Open
wants to merge 24 commits into
base: master
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
12 changes: 6 additions & 6 deletions api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ targetCompatibility = JavaVersion.VERSION_1_8
dependencies {
implementation Deps.kotlin_jdk
implementation Deps.retrofit
compile Deps.retrofitMoshiConverter
api Deps.retrofitMoshiConverter
implementation Deps.okhttp
compile Deps.loggingInterceptor
compile project(':mocks')
api Deps.loggingInterceptor
implementation project(':mocks')

compile(TestDeps.mockWebServer) {
exclude group: 'org.hamcrest', module: 'hamcrest-core'
}
implementation Deps.retrofitRxJavaAdapter

compile Deps.dagger
compile Deps.daggerAndroid
compile Deps.daggerAndroidSupport
api Deps.dagger
api Deps.daggerAndroid
api Deps.daggerAndroidSupport
kapt Deps.daggerCompiler
kapt Deps.daggerAndroidProcessor

Expand Down
31 changes: 22 additions & 9 deletions api/src/main/java/com/ragdroid/api/MarvelApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,30 @@ const val CHARACTER_ID = "characterId"
interface MarvelApi {

@GET("characters")
fun getCharacters(@Query(API_KEY) publicKey: String,
@Query(HASH) md5Digest: String,
@Query(TIMESTAMP) timestamp: Long,
@Query(OFFSET) offset: Int?,
@Query(LIMIT) limit: Int?): Single<TDataWrapper<List<TCharacterMarvel>>>
suspend fun getCharacters(@Query(API_KEY) publicKey: String,
@Query(HASH) md5Digest: String,
@Query(TIMESTAMP) timestamp: Long,
@Query(OFFSET) offset: Int?,
@Query(LIMIT) limit: Int?): TDataWrapper<List<TCharacterMarvel>>

@GET("characters/{characterId}")
fun getCharacter(@Path(CHARACTER_ID) characterId: Long,
@Query(API_KEY) publicKey: String,
@Query(HASH) md5Digest: String,
@Query(TIMESTAMP) timestamp: Long): Single<TDataWrapper<List<TCharacterMarvel>>>
suspend fun getCharacter(@Path(CHARACTER_ID) characterId: Long,
@Query(API_KEY) publicKey: String,
@Query(HASH) md5Digest: String,
@Query(TIMESTAMP) timestamp: Long): TDataWrapper<List<TCharacterMarvel>>

@GET("characters")
fun getCharactersSingle(@Query(API_KEY) publicKey: String,
@Query(HASH) md5Digest: String,
@Query(TIMESTAMP) timestamp: Long,
@Query(OFFSET) offset: Int?,
@Query(LIMIT) limit: Int?): Single<TDataWrapper<List<TCharacterMarvel>>>

@GET("characters/{characterId}")
fun getCharacterSingle(@Path(CHARACTER_ID) characterId: Long,
@Query(API_KEY) publicKey: String,
@Query(HASH) md5Digest: String,
@Query(TIMESTAMP) timestamp: Long): Single<TDataWrapper<List<TCharacterMarvel>>>

/**
* Retrieve list of comics by character Id
Expand Down
5 changes: 2 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.3.21'
repositories {
mavenCentral()
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0-rc01'
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin_version}"
classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin_version}"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
30 changes: 22 additions & 8 deletions buildSrc/src/main/java/Dependencies.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@

object Versions {
val kotlin_version = "1.3.11"
val kotlin_version = "1.3.30"
val kotlinx_coroutines = "1.3.2"

val compileSdk = 28
val minSdk = 21
val retrofitVersion = "2.3.0"
val retrofitVersion = "2.6.0"
val support_library = "27.1.1"
val material = "1.0.0"
val okhttpVersion = "3.9.1"
val constraint_layout = "2.0.0-alpha3"
val navigationVersion = "1.0.0-rc01"
val lifecycleEx = "2.0.0"
val lifecycleRx = "2.0.0"
val lifecycleEx = "2.2.0-alpha02"
val lifecycleRx = "2.2.0-alpha02"
val lifecycleSavedState = "1.0.0-alpha02"
val lifecycleCompiler = "2.0.0"
val arch_comp = "1.1.1"
val arch_comp_paging= "2.1.0"
val dagger = "2.20"
val dagger = "2.24"
val ktlint = "0.10.0"
val leakcanary = "1.6.2"
val rx = "2.1.6"
Expand All @@ -29,27 +30,36 @@ object Versions {
val reclaim = "1.2.2"
val rxBindings = "2.1.1"
val appCompat = "1.0.2"
val android_ktx = "1.2.0-alpha02"
val coroutine_binding = "1.0.0-RC2"
val flowExtensions = "0.0.2"
val wasabeef = "3.0.0"

// test libraries
val archTesting = "2.0.1"
val kluent = "1.4"
val spek = "1.1.5"
val mockitoKotlin = "1.5.0"
val mockitoKotlin = "2.1.0"
val mockito = "2.8.9"
val testRunner = "1.1.1"
val espresso = "3.1.0"
val crashlytics = "2.7.1"
}

object Deps {
val kotlin_jdk = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin_version}"
val kotlin_jdk = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${Versions.kotlin_version}"
val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.kotlinx_coroutines}"
val coroutinesRxInterop = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:${Versions.kotlinx_coroutines}"
val androidKtx = "androidx.core:core-ktx:${Versions.android_ktx}"
val constraintLayout = "androidx.constraintlayout:constraintlayout:${Versions.constraint_layout}"
val timber = "com.jakewharton.timber:timber:${Versions.timberVersion}"
val reclaim = "com.github.fueled:reclaim:${Versions.reclaim}"
val lifecycleEx = "androidx.lifecycle:lifecycle-extensions:${Versions.lifecycleEx}"
val lifecycleRx = "androidx.lifecycle:lifecycle-reactivestreams:${Versions.lifecycleRx}"
val lifecycleSavedState = "androidx.lifecycle:lifecycle-viewmodel-savedstate:${Versions.lifecycleSavedState}"
val lifecycleCompiler = "androidx.lifecycle:lifecycle-compiler:${Versions.lifecycleRx}"
val lifecycleViewModelKtx = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.lifecycleEx}"
val lifecycleRuntimeKtx = "androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycleEx}"
val navigationFragment = "android.arch.navigation:navigation-fragment-ktx:${Versions.navigationVersion}"
val navigationUIKtx = "android.arch.navigation:navigation-ui-ktx:${Versions.navigationVersion}"
val pagingRuntime = "androidx.paging:paging-runtime:${Versions.arch_comp_paging}"
Expand All @@ -72,6 +82,9 @@ object Deps {
val retrofitRxJavaAdapter = "com.squareup.retrofit2:adapter-rxjava2:${Versions.retrofitVersion}"
val okhttp = "com.squareup.okhttp3:okhttp:${Versions.okhttpVersion}"
val loggingInterceptor = "com.squareup.okhttp3:logging-interceptor:${Versions.okhttpVersion}"
val coroutineBindingSwipeRefresh = "com.github.satoshun.coroutinebinding:coroutinebinding-swiperefreshlayout:${Versions.coroutine_binding}"
val flowExtensions = "com.github.akarnokd:kotlin-flow-extensions:${Versions.flowExtensions}"
val recyclerViewAnimations = "jp.wasabeef:recyclerview-animators:${Versions.wasabeef}"
}

object TestDeps {
Expand All @@ -87,6 +100,7 @@ object TestDeps {
val kluent = "org.amshove.kluent:kluent:${Versions.kluent}"
val spek = "org.jetbrains.spek:spek-api:${Versions.spek}"
val spekJunitPlatformEngine = "org.jetbrains.spek:spek-junit-platform-engine:${Versions.spek}"
val mockitoKotlin = "com.nhaarman:mockito-kotlin-kt1.1:${Versions.mockitoKotlin}"
val mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:${Versions.mockitoKotlin}"
val mockito = "org.mockito:mockito-core:${Versions.mockito}"
val coroutinesTest = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.kotlinx_coroutines}"
}
16 changes: 9 additions & 7 deletions data/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,24 @@ sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8

dependencies {
implementation Deps.coroutinesAndroid
implementation Deps.coroutinesRxInterop
implementation Deps.kotlin_jdk
compile project(':api')
api project(':api')

compile Deps.timber
implementation Deps.timber
implementation Deps.rx

compile Deps.dagger
compile Deps.daggerAndroid
compile Deps.daggerAndroidSupport
implementation Deps.dagger
implementation Deps.daggerAndroid
implementation Deps.daggerAndroidSupport
kapt Deps.daggerCompiler
kapt Deps.daggerAndroidProcessor

testCompile TestDeps.kluent
testImplementation TestDeps.kluent
testImplementation TestDeps.spek
testImplementation TestDeps.spekJunitPlatformEngine
testCompile TestDeps.mockitoKotlin
testImplementation TestDeps.mockitoKotlin
testImplementation TestDeps.mockito

}
55 changes: 46 additions & 9 deletions data/src/main/java/com/ragdroid/data/MainRepositoryImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,36 @@ import com.ragdroid.data.entity.AppConfig
import com.ragdroid.data.entity.CharacterMapper
import com.ragdroid.data.entity.CharacterMarvel
import io.reactivex.Single
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

/**
* Created by garimajain on 18/11/17.
*/
class MainRepositoryImpl
@Inject
constructor(
class MainRepositoryImpl @Inject constructor(
private val marvelApi: MarvelApi,
private val characterMapper: CharacterMapper,
private val config: AppConfig,
private val helpers: Helpers): MainRepository {

override suspend fun fetchCharacters(): List<CharacterMarvel> {
val timeStamp = System.currentTimeMillis()
val charactersWrapper = marvelApi.getCharacters(
config.publicKey,
helpers.buildMD5Digest("" + timeStamp + config.privateKey
+ config.publicKey),
timeStamp,
0,
50)
val characters = charactersWrapper.data.results
.map {
characterMapper.map(it)
}.toList()
return characters
}

override fun fetchCharacters(): Single<List<CharacterMarvel>> {
override fun fetchCharactersSingle(): Single<List<CharacterMarvel>> {
val timeStamp = System.currentTimeMillis()
return charactersApiSingle(timeStamp)
.map { dataWrapper: TDataWrapper<List<TCharacterMarvel>> ->
Expand All @@ -33,7 +48,7 @@ constructor(
}
}

override fun fetchCharacter(id: Long): Single<CharacterMarvel> {
override fun fetchCharacterSingle(id: Long): Single<CharacterMarvel> {
val timeStamp = System.currentTimeMillis()
return characterApiSingle(id, timeStamp)
.map { dataWrapper: TDataWrapper<List<TCharacterMarvel>> ->
Expand All @@ -47,16 +62,17 @@ constructor(
}

private fun characterApiSingle(id: Long, timeStamp: Long): Single<TDataWrapper<List<TCharacterMarvel>>> {
return marvelApi.getCharacter(
return marvelApi.getCharacterSingle(
id,
config.publicKey,
helpers.buildMD5Digest("" + timeStamp + config.privateKey
+ config.publicKey),
timeStamp
)
}

private fun charactersApiSingle(timeStamp: Long): Single<TDataWrapper<List<TCharacterMarvel>>> {
return marvelApi.getCharacters(
return marvelApi.getCharactersSingle(
config.publicKey,
helpers.buildMD5Digest("" + timeStamp + config.privateKey
+ config.publicKey),
Expand All @@ -66,10 +82,31 @@ constructor(
)
}

override suspend fun fetchCharacter(id: Long): CharacterMarvel {
val timeStamp = System.currentTimeMillis()
val characterWrapper = marvelApi.getCharacter(
id,
config.publicKey,
helpers.buildMD5Digest("" + timeStamp + config.privateKey
+ config.publicKey), timeStamp)

val results = characterWrapper.data.results
.map {
characterMapper.map(it)
}.getOrNull(0) ?: throw IllegalAccessException("Character for id $id not found")
return results
}



}

interface MainRepository {

fun fetchCharacters(): Single<List<CharacterMarvel>>
fun fetchCharacter(id: Long): Single<CharacterMarvel>
fun fetchCharactersSingle(): Single<List<CharacterMarvel>>
fun fetchCharacterSingle(id: Long): Single<CharacterMarvel>


suspend fun fetchCharacters(): List<CharacterMarvel>
suspend fun fetchCharacter(id: Long): CharacterMarvel
}
8 changes: 4 additions & 4 deletions data/src/test/java/com/ragdroid/data/MainRepositorySpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ object MainRepositorySpec: Spek({
}

on("fetching from repository") {
`when`(mockApi.getCharacters(anyString(), anyString(), anyLong(), anyInt(), anyInt()))
`when`(mockApi.getCharactersSingle(anyString(), anyString(), anyLong(), anyInt(), anyInt()))
.thenReturn(Single.just(getFakeMarvelCharacters()))
val testObserver = TestObserver<List<CharacterMarvel>>()
repository.fetchCharacters()
repository.fetchCharactersSingle()
.subscribe(testObserver)

it("should receive 1 value") {
Expand All @@ -61,10 +61,10 @@ object MainRepositorySpec: Spek({

on("fetching from repository gives error") {
val error = mock(Throwable::class)
`when`(mockApi.getCharacters(anyString(), anyString(), anyLong(), anyInt(), anyInt()))
`when`(mockApi.getCharactersSingle(anyString(), anyString(), anyLong(), anyInt(), anyInt()))
.thenReturn(Single.error(error))
val testObserver = TestObserver<List<CharacterMarvel>>()
repository.fetchCharacters()
repository.fetchCharactersSingle()
.subscribe(testObserver)
it("should error") {
testObserver.assertError(error)
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Fri Nov 10 18:33:46 IST 2017
#Thu Oct 24 04:38:58 IST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
3 changes: 0 additions & 3 deletions mvvmi/src/main/java/com/ragdroid/mvvmi/core/MviView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ interface MviView<Action: MviAction, State: MviState> {
fun onMviViewCreated(savedInstanceState: Bundle?) {
subscribeToViewState()
subscribeToNavigationState()
if (savedInstanceState == null) {
viewModel.processActions(provideActions())
}
}

/**
Expand Down
11 changes: 4 additions & 7 deletions mvvmi/src/main/java/com/ragdroid/mvvmi/core/MviViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import timber.log.Timber
abstract class MviViewModel<Action: MviAction, Result: MviResult, State: MviState>(initialState: State) : ViewModel() {

private val stateProcessor: PublishProcessor<State> = PublishProcessor.create()
protected val actionsProcessor: PublishProcessor<Action> = PublishProcessor.create()
private val actionsProcessor: PublishProcessor<Action> = PublishProcessor.create()

protected var currentState: State = initialState
private set
Expand Down Expand Up @@ -55,12 +55,9 @@ abstract class MviViewModel<Action: MviAction, Result: MviResult, State: MviStat
actionsToResultTransformer(actions: Flowable<Action>): Flowable<Result>

@CallSuper
fun processActions(actions: Flowable<Action>) {
actionsProcessor.mergeWith(actions)
.doOnNext {
Timber.v("onAction $it")
}
.compose(actionToResultTransformer)
fun processActions() {

actionsToResultTransformer(actionsProcessor)
.doOnNext {
Timber.v("onResult $it")
}
Expand Down
1 change: 0 additions & 1 deletion mvvmi/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
<resources>
<string name="app_name">mvvmi</string>
</resources>
Loading