Skip to content

Feature/corutines network layer #36

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

Merged
merged 4 commits into from
Dec 18, 2020
Merged
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
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 21 additions & 14 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ android {
ktlint
}

kotlinOptions {
freeCompilerArgs = ["-Xallow-result-return-type"]
}

task ktlint(type: JavaExec, group: "verification") {
description = "Check Kotlin code style."
classpath = configurations.ktlint
Expand Down Expand Up @@ -117,37 +121,40 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation "androidx.preference:preference-ktx:1.1.0"
implementation 'com.google.android.material:material:1.1.0'
implementation "androidx.preference:preference-ktx:1.1.1"
implementation 'com.google.android.material:material:1.2.1'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.28.2'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.3.1'
//---- ANDROID ARCH ROOM ----
implementation 'android.arch.persistence.room:runtime:1.1.1'
kapt "android.arch.persistence.room:compiler:1.1.1"
//---- ANDROID ARCH LIFECYCLE ----
implementation 'android.arch.lifecycle:common-java8:1.1.1'
kapt "android.arch.lifecycle:compiler:1.1.1"
implementation 'android.arch.lifecycle:runtime:1.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

//---- GOOGLE JSON SERIALIZER/DESERIALIZER ----
implementation 'com.google.code.gson:gson:2.8.5'
//---- MixPanel ----
implementation 'com.mixpanel.android:mixpanel-android:5.6.1'
//---- Firebase ----
implementation 'com.google.firebase:firebase-core:17.2.2'
implementation 'com.google.firebase:firebase-analytics:17.2.1'
implementation 'com.google.firebase:firebase-core:18.0.0'
implementation 'com.google.firebase:firebase-analytics:18.0.0'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.61'
//---- Image ----
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.rootstrap.android.network.managers

import com.rootstrap.android.network.models.User
import com.rootstrap.android.network.models.UserSerializer
import com.rootstrap.android.util.extensions.Data

interface IUserManager {
suspend fun signUp(user: User): Result<Data<UserSerializer>>
suspend fun signIn(user: User): Result<Data<UserSerializer>>
suspend fun signOut(): Result<Data<Void>>
}
Original file line number Diff line number Diff line change
@@ -1,75 +1,31 @@
package com.rootstrap.android.network.managers

import androidx.annotation.RestrictTo
import com.rootstrap.android.bus
import com.rootstrap.android.network.models.User
import com.rootstrap.android.network.models.UserSerializer
import com.rootstrap.android.network.providers.ServiceProvider
import com.rootstrap.android.network.services.ApiService
import com.rootstrap.android.util.extensions.ActionCallback
import retrofit2.Response
import com.rootstrap.android.util.extensions.Data

/**
* Singleton Object
* */
object UserManager {
class UserManager : IUserManager {

private var service = ServiceProvider.create(ApiService::class.java)

fun signUp(user: User) {
val userSerializer = UserSerializer(user)
val signUp = service.signUp(userSerializer)
signUp.enqueue(UserCallback())
}
override suspend fun signUp(user: User): Result<Data<UserSerializer>> =
ActionCallback.call(service.signUp(UserSerializer(user)))

fun signIn(user: User) {
val userSerializer = UserSerializer(user)
val signIn = service.signIn(userSerializer)
signIn.enqueue(LogInCallback())
}
override suspend fun signIn(user: User): Result<Data<UserSerializer>> =
ActionCallback.call(service.signIn(UserSerializer(user)))

fun signOut() {
val signOut = service.signOut()
signOut.enqueue(LogOutCallback())
}

private class UserCallback : ActionCallback<UserSerializer>() {
override fun responseAction(response: Response<UserSerializer>) {
super.responseAction(response)
response.body()?.let {
SessionManager.user = it.user
}
bus.post(UserCreatedSuccessfullyEvent())
}
}

private class LogInCallback : ActionCallback<UserSerializer>() {

override fun responseAction(response: Response<UserSerializer>) {
super.responseAction(response)
response.body()?.let {
SessionManager.signIn(it.user)
}
bus.post(SignInSuccessfullyEvent())
}
}

private class LogOutCallback : ActionCallback<Void>() {

override fun responseAction(response: Response<Void>) {
super.responseAction(response)

SessionManager.signOut()
bus.post(SignedOutSuccessfullyEvent())
}
}
override suspend fun signOut(): Result<Data<Void>> =
ActionCallback.call(service.signOut())

@RestrictTo(RestrictTo.Scope.TESTS)
open fun reloadService(url: String) {
fun reloadService(url: String) {
service = ServiceProvider.create(ApiService::class.java, url)
}

class UserCreatedSuccessfullyEvent
class SignInSuccessfullyEvent
class SignedOutSuccessfullyEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ data class User(
@Json(name = "username") val username: String = ""
)

data class UserSerializer(val user: User)
data class UserSerializer(@Json(name = "user") val user: User)
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class ProfileActivity : BaseActivity(), ProfileView {
override fun updateState() {
when (viewModel.state) {
ProfileState.signOutFailure -> showError(viewModel.error)
ProfileState.signedOutSuccessfully -> goToFirstScreen()
ProfileState.signOutSuccessfully -> goToFirstScreen()
else -> {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,53 @@ package com.rootstrap.android.ui.activity.main

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.rootstrap.android.network.managers.IUserManager
import com.rootstrap.android.network.managers.UserManager
import com.rootstrap.android.ui.base.BaseViewModel
import com.rootstrap.android.util.NetworkState
import com.rootstrap.android.util.ViewModelListener
import com.rootstrap.android.util.extensions.ErrorEvent
import com.rootstrap.android.util.extensions.FailureEvent
import com.squareup.otto.Subscribe
import com.rootstrap.android.util.extensions.ApiErrorType
import com.rootstrap.android.util.extensions.ApiException
import kotlinx.coroutines.launch

open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) {

private val manager = UserManager
private val manager: IUserManager = UserManager()

fun signOut() {
networkState = NetworkState.loading
manager.signOut()
}

var state: ProfileState = ProfileState.none
set(value) {
field = value
listener?.updateState()
viewModelScope.launch {
val result = manager.signOut()
if (result.isSuccess) {
networkState = NetworkState.idle
state = ProfileState.signOutSuccessfully
} else {
manageError(result.exceptionOrNull())
}
}

@Subscribe
fun signedOutSuccessfully(event: UserManager.SignedOutSuccessfullyEvent) {
networkState = NetworkState.idle
state = ProfileState.signedOutSuccessfully
}

@Subscribe
fun signOutError(event: ErrorEvent) {
error = event.error
networkState = NetworkState.idle
networkState = NetworkState.error
}
private fun manageError(exception: Throwable?) {
error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) {
exception.message
} else null

@Subscribe
fun signOutFailure(event: FailureEvent) {
error = null
networkState = NetworkState.idle
networkState = NetworkState.error
state = ProfileState.signOutFailure
}

var state: ProfileState = ProfileState.none
set(value) {
field = value
listener?.updateState()
}
}

enum class ProfileState {
signOutFailure,
signedOutSuccessfully,
signOutSuccessfully,
none,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ class SignInActivity : PermissionActivity(), AuthView {
private val viewModelListener = object : ViewModelListener {
override fun updateState() {
when (viewModel.state) {
SignInState.signedInFailure -> showError(viewModel.error)
SignInState.signedInSuccess -> showProfile()
SignInState.signInFailure -> showError(viewModel.error)
SignInState.signInSuccessfully -> showProfile()
else -> {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ package com.rootstrap.android.ui.activity.main

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.rootstrap.android.network.managers.IUserManager
import com.rootstrap.android.network.managers.SessionManager
import com.rootstrap.android.network.managers.UserManager
import com.rootstrap.android.network.models.User
import com.rootstrap.android.ui.base.BaseViewModel
import com.rootstrap.android.util.NetworkState
import com.rootstrap.android.util.ViewModelListener
import com.rootstrap.android.util.extensions.ErrorEvent
import com.rootstrap.android.util.extensions.FailureEvent
import com.squareup.otto.Subscribe
import com.rootstrap.android.util.extensions.ApiErrorType
import com.rootstrap.android.util.extensions.ApiException
import kotlinx.coroutines.launch

open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) {

private val manager = UserManager
private val manager: IUserManager = UserManager()

var state: SignInState = SignInState.none
set(value) {
Expand All @@ -23,33 +26,35 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel

fun signIn(user: User) {
networkState = NetworkState.loading
manager.signIn(user)
}
viewModelScope.launch {
val result = manager.signIn(user = user)
if (result.isSuccess) {
result.getOrNull()?.value?.user?.let { user ->
SessionManager.signIn(user)
}

@Subscribe
fun signedInSuccessfully(event: UserManager.SignInSuccessfullyEvent) {
networkState = NetworkState.idle
state = SignInState.signedInSuccess
networkState = NetworkState.idle
state = SignInState.signInSuccessfully
} else {
manageError(result.exceptionOrNull())
}
}
}

@Subscribe
fun signedInError(event: ErrorEvent) {
error = event.error
networkState = NetworkState.idle
networkState = NetworkState.error
}
private fun manageError(exception: Throwable?) {
error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) {
exception.message
} else null

@Subscribe
fun signedInFailure(event: FailureEvent) {
error = null
networkState = NetworkState.idle
state = SignInState.signedInFailure
networkState = NetworkState.error
state = SignInState.signInFailure
}
}

enum class SignInState {
signedInFailure,
signedInSuccess,
signInFailure,
signInSuccessfully,
none,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ class SignUpActivity : BaseActivity(), AuthView {
private val viewModelListener = object : ViewModelListener {
override fun updateState() {
when (viewModel.state) {
SignUpState.signedUpFailure -> showError(viewModel.error)
SignUpState.signedUpSuccess -> showProfile()
SignUpState.signUpFailure -> showError(viewModel.error)
SignUpState.signUpSuccessfully -> showProfile()
else -> {
}
}
Expand Down
Loading