Created
July 11, 2018 09:50
-
-
Save jutikorn/698b2688370e7d4a408353b7cf761e19 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.util.Log | |
import retrofit2.Response | |
import java.util.regex.Pattern | |
/** | |
* Common class used by API responses. | |
* @param <T> the type of the response object | |
</T> */ | |
@Suppress("unused") // T is used in extending classes | |
sealed class ApiResponse<T> { | |
companion object { | |
fun <T> create(error: Throwable): ApiErrorResponse<T> { | |
return ApiErrorResponse(error.message ?: "unknown error") | |
} | |
fun <T> create(response: Response<T>): ApiResponse<T> { | |
return if (response.isSuccessful) { | |
val body = response.body() | |
if (body == null || response.code() == 204) { | |
ApiEmptyResponse() | |
} else { | |
ApiSuccessResponse( | |
body = body, | |
linkHeader = response.headers()?.get("link") | |
) | |
} | |
} else { | |
val msg = response.errorBody()?.string() | |
val errorMsg = if (msg.isNullOrEmpty()) { | |
response.message() | |
} else { | |
msg | |
} | |
ApiErrorResponse(errorMsg ?: "unknown error") | |
} | |
} | |
} | |
} | |
/** | |
* separate class for HTTP 204 resposes so that we can make ApiSuccessResponse's body non-null. | |
*/ | |
class ApiEmptyResponse<T> : ApiResponse<T>() | |
data class ApiSuccessResponse<T>( | |
val body: T, | |
val links: Map<String, String> | |
) : ApiResponse<T>() { | |
constructor(body: T, linkHeader: String?) : this( | |
body = body, | |
links = linkHeader?.extractLinks() ?: emptyMap() | |
) | |
val nextPage: Int? by lazy(LazyThreadSafetyMode.NONE) { | |
links[NEXT_LINK]?.let { next -> | |
val matcher = PAGE_PATTERN.matcher(next) | |
if (!matcher.find() || matcher.groupCount() != 1) { | |
null | |
} else { | |
try { | |
Integer.parseInt(matcher.group(1)) | |
} catch (ex: NumberFormatException) { | |
Log.w("Warning" ,"cannot parse next page from $next") | |
null | |
} | |
} | |
} | |
} | |
companion object { | |
private val LINK_PATTERN = Pattern.compile("<([^>]*)>[\\s]*;[\\s]*rel=\"([a-zA-Z0-9]+)\"") | |
private val PAGE_PATTERN = Pattern.compile("\\bpage=(\\d+)") | |
private const val NEXT_LINK = "next" | |
private fun String.extractLinks(): Map<String, String> { | |
val links = mutableMapOf<String, String>() | |
val matcher = LINK_PATTERN.matcher(this) | |
while (matcher.find()) { | |
val count = matcher.groupCount() | |
if (count == 2) { | |
links[matcher.group(2)] = matcher.group(1) | |
} | |
} | |
return links | |
} | |
} | |
} | |
data class ApiErrorResponse<T>(val errorMessage: String) : ApiResponse<T>() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.jutikorn.eddieplayer.common.structure.SingleLiveEvent | |
import retrofit2.Call | |
import retrofit2.CallAdapter | |
import retrofit2.Callback | |
import retrofit2.Response | |
import java.lang.reflect.Type | |
/** | |
* A Retrofit adapter that converts the Call into a SingleLiveEvent of ApiResponse. | |
* @param <R> | |
</R> */ | |
class SingleLiveEventCallAdapter<R>(private val responseType: Type) : | |
CallAdapter<R, SingleLiveEvent<ApiResponse<R>>> { | |
override fun responseType() = responseType | |
override fun adapt(call: Call<R>): SingleLiveEvent<ApiResponse<R>> = | |
SingleLiveEvent<ApiResponse<R>>().apply { | |
call.enqueue(object : Callback<R> { | |
override fun onResponse(call: Call<R>, response: Response<R>) { | |
this@apply.postValue(ApiResponse.create(response)) | |
} | |
override fun onFailure(call: Call<R>, throwable: Throwable) { | |
this@apply.postValue(ApiResponse.create(throwable)) | |
} | |
}) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.jutikorn.eddieplayer.common.structure.SingleLiveEvent | |
import retrofit2.CallAdapter | |
import retrofit2.CallAdapter.Factory | |
import retrofit2.Retrofit | |
import java.lang.reflect.ParameterizedType | |
import java.lang.reflect.Type | |
class SingleLiveEventCallAdapterFactory : Factory() { | |
override fun get( | |
returnType: Type, | |
annotations: Array<Annotation>, | |
retrofit: Retrofit | |
): CallAdapter<*, *>? { | |
if (Factory.getRawType(returnType) != SingleLiveEvent::class.java) { | |
return null | |
} | |
val observableType = Factory.getParameterUpperBound(0, returnType as ParameterizedType) | |
val rawObservableType = Factory.getRawType(observableType) | |
if (rawObservableType != ApiResponse::class.java) { | |
throw IllegalArgumentException("type must be a resource") | |
} | |
if (observableType !is ParameterizedType) { | |
throw IllegalArgumentException("resource must be parameterized") | |
} | |
val bodyType = Factory.getParameterUpperBound(0, observableType) | |
return SingleLiveEventCallAdapter<Any>(bodyType) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment