use KotlinResultCallAdapter instead of NetworkCallAdapter (#13)
This commit is contained in:
parent
466dba6096
commit
a27035b84d
|
@ -65,6 +65,8 @@ dependencies {
|
||||||
val daggerVersion = "2.40.5"
|
val daggerVersion = "2.40.5"
|
||||||
val jUnitVersion = "5.8.2"
|
val jUnitVersion = "5.8.2"
|
||||||
|
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
|
||||||
|
|
||||||
implementation("androidx.core:core-ktx:1.7.0")
|
implementation("androidx.core:core-ktx:1.7.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.4.1")
|
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||||
implementation("androidx.activity:activity-ktx:1.4.0")
|
implementation("androidx.activity:activity-ktx:1.4.0")
|
||||||
|
@ -98,7 +100,7 @@ dependencies {
|
||||||
implementation("com.squareup.moshi:moshi-adapters:$moshiVersion")
|
implementation("com.squareup.moshi:moshi-adapters:$moshiVersion")
|
||||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion")
|
kapt("com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion")
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
|
implementation("at.connyduck:kotlin-result-calladapter:1.0.0")
|
||||||
|
|
||||||
implementation("com.fxn769:pix:1.5.6")
|
implementation("com.fxn769:pix:1.5.6")
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ import at.connyduck.pixelcat.components.util.getMimeType
|
||||||
import at.connyduck.pixelcat.db.AccountManager
|
import at.connyduck.pixelcat.db.AccountManager
|
||||||
import at.connyduck.pixelcat.model.NewStatus
|
import at.connyduck.pixelcat.model.NewStatus
|
||||||
import at.connyduck.pixelcat.network.FediverseApi
|
import at.connyduck.pixelcat.network.FediverseApi
|
||||||
import at.connyduck.pixelcat.network.calladapter.NetworkResponseError
|
|
||||||
import dagger.android.DaggerService
|
import dagger.android.DaggerService
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -49,6 +48,7 @@ import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody.Companion.asRequestBody
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import java.util.TimerTask
|
import java.util.TimerTask
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
@ -173,7 +173,7 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
||||||
account.domain,
|
account.domain,
|
||||||
statusToSend.idempotencyKey,
|
statusToSend.idempotencyKey,
|
||||||
newStatus
|
newStatus
|
||||||
).fold<Any?>(
|
).fold(
|
||||||
{
|
{
|
||||||
statusesToSend.remove(id)
|
statusesToSend.remove(id)
|
||||||
stopSelfWhenDone()
|
stopSelfWhenDone()
|
||||||
|
@ -192,20 +192,8 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
||||||
val statusToSend = statusesToSend[id] ?: return
|
val statusToSend = statusesToSend[id] ?: return
|
||||||
|
|
||||||
when (error) {
|
when (error) {
|
||||||
is NetworkResponseError.ApiError, is UnrecoverableError -> {
|
is IOException -> {
|
||||||
// the server refused to accept the status, save toot & show error message
|
// possibly a network problem, we might still have a chance sending the status
|
||||||
// TODO saveToDrafts
|
|
||||||
|
|
||||||
val builder = NotificationCompat.Builder(this@SendStatusService, CHANNEL_ID)
|
|
||||||
.setSmallIcon(R.drawable.ic_cat)
|
|
||||||
.setContentTitle(getString(R.string.send_status_notification_error_title))
|
|
||||||
// .setContentText(getString(R.string.send_toot_notification_saved_content))
|
|
||||||
.setColor(getColorForAttr(android.R.attr.colorPrimary))
|
|
||||||
|
|
||||||
notificationManager.cancel(id)
|
|
||||||
notificationManager.notify(errorNotificationId--, builder.build())
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
var backoff = TimeUnit.SECONDS.toMillis(statusToSend.retries.toLong())
|
var backoff = TimeUnit.SECONDS.toMillis(statusToSend.retries.toLong())
|
||||||
if (backoff > MAX_RETRY_INTERVAL) {
|
if (backoff > MAX_RETRY_INTERVAL) {
|
||||||
backoff = MAX_RETRY_INTERVAL
|
backoff = MAX_RETRY_INTERVAL
|
||||||
|
@ -220,6 +208,19 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
||||||
backoff
|
backoff
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
|
// the server refused to accept the status, save toot & show error message
|
||||||
|
// TODO saveToDrafts
|
||||||
|
|
||||||
|
val builder = NotificationCompat.Builder(this@SendStatusService, CHANNEL_ID)
|
||||||
|
.setSmallIcon(R.drawable.ic_cat)
|
||||||
|
.setContentTitle(getString(R.string.send_status_notification_error_title))
|
||||||
|
// .setContentText(getString(R.string.send_toot_notification_saved_content))
|
||||||
|
.setColor(getColorForAttr(android.R.attr.colorPrimary))
|
||||||
|
|
||||||
|
notificationManager.cancel(id)
|
||||||
|
notificationManager.notify(errorNotificationId--, builder.build())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||||
import at.connyduck.pixelcat.db.entitity.toEntity
|
import at.connyduck.pixelcat.db.entitity.toEntity
|
||||||
import at.connyduck.pixelcat.model.Status
|
import at.connyduck.pixelcat.model.Status
|
||||||
import at.connyduck.pixelcat.network.FediverseApi
|
import at.connyduck.pixelcat.network.FediverseApi
|
||||||
import at.connyduck.pixelcat.network.calladapter.NetworkResponse
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@ -43,8 +42,8 @@ class TimelineUseCases @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun NetworkResponse<Status>.updateStatusInDb() {
|
private suspend fun Result<Status>.updateStatusInDb() {
|
||||||
fold<Any?>(
|
fold(
|
||||||
{ updatedStatus ->
|
{ updatedStatus ->
|
||||||
val accountId = accountManager.activeAccount()?.id!!
|
val accountId = accountManager.activeAccount()?.id!!
|
||||||
val updatedStatusEntity = updatedStatus.toEntity(accountId)
|
val updatedStatusEntity = updatedStatus.toEntity(accountId)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package at.connyduck.pixelcat.dagger
|
package at.connyduck.pixelcat.dagger
|
||||||
|
|
||||||
|
import at.connyduck.calladapter.kotlinresult.KotlinResultCallAdapterFactory
|
||||||
import at.connyduck.pixelcat.BuildConfig
|
import at.connyduck.pixelcat.BuildConfig
|
||||||
import at.connyduck.pixelcat.db.AccountManager
|
import at.connyduck.pixelcat.db.AccountManager
|
||||||
import at.connyduck.pixelcat.model.Notification
|
import at.connyduck.pixelcat.model.Notification
|
||||||
|
@ -26,7 +27,6 @@ import at.connyduck.pixelcat.network.FediverseApi
|
||||||
import at.connyduck.pixelcat.network.InstanceSwitchAuthInterceptor
|
import at.connyduck.pixelcat.network.InstanceSwitchAuthInterceptor
|
||||||
import at.connyduck.pixelcat.network.RefreshTokenAuthenticator
|
import at.connyduck.pixelcat.network.RefreshTokenAuthenticator
|
||||||
import at.connyduck.pixelcat.network.UserAgentInterceptor
|
import at.connyduck.pixelcat.network.UserAgentInterceptor
|
||||||
import at.connyduck.pixelcat.network.calladapter.NetworkResponseAdapterFactory
|
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import com.squareup.moshi.adapters.EnumJsonAdapter
|
import com.squareup.moshi.adapters.EnumJsonAdapter
|
||||||
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
|
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
|
||||||
|
@ -82,7 +82,7 @@ class NetworkModule {
|
||||||
return Retrofit.Builder()
|
return Retrofit.Builder()
|
||||||
.baseUrl("https://" + FediverseApi.PLACEHOLDER_DOMAIN)
|
.baseUrl("https://" + FediverseApi.PLACEHOLDER_DOMAIN)
|
||||||
.client(httpClient)
|
.client(httpClient)
|
||||||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
.addCallAdapterFactory(KotlinResultCallAdapterFactory.create())
|
||||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ import at.connyduck.pixelcat.model.Notification
|
||||||
import at.connyduck.pixelcat.model.Relationship
|
import at.connyduck.pixelcat.model.Relationship
|
||||||
import at.connyduck.pixelcat.model.Status
|
import at.connyduck.pixelcat.model.Status
|
||||||
import at.connyduck.pixelcat.model.StatusContext
|
import at.connyduck.pixelcat.model.StatusContext
|
||||||
import at.connyduck.pixelcat.network.calladapter.NetworkResponse
|
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.Field
|
import retrofit2.http.Field
|
||||||
|
@ -57,7 +56,7 @@ interface FediverseApi {
|
||||||
@Field("website") clientWebsite: String,
|
@Field("website") clientWebsite: String,
|
||||||
@Field("redirect_uris") redirectUris: String,
|
@Field("redirect_uris") redirectUris: String,
|
||||||
@Field("scopes") scopes: String
|
@Field("scopes") scopes: String
|
||||||
): NetworkResponse<AppCredentials>
|
): Result<AppCredentials>
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST("oauth/token")
|
@POST("oauth/token")
|
||||||
|
@ -68,7 +67,7 @@ interface FediverseApi {
|
||||||
@Field("redirect_uri") redirectUri: String,
|
@Field("redirect_uri") redirectUri: String,
|
||||||
@Field("code") code: String,
|
@Field("code") code: String,
|
||||||
@Field("grant_type") grantType: String = "authorization_code"
|
@Field("grant_type") grantType: String = "authorization_code"
|
||||||
): NetworkResponse<AccessToken>
|
): Result<AccessToken>
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST("oauth/token")
|
@POST("oauth/token")
|
||||||
|
@ -78,17 +77,17 @@ interface FediverseApi {
|
||||||
@Field("client_secret") clientSecret: String,
|
@Field("client_secret") clientSecret: String,
|
||||||
@Field("refresh_token") refreshToken: String,
|
@Field("refresh_token") refreshToken: String,
|
||||||
@Field("grant_type") grantType: String = "refresh_token"
|
@Field("grant_type") grantType: String = "refresh_token"
|
||||||
): NetworkResponse<AccessToken>
|
): Result<AccessToken>
|
||||||
|
|
||||||
@GET("api/v1/accounts/verify_credentials")
|
@GET("api/v1/accounts/verify_credentials")
|
||||||
suspend fun accountVerifyCredentials(): NetworkResponse<Account>
|
suspend fun accountVerifyCredentials(): Result<Account>
|
||||||
|
|
||||||
@GET("api/v1/timelines/home")
|
@GET("api/v1/timelines/home")
|
||||||
suspend fun homeTimeline(
|
suspend fun homeTimeline(
|
||||||
@Query("max_id") maxId: String? = null,
|
@Query("max_id") maxId: String? = null,
|
||||||
@Query("since_id") sinceId: String? = null,
|
@Query("since_id") sinceId: String? = null,
|
||||||
@Query("limit") limit: Int? = null
|
@Query("limit") limit: Int? = null
|
||||||
): NetworkResponse<List<Status>>
|
): Result<List<Status>>
|
||||||
|
|
||||||
@GET("api/v1/accounts/{id}/statuses")
|
@GET("api/v1/accounts/{id}/statuses")
|
||||||
suspend fun accountTimeline(
|
suspend fun accountTimeline(
|
||||||
|
@ -99,12 +98,12 @@ interface FediverseApi {
|
||||||
@Query("exclude_replies") excludeReplies: Boolean? = false,
|
@Query("exclude_replies") excludeReplies: Boolean? = false,
|
||||||
@Query("only_media") onlyMedia: Boolean? = true,
|
@Query("only_media") onlyMedia: Boolean? = true,
|
||||||
@Query("pinned") pinned: Boolean? = false
|
@Query("pinned") pinned: Boolean? = false
|
||||||
): NetworkResponse<Status>
|
): Result<Status>
|
||||||
|
|
||||||
@GET("api/v1/accounts/{id}")
|
@GET("api/v1/accounts/{id}")
|
||||||
suspend fun account(
|
suspend fun account(
|
||||||
@Path("id") accountId: String
|
@Path("id") accountId: String
|
||||||
): NetworkResponse<Account>
|
): Result<Account>
|
||||||
|
|
||||||
@GET("api/v1/accounts/{id}/statuses")
|
@GET("api/v1/accounts/{id}/statuses")
|
||||||
suspend fun accountStatuses(
|
suspend fun accountStatuses(
|
||||||
|
@ -114,50 +113,50 @@ interface FediverseApi {
|
||||||
@Query("limit") limit: Int? = null,
|
@Query("limit") limit: Int? = null,
|
||||||
@Query("only_media") onlyMedia: Boolean? = null,
|
@Query("only_media") onlyMedia: Boolean? = null,
|
||||||
@Query("exclude_reblogs") excludeReblogs: Boolean? = null
|
@Query("exclude_reblogs") excludeReblogs: Boolean? = null
|
||||||
): NetworkResponse<List<Status>>
|
): Result<List<Status>>
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST("api/v1/accounts/{id}/follow")
|
@POST("api/v1/accounts/{id}/follow")
|
||||||
suspend fun followAccount(
|
suspend fun followAccount(
|
||||||
@Path("id") accountId: String,
|
@Path("id") accountId: String,
|
||||||
@Field("reblogs") showReblogs: Boolean
|
@Field("reblogs") showReblogs: Boolean
|
||||||
): NetworkResponse<Relationship>
|
): Result<Relationship>
|
||||||
|
|
||||||
@POST("api/v1/accounts/{id}/unfollow")
|
@POST("api/v1/accounts/{id}/unfollow")
|
||||||
suspend fun unfollowAccount(
|
suspend fun unfollowAccount(
|
||||||
@Path("id") accountId: String
|
@Path("id") accountId: String
|
||||||
): NetworkResponse<Relationship>
|
): Result<Relationship>
|
||||||
|
|
||||||
@POST("api/v1/accounts/{id}/block")
|
@POST("api/v1/accounts/{id}/block")
|
||||||
suspend fun blockAccount(
|
suspend fun blockAccount(
|
||||||
@Path("id") accountId: String
|
@Path("id") accountId: String
|
||||||
): NetworkResponse<Relationship>
|
): Result<Relationship>
|
||||||
|
|
||||||
@POST("api/v1/accounts/{id}/unblock")
|
@POST("api/v1/accounts/{id}/unblock")
|
||||||
suspend fun unblockAccount(
|
suspend fun unblockAccount(
|
||||||
@Path("id") accountId: String
|
@Path("id") accountId: String
|
||||||
): NetworkResponse<Relationship>
|
): Result<Relationship>
|
||||||
|
|
||||||
@POST("api/v1/accounts/{id}/mute")
|
@POST("api/v1/accounts/{id}/mute")
|
||||||
suspend fun muteAccount(
|
suspend fun muteAccount(
|
||||||
@Path("id") accountId: String
|
@Path("id") accountId: String
|
||||||
): NetworkResponse<Relationship>
|
): Result<Relationship>
|
||||||
|
|
||||||
@POST("api/v1/accounts/{id}/unmute")
|
@POST("api/v1/accounts/{id}/unmute")
|
||||||
suspend fun unmuteAccount(
|
suspend fun unmuteAccount(
|
||||||
@Path("id") accountId: String
|
@Path("id") accountId: String
|
||||||
): NetworkResponse<Relationship>
|
): Result<Relationship>
|
||||||
|
|
||||||
@GET("api/v1/accounts/relationships")
|
@GET("api/v1/accounts/relationships")
|
||||||
suspend fun relationships(
|
suspend fun relationships(
|
||||||
@Query("id[]") accountIds: List<String>
|
@Query("id[]") accountIds: List<String>
|
||||||
): NetworkResponse<List<Relationship>>
|
): Result<List<Relationship>>
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("api/v1/media")
|
@POST("api/v1/media")
|
||||||
suspend fun uploadMedia(
|
suspend fun uploadMedia(
|
||||||
@Part file: MultipartBody.Part
|
@Part file: MultipartBody.Part
|
||||||
): NetworkResponse<Attachment>
|
): Result<Attachment>
|
||||||
|
|
||||||
@POST("api/v1/statuses")
|
@POST("api/v1/statuses")
|
||||||
suspend fun createStatus(
|
suspend fun createStatus(
|
||||||
|
@ -165,42 +164,42 @@ interface FediverseApi {
|
||||||
@Header(DOMAIN_HEADER) domain: String,
|
@Header(DOMAIN_HEADER) domain: String,
|
||||||
@Header("Idempotency-Key") idempotencyKey: String,
|
@Header("Idempotency-Key") idempotencyKey: String,
|
||||||
@Body status: NewStatus
|
@Body status: NewStatus
|
||||||
): NetworkResponse<Status>
|
): Result<Status>
|
||||||
|
|
||||||
@POST("api/v1/statuses")
|
@POST("api/v1/statuses")
|
||||||
suspend fun reply(
|
suspend fun reply(
|
||||||
@Body status: NewStatus
|
@Body status: NewStatus
|
||||||
): NetworkResponse<Status>
|
): Result<Status>
|
||||||
|
|
||||||
@POST("api/v1/statuses/{id}/favourite")
|
@POST("api/v1/statuses/{id}/favourite")
|
||||||
suspend fun favouriteStatus(
|
suspend fun favouriteStatus(
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
): NetworkResponse<Status>
|
): Result<Status>
|
||||||
|
|
||||||
@POST("api/v1/statuses/{id}/unfavourite")
|
@POST("api/v1/statuses/{id}/unfavourite")
|
||||||
suspend fun unfavouriteStatus(
|
suspend fun unfavouriteStatus(
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
): NetworkResponse<Status>
|
): Result<Status>
|
||||||
|
|
||||||
@POST("api/v1/statuses/{id}/reblog")
|
@POST("api/v1/statuses/{id}/reblog")
|
||||||
suspend fun reblogStatus(
|
suspend fun reblogStatus(
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
): NetworkResponse<Status>
|
): Result<Status>
|
||||||
|
|
||||||
@POST("api/v1/statuses/{id}/unreblog")
|
@POST("api/v1/statuses/{id}/unreblog")
|
||||||
suspend fun unreblogStatus(
|
suspend fun unreblogStatus(
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
): NetworkResponse<Status>
|
): Result<Status>
|
||||||
|
|
||||||
@GET("api/v1/statuses/{id}")
|
@GET("api/v1/statuses/{id}")
|
||||||
suspend fun status(
|
suspend fun status(
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
): NetworkResponse<Status>
|
): Result<Status>
|
||||||
|
|
||||||
@GET("api/v1/statuses/{id}/context")
|
@GET("api/v1/statuses/{id}/context")
|
||||||
suspend fun statusContext(
|
suspend fun statusContext(
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
): NetworkResponse<StatusContext>
|
): Result<StatusContext>
|
||||||
|
|
||||||
@GET("api/v1/notifications")
|
@GET("api/v1/notifications")
|
||||||
suspend fun notifications(
|
suspend fun notifications(
|
||||||
|
@ -208,5 +207,5 @@ interface FediverseApi {
|
||||||
@Query("since_id") sinceId: String? = null,
|
@Query("since_id") sinceId: String? = null,
|
||||||
@Query("limit") limit: Int? = null,
|
@Query("limit") limit: Int? = null,
|
||||||
@Query("exclude_types[]") excludes: Set<String>? = null
|
@Query("exclude_types[]") excludes: Set<String>? = null
|
||||||
): NetworkResponse<List<Notification>>
|
): Result<List<Notification>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.CallAdapter
|
|
||||||
import java.lang.reflect.Type
|
|
||||||
|
|
||||||
class NetworkCallAdapter<S : Any>(
|
|
||||||
private val successType: Type
|
|
||||||
) : CallAdapter<S, Call<NetworkResponse<S>>> {
|
|
||||||
|
|
||||||
override fun responseType(): Type = successType
|
|
||||||
|
|
||||||
override fun adapt(call: Call<S>): Call<NetworkResponse<S>> {
|
|
||||||
return NetworkResponseCall(call)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
sealed class NetworkResponse<out A : Any> {
|
|
||||||
|
|
||||||
data class Success<T : Any>(val body: T) : NetworkResponse<T>()
|
|
||||||
|
|
||||||
data class Failure(val reason: NetworkResponseError) : NetworkResponse<Nothing>()
|
|
||||||
|
|
||||||
inline fun <B> fold(onSuccess: (A) -> B, onFailure: (NetworkResponseError) -> B): B = when (this) {
|
|
||||||
is Success -> onSuccess(body)
|
|
||||||
is Failure -> onFailure(reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class NetworkResponseError : Throwable() {
|
|
||||||
|
|
||||||
data class ApiError(val code: Int) : NetworkResponseError()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Network error
|
|
||||||
*/
|
|
||||||
data class NetworkError(val error: IOException) : NetworkResponseError()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For example, json parsing error
|
|
||||||
*/
|
|
||||||
data class UnknownError(val error: Throwable?) : NetworkResponseError()
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.CallAdapter
|
|
||||||
import retrofit2.Retrofit
|
|
||||||
import java.lang.reflect.ParameterizedType
|
|
||||||
import java.lang.reflect.Type
|
|
||||||
|
|
||||||
class NetworkResponseAdapterFactory : CallAdapter.Factory() {
|
|
||||||
|
|
||||||
override fun get(
|
|
||||||
returnType: Type,
|
|
||||||
annotations: Array<Annotation>,
|
|
||||||
retrofit: Retrofit
|
|
||||||
): CallAdapter<*, *>? {
|
|
||||||
|
|
||||||
// suspend functions wrap the response type in `Call`
|
|
||||||
if (Call::class.java != getRawType(returnType)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// check first that the return type is `ParameterizedType`
|
|
||||||
check(returnType is ParameterizedType) {
|
|
||||||
"return type must be parameterized as Call<NetworkResponse<<Foo>> or Call<NetworkResponse<out Foo>>"
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the response type inside the `Call` type
|
|
||||||
val responseType = getParameterUpperBound(0, returnType)
|
|
||||||
// if the response type is not ApiResponse then we can't handle this type, so we return null
|
|
||||||
if (getRawType(responseType) != NetworkResponse::class.java) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// the response type is ApiResponse and should be parameterized
|
|
||||||
check(responseType is ParameterizedType) { "Response must be parameterized as NetworkResponse<Foo> or NetworkResponse<out Foo>" }
|
|
||||||
|
|
||||||
val successBodyType = getParameterUpperBound(0, responseType)
|
|
||||||
|
|
||||||
return NetworkCallAdapter<Any>(successBodyType)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import okhttp3.Request
|
|
||||||
import okio.Timeout
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Callback
|
|
||||||
import retrofit2.Response
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
internal class NetworkResponseCall<S : Any>(
|
|
||||||
private val delegate: Call<S>
|
|
||||||
) : Call<NetworkResponse<S>> {
|
|
||||||
|
|
||||||
override fun enqueue(callback: Callback<NetworkResponse<S>>) {
|
|
||||||
return delegate.enqueue(
|
|
||||||
object : Callback<S> {
|
|
||||||
override fun onResponse(call: Call<S>, response: Response<S>) {
|
|
||||||
val body = response.body()
|
|
||||||
|
|
||||||
if (response.isSuccessful) {
|
|
||||||
if (body != null) {
|
|
||||||
callback.onResponse(
|
|
||||||
this@NetworkResponseCall,
|
|
||||||
Response.success(NetworkResponse.Success(body))
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Response is successful but the body is null
|
|
||||||
callback.onResponse(
|
|
||||||
this@NetworkResponseCall,
|
|
||||||
Response.success(
|
|
||||||
NetworkResponse.Failure(
|
|
||||||
NetworkResponseError.ApiError(
|
|
||||||
response.code()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
callback.onResponse(
|
|
||||||
this@NetworkResponseCall,
|
|
||||||
Response.success(
|
|
||||||
NetworkResponse.Failure(
|
|
||||||
NetworkResponseError.ApiError(
|
|
||||||
response.code()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<S>, throwable: Throwable) {
|
|
||||||
val networkResponse = when (throwable) {
|
|
||||||
is IOException -> NetworkResponse.Failure(
|
|
||||||
NetworkResponseError.NetworkError(
|
|
||||||
throwable
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else -> NetworkResponse.Failure(NetworkResponseError.UnknownError(throwable))
|
|
||||||
}
|
|
||||||
callback.onResponse(this@NetworkResponseCall, Response.success(networkResponse))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isExecuted() = delegate.isExecuted
|
|
||||||
|
|
||||||
override fun clone() = NetworkResponseCall(delegate.clone())
|
|
||||||
|
|
||||||
override fun isCanceled() = delegate.isCanceled
|
|
||||||
|
|
||||||
override fun cancel() = delegate.cancel()
|
|
||||||
|
|
||||||
override fun execute(): Response<NetworkResponse<S>> {
|
|
||||||
throw UnsupportedOperationException("NetworkResponseCall doesn't support synchronized execution")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun request(): Request = delegate.request()
|
|
||||||
|
|
||||||
override fun timeout(): Timeout = delegate.timeout()
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.mockwebserver.MockResponse
|
|
||||||
import okhttp3.mockwebserver.MockWebServer
|
|
||||||
import okhttp3.mockwebserver.SocketPolicy
|
|
||||||
import org.junit.jupiter.api.AfterEach
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import retrofit2.Retrofit
|
|
||||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
class ApiTest {
|
|
||||||
|
|
||||||
private var mockWebServer = MockWebServer()
|
|
||||||
|
|
||||||
private lateinit var api: TestApi
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setup() {
|
|
||||||
mockWebServer.start()
|
|
||||||
|
|
||||||
val moshi = Moshi.Builder()
|
|
||||||
.add(KotlinJsonAdapterFactory())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
api = Retrofit.Builder()
|
|
||||||
.baseUrl(mockWebServer.url("/"))
|
|
||||||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
|
||||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
|
||||||
.client(OkHttpClient())
|
|
||||||
.build()
|
|
||||||
.create(TestApi::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
fun shutdown() {
|
|
||||||
mockWebServer.shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun mockResponse(responseCode: Int, body: String = "") = MockResponse()
|
|
||||||
.setResponseCode(responseCode)
|
|
||||||
.setBody(body)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should return the correct test object`() {
|
|
||||||
val response = mockResponse(
|
|
||||||
200,
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"lets": "not",
|
|
||||||
"test": 1
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
mockWebServer.enqueue(response)
|
|
||||||
|
|
||||||
val responseObject = runBlocking {
|
|
||||||
api.testEndpoint()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
NetworkResponse.Success(TestResponseClass("not", 1)),
|
|
||||||
responseObject
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should return a ApiError failure when the server returns error 500`() {
|
|
||||||
val errorCode = 500
|
|
||||||
val response = mockResponse(errorCode)
|
|
||||||
|
|
||||||
mockWebServer.enqueue(response)
|
|
||||||
|
|
||||||
val responseObject = runBlocking {
|
|
||||||
api.testEndpoint()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
NetworkResponse.Failure(NetworkResponseError.ApiError(errorCode)),
|
|
||||||
responseObject
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should return a NetworkError failure when the network fails`() {
|
|
||||||
mockWebServer.enqueue(MockResponse().apply { socketPolicy = SocketPolicy.DISCONNECT_AFTER_REQUEST })
|
|
||||||
val responseObject = runBlocking {
|
|
||||||
api.testEndpoint()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
NetworkResponse.Failure(
|
|
||||||
NetworkResponseError.NetworkError(
|
|
||||||
object : IOException() {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
return (other is IOException)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
),
|
|
||||||
responseObject
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import com.squareup.moshi.Types
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.Assertions.assertNull
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Retrofit
|
|
||||||
|
|
||||||
class NetworkResponseAdapterFactoryTest {
|
|
||||||
|
|
||||||
private val retrofit = Retrofit.Builder().baseUrl("http://example.com").build()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should return a NetworkResponseCallAdapter when the type is supported`() {
|
|
||||||
val networkResponseType =
|
|
||||||
Types.newParameterizedType(NetworkResponse::class.java, TestResponseClass::class.java)
|
|
||||||
val callType = Types.newParameterizedType(Call::class.java, networkResponseType)
|
|
||||||
|
|
||||||
val adapter = NetworkResponseAdapterFactory().get(callType, arrayOf(), retrofit)
|
|
||||||
|
|
||||||
assertEquals(TestResponseClass::class.java, adapter?.responseType())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should return null if the type is not supported`() {
|
|
||||||
|
|
||||||
val adapter = NetworkResponseAdapterFactory().get(TestResponseClass::class.java, arrayOf(), retrofit)
|
|
||||||
|
|
||||||
assertNull(adapter)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.junit.jupiter.api.assertThrows
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Callback
|
|
||||||
import retrofit2.Response
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
class NetworkResponseCallTest {
|
|
||||||
|
|
||||||
private val backingCall = TestCall<String>()
|
|
||||||
private val networkCall = NetworkResponseCall(backingCall)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should throw an error when invoking 'execute'`() {
|
|
||||||
assertThrows<UnsupportedOperationException> {
|
|
||||||
networkCall.execute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should delegate properties to backing call`() {
|
|
||||||
with(networkCall) {
|
|
||||||
assertEquals(isExecuted, backingCall.isExecuted)
|
|
||||||
assertEquals(isCanceled, backingCall.isCanceled)
|
|
||||||
assertEquals(request(), backingCall.request())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should return new instance when cloned`() {
|
|
||||||
val clonedCall = networkCall.clone()
|
|
||||||
assert(clonedCall !== networkCall)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should cancel backing call as well when cancelled`() {
|
|
||||||
networkCall.cancel()
|
|
||||||
assert(backingCall.isCanceled)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should parse successful call as NetworkResponse Success`() {
|
|
||||||
val body = "Test body"
|
|
||||||
networkCall.enqueue(
|
|
||||||
object : Callback<NetworkResponse<String>> {
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<NetworkResponse<String>>,
|
|
||||||
response: Response<NetworkResponse<String>>
|
|
||||||
) {
|
|
||||||
assertTrue(response.isSuccessful)
|
|
||||||
assertEquals(
|
|
||||||
response.body(),
|
|
||||||
NetworkResponse.Success(body)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<NetworkResponse<String>>, t: Throwable) {
|
|
||||||
throw IllegalStateException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
backingCall.complete(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should parse call with 404 error code as ApiError`() {
|
|
||||||
val errorCode = 404
|
|
||||||
val errorBody = "not found"
|
|
||||||
networkCall.enqueue(
|
|
||||||
object : Callback<NetworkResponse<String>> {
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<NetworkResponse<String>>,
|
|
||||||
response: Response<NetworkResponse<String>>
|
|
||||||
) {
|
|
||||||
assertEquals(
|
|
||||||
response.body(),
|
|
||||||
NetworkResponse.Failure(NetworkResponseError.ApiError(errorCode))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<NetworkResponse<String>>, t: Throwable) {
|
|
||||||
throw IllegalStateException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
backingCall.complete(Response.error(errorCode, errorBody.toResponseBody()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should parse call with IOException as NetworkError`() {
|
|
||||||
val exception = IOException()
|
|
||||||
networkCall.enqueue(
|
|
||||||
object : Callback<NetworkResponse<String>> {
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<NetworkResponse<String>>,
|
|
||||||
response: Response<NetworkResponse<String>>
|
|
||||||
) {
|
|
||||||
assertEquals(
|
|
||||||
response.body(),
|
|
||||||
NetworkResponse.Failure(NetworkResponseError.NetworkError(exception))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<NetworkResponse<String>>, t: Throwable) {
|
|
||||||
throw IllegalStateException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
backingCall.completeWithException(exception)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import retrofit2.http.GET
|
|
||||||
|
|
||||||
interface TestApi {
|
|
||||||
|
|
||||||
@GET("testpath")
|
|
||||||
suspend fun testEndpoint(): NetworkResponse<TestResponseClass>
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
import okhttp3.Request
|
|
||||||
import okio.Timeout
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Callback
|
|
||||||
import retrofit2.Response
|
|
||||||
import java.io.InterruptedIOException
|
|
||||||
|
|
||||||
class TestCall<T> : Call<T> {
|
|
||||||
private var executed = false
|
|
||||||
private var canceled = false
|
|
||||||
private var callback: Callback<T>? = null
|
|
||||||
private var request = Request.Builder().url("http://example.com").build()
|
|
||||||
|
|
||||||
fun completeWithException(t: Throwable) {
|
|
||||||
synchronized(this) {
|
|
||||||
callback?.onFailure(this, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun complete(body: T) = complete(Response.success(body))
|
|
||||||
|
|
||||||
fun complete(response: Response<T>) {
|
|
||||||
synchronized(this) {
|
|
||||||
callback?.onResponse(this, response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun enqueue(callback: Callback<T>) {
|
|
||||||
synchronized(this) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isExecuted() = synchronized(this) { executed }
|
|
||||||
override fun isCanceled() = synchronized(this) { canceled }
|
|
||||||
override fun clone() = TestCall<T>()
|
|
||||||
|
|
||||||
override fun cancel() {
|
|
||||||
synchronized(this) {
|
|
||||||
if (canceled) return
|
|
||||||
canceled = true
|
|
||||||
|
|
||||||
val exception = InterruptedIOException("canceled")
|
|
||||||
callback?.onFailure(this, exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun execute(): Response<T> {
|
|
||||||
throw UnsupportedOperationException("Network call does not support synchronous execution")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun request() = request
|
|
||||||
override fun timeout() = Timeout()
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Conny Duck
|
|
||||||
*
|
|
||||||
* This file is part of Pixelcat.
|
|
||||||
*
|
|
||||||
* Pixelcat is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Pixelcat is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package at.connyduck.pixelcat.network.calladapter
|
|
||||||
|
|
||||||
data class TestResponseClass(
|
|
||||||
val lets: String,
|
|
||||||
val test: Int
|
|
||||||
)
|
|
|
@ -14,6 +14,7 @@ buildscript {
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven(url = "https://jitpack.io")
|
maven(url = "https://jitpack.io")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue