Skip to content

Commit

Permalink
Improve error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmedeltaher committed Jan 18, 2020
1 parent 3691625 commit 2077efc
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 29 deletions.
6 changes: 2 additions & 4 deletions app/src/main/java/com/task/data/Resource.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.task.data

import com.task.data.error.Error

// A generic class that contains data and status about loading this data.
sealed class Resource<T>(
val data: T? = null,
val error: Error? = null
val errorCode: Int? = null
) {
class Success<T>(data: T) : Resource<T>(data)
class Loading<T>(data: T? = null) : Resource<T>(data)
class DataError<T>(error: Error) : Resource<T>(null, error)
class DataError<T>(errorCode: Int) : Resource<T>(null, errorCode)
}
12 changes: 5 additions & 7 deletions app/src/main/java/com/task/data/remote/RemoteRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package com.task.data.remote

import com.task.App
import com.task.data.Resource
import com.task.data.error.Error
import com.task.data.error.Error.Companion.NETWORK_ERROR
import com.task.data.error.Error.Companion.NO_INTERNET_CONNECTION
import com.task.data.error.factory.ErrorFactoryImpl
import com.task.data.remote.dto.NewsModel
import com.task.data.remote.service.NewsService
import com.task.utils.Constants
Expand All @@ -20,7 +18,7 @@ import javax.inject.Inject
*/

class RemoteRepository @Inject
constructor(private val serviceGenerator: ServiceGenerator, private val errors: ErrorFactoryImpl) : RemoteSource {
constructor(private val serviceGenerator: ServiceGenerator) : RemoteSource {

override suspend fun requestNews(): Resource<NewsModel> {
val newsService = serviceGenerator.createService(NewsService::class.java, Constants.BASE_URL)
Expand All @@ -29,25 +27,25 @@ constructor(private val serviceGenerator: ServiceGenerator, private val errors:
Resource.Success(data = response)
}
else -> {
Resource.DataError(error = response as Error)
Resource.DataError(errorCode = response as Int)
}
}
}

private suspend fun processCall(responseCall: suspend () -> Response<*>): Any? {
if (!isConnected(App.context)) {
return errors.getError(NO_INTERNET_CONNECTION)
return NO_INTERNET_CONNECTION
}
return try {
val response = responseCall.invoke()
val responseCode = response.code()
if (response.isSuccessful) {
response.body()
} else {
errors.getError(responseCode)
responseCode
}
} catch (e: IOException) {
errors.getError(NETWORK_ERROR)
NETWORK_ERROR
}
}
}
8 changes: 4 additions & 4 deletions app/src/main/java/com/task/di/ErrorModule.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package com.task.di

import com.task.data.error.factory.ErrorFactory
import com.task.data.error.factory.ErrorFactoryImpl
import com.task.data.error.mapper.ErrorMapper
import com.task.data.error.mapper.ErrorMapperInterface
import com.task.usecase.errors.ErrorFactory
import com.task.usecase.errors.ErrorManager
import dagger.Binds
import dagger.Module
import javax.inject.Singleton

