Basic error handling for network requests

This commit is contained in:
Phillip Thelen 2022-06-03 14:58:31 +02:00
parent 0a155a6840
commit 774a5c9c82
13 changed files with 84 additions and 15 deletions

View file

@ -37,6 +37,7 @@
</activity>
<activity android:name=".ui.activities.TaskListActivity" />
<activity android:name=".ui.activities.AvatarActivity" />
<activity android:name="androidx.wear.activity.ConfirmationActivity" />
</application>
</manifest>

View file

@ -0,0 +1,3 @@
package com.habitrpg.wearos.habitica.models
data class DisplayedError(val title: String)

View file

@ -7,8 +7,8 @@ import com.habitrpg.wearos.habitica.ui.viewmodels.AvatarViewModel
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class AvatarActivity: BaseActivity<ActivityAvatarBinding>() {
val viewModel: AvatarViewModel by viewModels()
class AvatarActivity: BaseActivity<ActivityAvatarBinding, AvatarViewModel>() {
override val viewModel: AvatarViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityAvatarBinding.inflate(layoutInflater)

View file

@ -1,14 +1,27 @@
package com.habitrpg.wearos.habitica.ui.activities
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.viewbinding.ViewBinding
import androidx.wear.activity.ConfirmationActivity
import com.habitrpg.wearos.habitica.ui.viewmodels.BaseViewModel
open class BaseActivity<B: ViewBinding> : ComponentActivity() {
abstract class BaseActivity<B: ViewBinding, VM: BaseViewModel> : ComponentActivity() {
protected lateinit var binding: B
abstract val viewModel: VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
viewModel.errorValues.observe(this) {
val intent = Intent(this, ConfirmationActivity::class.java).apply {
putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, ConfirmationActivity.FAILURE_ANIMATION)
putExtra(ConfirmationActivity.EXTRA_MESSAGE, it.title)
putExtra(ConfirmationActivity.EXTRA_ANIMATION_DURATION_MILLIS, 3000)
}
startActivity(intent)
}
}
}

View file

@ -26,8 +26,8 @@ data class MenuItem(
)
@AndroidEntryPoint
class MainActivity : BaseActivity<ActivityMainBinding>() {
val viewModel: MainViewModel by viewModels()
class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {
override val viewModel: MainViewModel by viewModels()
private val adapter = HubAdapter()

View file

@ -11,9 +11,9 @@ import com.habitrpg.wearos.habitica.ui.viewmodels.TaskListViewModel
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class TaskListActivity: BaseActivity<ActivityTasklistBinding>() {
class TaskListActivity: BaseActivity<ActivityTasklistBinding, TaskListViewModel>() {
private val adapter = TaskListAdapter()
private val viewModel: TaskListViewModel by viewModels()
override val viewModel: TaskListViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityTasklistBinding.inflate(layoutInflater)

View file

@ -5,17 +5,24 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
import com.habitrpg.wearos.habitica.models.User
import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class AvatarViewModel @Inject constructor(userRepository: UserRepository) : BaseViewModel(userRepository) {
class AvatarViewModel @Inject constructor(
userRepository: UserRepository,
exceptionBuilder: ExceptionHandlerBuilder
) : BaseViewModel(
userRepository,
exceptionBuilder
) {
val _user = MutableLiveData<User>()
val user: LiveData<User> = _user
init {
viewModelScope.launch {
viewModelScope.launch(exceptionBuilder.userFacing(this)) {
_user.value = userRepository.retrieveUser()
}
}

View file

@ -1,6 +1,15 @@
package com.habitrpg.wearos.habitica.ui.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
import com.habitrpg.wearos.habitica.models.DisplayedError
import com.habitrpg.wearos.habitica.util.ErrorPresenter
import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder
open class BaseViewModel(val userRepository: UserRepository): ViewModel()
open class BaseViewModel(
val userRepository: UserRepository,
val exceptionBuilder: ExceptionHandlerBuilder
): ViewModel(), ErrorPresenter {
override val errorValues = MutableLiveData<DisplayedError>()
}

View file

@ -5,17 +5,21 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
import com.habitrpg.wearos.habitica.models.User
import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class MainViewModel @Inject constructor(userRepository: UserRepository) : BaseViewModel(userRepository) {
class MainViewModel @Inject constructor(
userRepository: UserRepository,
exceptionBuilder: ExceptionHandlerBuilder
) : BaseViewModel(userRepository, exceptionBuilder) {
val _user = MutableLiveData<User>()
val user: LiveData<User> = _user
init {
viewModelScope.launch {
viewModelScope.launch(exceptionBuilder.userFacing(this)) {
_user.value = userRepository.retrieveUser()
}
}

View file

@ -8,6 +8,7 @@ import com.habitrpg.common.habitica.models.tasks.TaskType
import com.habitrpg.wearos.habitica.data.repositories.TaskRepository
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
import com.habitrpg.wearos.habitica.models.tasks.Task
import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -16,15 +17,16 @@ import javax.inject.Inject
class TaskListViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val taskRepository: TaskRepository,
userRepository: UserRepository
) : BaseViewModel(userRepository) {
userRepository: UserRepository,
exceptionBuilder: ExceptionHandlerBuilder
) : BaseViewModel(userRepository, exceptionBuilder) {
val _tasks = MutableLiveData<List<Task>>()
val tasks: LiveData<List<Task>> = _tasks
val taskType = TaskType.from(savedStateHandle.get<String>("task_type"))
init {
viewModelScope.launch {
viewModelScope.launch(exceptionBuilder.userFacing(this)) {
_tasks.value = taskRepository.retrieveTasks()?.tasks?.values?.filter {
it.type == taskType
}?.toList()

View file

@ -0,0 +1,8 @@
package com.habitrpg.wearos.habitica.util
import androidx.lifecycle.MutableLiveData
import com.habitrpg.wearos.habitica.models.DisplayedError
interface ErrorPresenter {
val errorValues: MutableLiveData<DisplayedError>
}

View file

@ -0,0 +1,22 @@
package com.habitrpg.wearos.habitica.util
import android.util.Log
import com.habitrpg.wearos.habitica.models.DisplayedError
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.CoroutineExceptionHandler
import javax.inject.Inject
@ViewModelScoped
class ExceptionHandlerBuilder @Inject constructor() {
fun silent(): CoroutineExceptionHandler {
return CoroutineExceptionHandler { context, throwable ->
Log.e(context.toString(), "Error: ${throwable.cause}", throwable)
}
}
fun userFacing(errorPresenter: ErrorPresenter): CoroutineExceptionHandler {
return CoroutineExceptionHandler { _, throwable ->
errorPresenter.errorValues.value = throwable.message?.let { DisplayedError(it) }
}
}
}