Skip to content

Commit

Permalink
(#2) Add error exception handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
myung jun Hyun committed Nov 25, 2019
1 parent 151ee1f commit e4b8eca
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 13 deletions.
10 changes: 10 additions & 0 deletions app/src/main/java/com/mashup/api/ErrorResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.mashup.api

data class ErrorResponse(
val code: String,
val message: String?
) {
fun hasErrors(): Boolean {
return !message.isNullOrEmpty()
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/com/mashup/api/notice/NoticeService.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mashup.api.notice

import com.mashup.api.notice.request.AttendanceUpdateRequest
import com.mashup.repository.notice.remote.response.NoticeListResponse
import com.mashup.api.notice.response.NoticeListResponse
import io.reactivex.Completable
import io.reactivex.Flowable
import retrofit2.http.*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.mashup.repository.notice.remote.response
package com.mashup.api.notice.response

import com.mashup.model.Notice

Expand Down
16 changes: 12 additions & 4 deletions app/src/main/java/com/mashup/app/login/LoginViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mashup.app.login

import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
Expand All @@ -20,8 +21,8 @@ class LoginViewModel(
private val _isLoginSuccess = MutableLiveData<Event<Boolean>>()
val isLoginSuccess: LiveData<Event<Boolean>> = _isLoginSuccess

private val _snackbarText = MutableLiveData<Event<Int>>()
val snackbarText: LiveData<Event<Int>> = _snackbarText
private val _snackbarText = MutableLiveData<Event<Any>>()
val snackbarText: LiveData<Event<Any>> = _snackbarText

val email = MutableLiveData<String>()
val password = MutableLiveData<String>()
Expand All @@ -35,8 +36,15 @@ class LoginViewModel(
repository.getAuthToken(AuthTokenRequest("", currentEmail, currentPassword))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ _isLoginSuccess.value = Event(true) },
{ _snackbarText.value = Event(R.string.login_error_message_fail_login) })
.subscribe(
{ _isLoginSuccess.value = Event(true) },
{
if (it.message.isNullOrEmpty()) {
_snackbarText.value = Event(R.string.error_message_unknown)
} else {
_snackbarText.value = Event(it.message!!)
}
})
)
} else {
_snackbarText.value = Event(R.string.login_error_message_empty_text)
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/com/mashup/app/notices/NoticesFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.snackbar.Snackbar
import com.mashup.app.attendees.AttendeesActivity
import com.mashup.app.attendees.AttendeesFragment
import com.mashup.app.login.LoginActivity
import com.mashup.app.noticedetail.NoticeDetailActivity
import com.mashup.app.noticedetail.NoticeDetailFragment
import com.mashup.databinding.NoticesFragmentBinding
import com.mashup.util.EventObserver
import com.mashup.util.setupSnackbar
import org.koin.androidx.viewmodel.ext.android.viewModel

class NoticesFragment : Fragment() {
Expand Down Expand Up @@ -40,6 +42,7 @@ class NoticesFragment : Fragment() {
super.onActivityCreated(savedInstanceState)
viewDataBinding.setLifecycleOwner(this.viewLifecycleOwner)
setupListAdapter()
setupSnackbar()
setupEventObserver()
}

Expand All @@ -51,6 +54,10 @@ class NoticesFragment : Fragment() {
}
}

private fun setupSnackbar() {
view?.setupSnackbar(this, viewModel.snackbarText, Snackbar.LENGTH_SHORT)
}

private fun setupEventObserver() {
viewModel.itemChangedEvent.observe(this, EventObserver { position ->
listAdapter.notifyItemChanged(position)
Expand Down
14 changes: 13 additions & 1 deletion app/src/main/java/com/mashup/app/notices/NoticesViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.mashup.app.notices
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.mashup.R
import com.mashup.model.*
import com.mashup.repository.notice.NoticesRepository
import com.mashup.repository.user.UserRepository
Expand Down Expand Up @@ -33,6 +34,9 @@ class NoticesViewModel(
private val _showAttendeesEvent = MutableLiveData<Event<List<NoticeAttendance>>>()
val showAttendeesEvent: LiveData<Event<List<NoticeAttendance>>> = _showAttendeesEvent

private val _snackbarText = MutableLiveData<Event<Any>>()
val snackbarText: LiveData<Event<Any>> = _snackbarText

private val compositeDisposable = CompositeDisposable()

private lateinit var authToken: AuthToken
Expand Down Expand Up @@ -75,7 +79,15 @@ class NoticesViewModel(
noticesRepository.updateNoticeAttendance(authToken.key, noticeId, voteStatus)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ updateList(noticeId, voteStatus) }, { updateList(noticeId, voteStatus) })//TODO 예외 처리로 돌려얗함
.subscribe(
{ updateList(noticeId, voteStatus) },
{
if (it.message.isNullOrEmpty()) {
_snackbarText.value = Event(R.string.error_message_unknown)
} else {
_snackbarText.value = Event(it.message!!)
}
})//TODO 예외 처리로 돌려얗함
)
}

Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/mashup/di/NetworkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
import com.mashup.util.ErrorInterceptor



private const val BASE_URL = BuildConfig.BASE_URL
private const val TIMEOUT_CONNECT = 15L
Expand All @@ -24,6 +27,7 @@ val NetworkModule = module {
cache(get())
connectTimeout(TIMEOUT_CONNECT, TimeUnit.SECONDS)
retryOnConnectionFailure(true)
addInterceptor(ErrorInterceptor())
addInterceptor(HttpLoggingInterceptor().apply {
if (BuildConfig.DEBUG) {
level = HttpLoggingInterceptor.Level.BODY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.mashup.repository.notice.remote
import com.mashup.api.notice.NoticeService
import com.mashup.api.notice.request.AttendanceUpdateRequest
import com.mashup.model.VoteStatus
import com.mashup.repository.notice.remote.response.NoticeListResponse
import com.mashup.api.notice.response.NoticeListResponse
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.schedulers.Schedulers
Expand Down
55 changes: 55 additions & 0 deletions app/src/main/java/com/mashup/util/ErrorInterCepter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.mashup.util

import com.google.gson.Gson
import com.google.gson.JsonParseException
import com.mashup.api.ErrorResponse
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
import java.nio.charset.Charset

private val UTF8 = Charset.forName("UTF-8")

class ErrorInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val response = chain.proceed(originalRequest)

return if (response.code() >= 400) {
throwError(response)
response
} else {
response
}
}

@Throws(IOException::class)
private fun throwError(response: Response) {
val responseBody = response.body()
if (responseBody != null) {
val source = responseBody.source()
source.request(java.lang.Long.MAX_VALUE) // Buffer the entire body.
val buffer = source.buffer()

var charset = UTF8
val contentType = responseBody.contentType()
if (contentType != null) {
charset = contentType.charset(UTF8)
}

if (responseBody.contentLength() != 0L) {
val responseJSON = buffer.clone().readString(charset)
val message: String?
try {
message = Gson().fromJson(responseJSON, ErrorResponse::class.java).message
throw Throwable(message)
} catch (error: JsonParseException) {
throw Throwable("")
}
}
} else {
throw Throwable("")
}
}
}
13 changes: 9 additions & 4 deletions app/src/main/java/com/mashup/util/ViewExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.google.android.material.snackbar.Snackbar
import java.lang.Exception

/**
* Transforms static java function Snackbar.make() to an extension function on View.
Expand All @@ -17,14 +18,18 @@ fun View.showSnackbar(snackbarText: String, timeLength: Int) {
* Triggers a snackbar message when the value contained by snackbarTaskMessageLiveEvent is modified.
*/
fun View.setupSnackbar(
lifecycleOwner: LifecycleOwner,
snackbarEvent: LiveData<Event<Int>>,
timeLength: Int
lifecycleOwner: LifecycleOwner,
snackbarEvent: LiveData<Event<Any>>,
timeLength: Int
) {

snackbarEvent.observe(lifecycleOwner, Observer { event ->
event.getContentIfNotHandled()?.let {
showSnackbar(context.getString(it), timeLength)
when (it) {
is Int -> showSnackbar(context.getString(it), timeLength)
is String -> showSnackbar(it, timeLength)
else -> throw TypeCastException()
}
}
})
}
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@
<string name="notice_tv_vote_absent">불참할게요! %d</string>

<string name="login_error_message_empty_text">이메일 혹은 비밀번호를 채워주세요!</string>
<string name="login_error_message_fail_login">이메일 혹은 비밀번호를 확인해주세요!</string>
<string name="error_message_unknown">알 수 없는 에러가 발생했습니다.</string>
</resources>

0 comments on commit e4b8eca

Please sign in to comment.