// Tells Dagger this is a Dagger module
// with @Module we Telling Dagger that, this is a Dagger module
@Module
abstract class ErrorModule {
@Binds
@Singleton
abstract fun provideErrorFactoryImpl(errorFactoryImpl: ErrorFactoryImpl): ErrorFactory
abstract fun provideErrorFactoryImpl(errorManager: ErrorManager): ErrorFactory

@Binds
@Singleton
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/java/com/task/ui/base/BaseViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package com.task.ui.base

import androidx.lifecycle.ViewModel
import com.task.usecase.errors.ErrorManager


/**
* Created by AhmedEltaher on 5/12/2016
*/


abstract class BaseViewModel: ViewModel()
abstract class BaseViewModel : ViewModel() {
/**Inject Singlton ErrorManager
* Use this errorManager to get the Errors
*/
abstract val errorManager: ErrorManager

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.task.ui.component.details

import androidx.lifecycle.MutableLiveData
import com.task.data.error.mapper.ErrorMapper
import com.task.data.remote.dto.NewsItem
import com.task.ui.base.BaseViewModel
import com.task.usecase.errors.ErrorManager
import javax.inject.Inject

/**
* Created by AhmedEltaher on 11/12/16.
*/

class DetailsViewModel @Inject
constructor() : BaseViewModel(){
constructor() : BaseViewModel() {
var newsItem: MutableLiveData<NewsItem> = MutableLiveData()
override val errorManager: ErrorManager
get() = ErrorManager(ErrorMapper())
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import com.task.utils.*
import kotlinx.android.synthetic.main.home_activity.*
import kotlinx.android.synthetic.main.toolbar.*
import org.jetbrains.anko.intentFor
import org.jetbrains.anko.toast
import javax.inject.Inject

/**
Expand Down Expand Up @@ -82,6 +81,10 @@ class NewsListActivity : BaseActivity() {
rl_news_list.setupSnackbar(this, event, Snackbar.LENGTH_LONG)
}

private fun observeToast(event: LiveData<Event<Any>>) {
rl_news_list.showToast(this, event, Snackbar.LENGTH_LONG)
}

private fun showSearchError() {
newsListViewModel.showSnackbarMessage(R.string.search_error)
}
Expand Down Expand Up @@ -116,7 +119,7 @@ class NewsListActivity : BaseActivity() {
is Resource.Success -> newsModel.data?.let { bindListData(newsModel = it) }
is Resource.DataError -> {
showDataView(false)
toast("${newsModel.error?.description}")
newsModel.errorCode?.let { newsListViewModel.showToastMessage(it) }
}
}

Expand All @@ -128,5 +131,7 @@ class NewsListActivity : BaseActivity() {
observe(newsListViewModel.noSearchFound, ::noSearchResult)
observeEvent(newsListViewModel.openNewsDetails, ::navigateToDetailsScreen)
observeSnackBarMessages(newsListViewModel.showSnackBar)
observeToast(newsListViewModel.showToast)

}
}
19 changes: 14 additions & 5 deletions app/src/main/java/com/task/ui/component/news/NewsListViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import androidx.annotation.StringRes
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.task.data.Resource
import com.task.data.error.mapper.ErrorMapper
import com.task.data.remote.dto.NewsItem
import com.task.data.remote.dto.NewsModel
import com.task.ui.base.BaseViewModel
import com.task.usecase.NewsUseCase
import com.task.usecase.errors.ErrorManager
import com.task.utils.Event
import javax.inject.Inject

Expand All @@ -18,6 +20,9 @@ import javax.inject.Inject
class NewsListViewModel @Inject
constructor(private val newsDataUseCase: NewsUseCase) : BaseViewModel() {

override val errorManager: ErrorManager
get() = ErrorManager(ErrorMapper())

/**
* Data --> LiveData, Exposed as LiveData, Locally in viewModel as MutableLiveData
*/
Expand All @@ -30,16 +35,19 @@ constructor(private val newsDataUseCase: NewsUseCase) : BaseViewModel() {
val noSearchFound: LiveData<Unit> get() = noSearchFoundPrivate

/**
* UI actions as event, user action is single one time event, Shouldn't be multiple time consumption
* UI actions as event, user action is single one time event, Shouldn't be multiple time consumption
*/
private val openNewsDetailsPrivate = MutableLiveData<Event<NewsItem>>()
val openNewsDetails: LiveData<Event<NewsItem>> get() = openNewsDetailsPrivate

/**
* Error handling as UI
*/
private val showSnackBarPrivate = MutableLiveData<Event<Int>>()
val showSnackBar: LiveData<Event<Int>> get() = showSnackBarPrivate

private val showToastPrivate = MutableLiveData<Event<Int>>()
val showToast: LiveData<Event<Int>> get() = showToastPrivate
private val showToastPrivate = MutableLiveData<Event<Any>>()
val showToast: LiveData<Event<Any>> get() = showToastPrivate


fun getNews() {
Expand All @@ -54,8 +62,9 @@ constructor(private val newsDataUseCase: NewsUseCase) : BaseViewModel() {
showSnackBarPrivate.value = Event(message)
}

fun showToastMessage(@StringRes message: Int) {
showToastPrivate.value = Event(message)
fun showToastMessage(errorCode: Int) {
val error = errorManager.getError(errorCode)
showToastPrivate.value = Event(error.description)
}

fun onSearchClick(newsTitle: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.task.ui.component.splash

import com.task.data.error.mapper.ErrorMapper
import com.task.ui.base.BaseViewModel
import com.task.usecase.errors.ErrorManager
import javax.inject.Inject

/**
* Created by AhmedEltaher on 5/12/2016
*/

class SplashViewModel @Inject
constructor() : BaseViewModel()
constructor() : BaseViewModel(){
override val errorManager: ErrorManager
get() = ErrorManager(ErrorMapper())
}
4 changes: 3 additions & 1 deletion app/src/main/java/com/task/usecase/NewsUseCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import androidx.lifecycle.MutableLiveData
import com.task.data.DataSource
import com.task.data.Resource
import com.task.data.error.Error
import com.task.data.error.Error.Companion.NETWORK_ERROR
import com.task.data.remote.dto.NewsItem
import com.task.data.remote.dto.NewsModel
import com.task.usecase.errors.ErrorManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -30,7 +32,7 @@ constructor(private val dataRepository: DataSource, override val coroutineContex
serviceResponse = dataRepository.requestNews()
newsMutableLiveData.postValue(serviceResponse)
} catch (e: Exception) {
newsMutableLiveData.postValue(Resource.DataError(Error(e)))
newsMutableLiveData.postValue(Resource.DataError(NETWORK_ERROR))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.task.data.error.factory
package com.task.usecase.errors

import com.task.data.error.Error

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.task.data.error.factory
package com.task.usecase.errors

import com.task.data.error.Error
import com.task.data.error.mapper.ErrorMapper
Expand All @@ -8,7 +8,7 @@ import javax.inject.Inject
* Created by AhmedEltaher on 5/12/2016
*/

class ErrorFactoryImpl @Inject constructor(private val errorMapper: ErrorMapper) : ErrorFactory {
class ErrorManager @Inject constructor(private val errorMapper: ErrorMapper) : ErrorFactory {
override fun getError(errorCode: Int): Error {
return Error(code = errorCode, description = errorMapper.errorsMap.getValue(errorCode))
}
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/java/com/task/utils/ViewExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Service
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.AppCompatEditText
import androidx.appcompat.widget.AppCompatTextView
Expand Down Expand Up @@ -73,6 +74,24 @@ fun View.setupSnackbar(
})
}

fun View.showToast(
lifecycleOwner: LifecycleOwner,
snackbarEvent: LiveData<Event<Any>>,
timeLength: Int
) {

snackbarEvent.observe(lifecycleOwner, Observer { event ->
event.getContentIfNotHandled()?.let {
when (it) {
is String -> Toast.makeText(context, it, timeLength).show()
is Int -> Toast.makeText(context, context.getString(it), timeLength).show()
else -> {
}
}
}
})
}

fun ImageView.loadImage(@DrawableRes resId: Int) = Picasso.get().load(resId).into(this)
fun ImageView.loadImage(url: String) = Picasso.get().load(url).placeholder(R.drawable.news).error(R.drawable.news).into(this)

Expand Down

0 comments on commit 2077efc

Please sign in to comment.