Skip to content

Commit

Permalink
Migrate to new architecture and remove unused dependencies (#167)
Browse files Browse the repository at this point in the history
  • Loading branch information
mirland authored May 4, 2021
1 parent 07c7dbb commit b0b617e
Show file tree
Hide file tree
Showing 35 changed files with 455 additions and 530 deletions.
44 changes: 9 additions & 35 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ buildscript {
}
}

apply plugin: 'com.android.application'
plugins {
id "com.android.application"
id "kotlin-android"
id "kotlin-parcelize"
id "kotlin-kapt"
}

apply from: rootProject.file('scripts/versioning.gradle')
apply from: rootProject.file('scripts/read_properties.gradle')
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.jetbrains.kotlin.android.extensions'
//apply plugin: 'androidx.navigation.safeargs.kotlin' TODO enable when compatible

if (getEnvVariable('KEYSTORE_FILE') != null) {
// Avoid Signing not ready issues in Gradle Play Publisher plugin
Expand Down Expand Up @@ -44,7 +45,6 @@ android {

buildFeatures {
compose true
viewBinding true
}

signingConfigs {
Expand Down Expand Up @@ -93,7 +93,7 @@ android {

lintOptions {
lintConfig file("lint.xml")
disable "UnsafeExperimentalUsageError", "UnsafeExperimentalUsageWarning", "ObsoleteLintCustomCheck"
disable "ObsoleteLintCustomCheck"
// Timber linter doesn't work with AGP 7.0.0-alpha13 https://github.com/JakeWharton/timber/issues/408
disable "LogNotTimber", "StringFormatInTimber", "ThrowableNotAtBeginning", "BinaryOperationInTimber",
"TimberArgCount", "TimberArgTypes", "TimberTagLength", "TimberExceptionLogging"
Expand All @@ -106,13 +106,9 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}

androidExtensions {
experimental = true
}

kotlinOptions {
useIR = true
languageVersion = "1.4"
languageVersion = "1.5"
jvmTarget = '1.8'
freeCompilerArgs += [
"-XXLanguage:+InlineClasses",
Expand All @@ -125,10 +121,8 @@ android {
"-Xuse-experimental=kotlinx.coroutines.FlowPreview",
"-Xuse-experimental=kotlin.ExperimentalStdlibApi",
]
languageVersion = "1.4"
}
composeOptions {
kotlinCompilerVersion versions.kotlin
kotlinCompilerExtensionVersion versions.compose
}
}
Expand All @@ -139,43 +133,23 @@ dependencies {

debugImplementation deps.leakcanary

devImplementation deps.androidSwissKnife.navigationDebug

implementation deps.androidSwissKnife.core
implementation deps.androidSwissKnife.dataStore
implementation deps.androidSwissKnife.navigation
implementation deps.androidX.appcompat
implementation deps.androidX.cardview
implementation deps.androidX.constraint_layout
implementation deps.androidX.dataStore
implementation deps.androidX.design
implementation deps.androidX.lifecycle.livedata
implementation deps.androidX.lifecycle.runtime
implementation deps.androidX.lifecycle.saveState
implementation deps.androidX.lifecycle.viewmodel
implementation deps.androidX.navigation.fragment
implementation deps.androidX.navigation.ui
implementation deps.androidX.recyclerview
implementation deps.androidX.room.coreKtx
implementation deps.androidX.transition
implementation deps.androidX.viewPager2
implementation deps.coil
implementation deps.compose.compiler
implementation deps.compose.constraintLayout
implementation deps.compose.foundation
implementation deps.compose.liveData
implementation deps.compose.navigation
implementation deps.compose.material
implementation deps.compose.runtime
implementation deps.compose.tooling
implementation deps.compose.ui
implementation deps.compose.viewModel
implementation deps.firebase.crashlytics
implementation deps.koin.android
implementation deps.koin.compose
implementation deps.kotlin.core
implementation deps.kotlin.coroutines.core
implementation deps.kotlin.reflect
implementation deps.okIo
implementation deps.okhttp3.core
implementation deps.okhttp3.loggingIntercepror
Expand Down
3 changes: 1 addition & 2 deletions app/src/dev/java/com/xmartlabs/gong/App.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.xmartlabs.gong

import com.xmartlabs.gong.device.di.DiAppModules
import com.xmartlabs.gong.device.logger.DebugNavigationLogger
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin

Expand All @@ -14,7 +13,7 @@ class App : AppBase() {
override fun setupKoinModules() {
startKoin {
androidContext(this@App)
modules(DiAppModules.provideModules(DebugNavigationLogger()))
modules(DiAppModules.provideModules())
}
}

Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar"
android:theme="@style/AppTheme"
tools:targetApi="n"
>

Expand Down
69 changes: 67 additions & 2 deletions app/src/main/java/com/xmartlabs/gong/device/common/ProcessState.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,80 @@
package com.xmartlabs.gong.device.common

import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

/**
* A generic class that holds a value with its loading status.
* @param <T>
*/
sealed class ProcessState<out R> {
data class Finish<out T>(val result: Result<T>) : ProcessState<T>()
object Loading : ProcessState<Nothing>()
data class Success<out T>(val data: T) : ProcessState<T>(), ProcessResult<T>
data class Failure(val exception: Throwable) : ProcessState<Nothing>(), ProcessResult<Nothing>

override fun toString(): String = when (this) {
is Loading -> "Loading"
is Finish<*> -> "Finish = Result[$result]"
is Success<*> -> "Success[data=$data]"
is Failure -> "Failure[exception=$exception]"
}
}

sealed interface ProcessResult<out T> {
companion object {
inline fun <T> runCatching(block: () -> T): ProcessResult<T> = try {
ProcessState.Success(block())
} catch (ex: Throwable) {
ProcessState.Failure(ex)
}
}
}

@Suppress("UNCHECKED_CAST")
fun <T> ProcessState<T>.asResult(): ProcessResult<T>? =
this as? ProcessResult<T>

fun <T> ProcessResult<T>.getDataOrNull() = (this as? ProcessState.Success<T>)?.data

fun <T> ProcessResult<T>.asProcessState(): ProcessState<T> = when (this) {
is ProcessState.Failure -> this
is ProcessState.Success -> this
}

fun <T> ProcessState<T>.getDataOrNull() = (this as? ProcessState.Success<T>)?.data

inline fun <T> ProcessResult<T>.onFailure(action: (exception: Throwable) -> Unit): ProcessResult<T> {
contract {
callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
if (this is ProcessState.Failure) action(exception)
return this
}

inline fun <T> ProcessResult<T>.onSuccess(action: (value: T) -> Unit): ProcessResult<T> {
contract {
callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
if (this is ProcessState.Success<T>) action(data)
return this
}

fun ProcessState<*>.isLoading(): Boolean {
contract {
returns(true) implies (this@isLoading is ProcessState.Loading)
}
return this is ProcessState.Loading
}

fun <T> ProcessState<T>.isSuccess(): Boolean {
contract {
returns(true) implies (this@isSuccess is ProcessState.Success<T>)
}
return this is ProcessState.Success<T>
}

fun ProcessState<*>.isFailure(): Boolean {
contract {
returns(true) implies (this@isFailure is ProcessState.Failure)
}
return this is ProcessState.Failure
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.xmartlabs.gong.device.di

import com.xmartlabs.gong.device.logger.NavigationLogger
import org.koin.dsl.module

/**
* Created by mirland on 25/05/20.
*/
Expand All @@ -17,6 +14,5 @@ object DiAppModules {
ViewModelDiModule.viewModels,
)

fun provideModules(navigationLogger: NavigationLogger) =
modules + module { single { navigationLogger } }
fun provideModules() = modules
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.koin.dsl.module
*/
object ViewModelDiModule {
val viewModels = module {
viewModel { SignInScreenViewModel(get(), get()) }
viewModel { SignInScreenViewModel(get()) }
viewModel { SplashScreenViewModel(get()) }
viewModel { WelcomeScreenViewModel(get(), get()) }
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ class GetSessionTypeUseCase(
dispatcher: CoroutineDispatcher
) : CoroutineUseCase<Unit, SessionType>(dispatcher) {
override suspend fun execute(params: Unit): SessionType =
sessionRepository.isUserLogged()
.let { isUserLogged -> if (isUserLogged) SessionType.LOGGED else SessionType.NOT_LOGGED }
if (sessionRepository.isUserLogged()) SessionType.LOGGED else SessionType.NOT_LOGGED
}

enum class SessionType {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.xmartlabs.gong.domain.usecase.common

import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import com.xmartlabs.gong.device.common.ProcessResult
import com.xmartlabs.gong.device.common.ProcessState
import com.xmartlabs.gong.device.common.onFailure
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
Expand All @@ -15,26 +15,14 @@ import timber.log.Timber
* https://github.com/google/iosched/blob/ee7f31c16f2d1e1f20f479b384dccb205e3e9584/shared/src/main/java/com/google/samples/apps/iosched/shared/domain/CoroutineUseCase.kt
*/
abstract class CoroutineUseCase<in P, R>(private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default) {
fun invokeAsLiveData(params: P): LiveData<ProcessState<R>> = invokeAsFlow(params).asLiveData()
fun invokeAsFlow(params: P): Flow<ProcessState<R>> = flow { emit(invoke(params)) }
.mapResultToProcessState()

fun invokeAsFlow(params: P): Flow<ProcessState<R>> = flow {
emit(ProcessState.Loading)
emit(ProcessState.Finish(invoke(params)))
}

suspend operator fun invoke(params: P): Result<R> = try {
// Moving all use case's executions to the injected dispatcher
// In production code, this is usually the Default dispatcher (background thread)
// In tests, this becomes a TestCoroutineDispatcher
withContext(coroutineDispatcher) {
execute(params).let {
Result.success(it)
suspend operator fun invoke(params: P): ProcessResult<R> =
withContext(coroutineDispatcher) {
ProcessResult.runCatching { execute(params) }
.onFailure { Timber.w(it) }
}
}
} catch (throwable: Throwable) {
Timber.w(throwable)
Result.failure(throwable)
}

/**
* Override this to set the code to be executed.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
package com.xmartlabs.gong.domain.usecase.common

import androidx.lifecycle.asLiveData
import com.xmartlabs.gong.device.common.ProcessState
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import timber.log.Timber

/**
* Created by mirland on 25/04/20.
*/
abstract class FlowCoroutineUseCase<in P, R>(private val coroutineDispatcher: CoroutineDispatcher) {
@OptIn(ExperimentalCoroutinesApi::class)
operator fun invoke(params: P): Flow<Result<R>> = execute(params)
.map { value -> Result.success(value) }
.catch { error ->
Timber.w(error)
emit(Result.failure(error))
operator fun invoke(params: P): Flow<ProcessState<R>> = execute(params)
.mapToProcessState()
.onEach { processState ->
if (processState is ProcessState.Failure) {
Timber.w(processState.exception)
}
}
.flowOn(coroutineDispatcher)

fun invokeAsLiveData(params: P) = invoke(params)
.asLiveData()

/**
* Override this to set the code to be executed.
*/
Expand Down
Loading

0 comments on commit b0b617e

Please sign in to comment.