diff --git a/app_fenrir/build.gradle b/app_fenrir/build.gradle index 0ced149a0..826ecce54 100644 --- a/app_fenrir/build.gradle +++ b/app_fenrir/build.gradle @@ -139,7 +139,6 @@ dependencies { implementation project(path: ":viewpager2") implementation project(path: ":material") implementation project(path: ":preference") - implementation project(path: ":retrofit") implementation project(path: ":camera2") implementation("com.squareup.okhttp3:okhttp-android:$okhttpLibraryVersion") //implementation("com.squareup.okhttp3:logging-interceptor:$okhttpLibraryVersion") diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/App.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/App.kt index a3c8dd63a..6454a144a 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/App.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/App.kt @@ -101,6 +101,7 @@ class App : Application() { }, RxUtils.ignore()) ) RxJavaPlugins.setErrorHandler { + it.printStackTrace() Handler(mainLooper).post { if (Settings.get().other().isDeveloper_mode) { createCustomToast(this).showToastError( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IOtherVkRestProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IOtherVkRestProvider.kt new file mode 100644 index 000000000..e3b608dd7 --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IOtherVkRestProvider.kt @@ -0,0 +1,11 @@ +package dev.ragnarok.fenrir.api + +import dev.ragnarok.fenrir.api.rest.SimplePostHttp +import io.reactivex.rxjava3.core.Single + +interface IOtherVkRestProvider { + fun provideAuthRest(): Single + fun provideAuthServiceRest(): Single + fun provideLongpollRest(): Single + fun provideLocalServerRest(): Single +} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IOtherVkRetrofitProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IOtherVkRetrofitProvider.kt deleted file mode 100644 index 2f78ae166..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IOtherVkRetrofitProvider.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.ragnarok.fenrir.api - -import io.reactivex.rxjava3.core.Single - -interface IOtherVkRetrofitProvider { - fun provideAuthRetrofit(): Single - fun provideAuthServiceRetrofit(): Single - fun provideLongpollRetrofit(): Single - fun provideLocalServerRetrofit(): Single -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IServiceProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IServiceProvider.kt index 688aefa29..5820f8234 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IServiceProvider.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IServiceProvider.kt @@ -1,11 +1,12 @@ package dev.ragnarok.fenrir.api +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single interface IServiceProvider { - fun provideService( + fun provideService( accountId: Int, - serviceClass: Class, + serviceClass: T, vararg tokenTypes: Int ): Single } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IUploadRestProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IUploadRestProvider.kt new file mode 100644 index 000000000..4224f7646 --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IUploadRestProvider.kt @@ -0,0 +1,8 @@ +package dev.ragnarok.fenrir.api + +import dev.ragnarok.fenrir.api.rest.SimplePostHttp +import io.reactivex.rxjava3.core.Single + +interface IUploadRestProvider { + fun provideUploadRest(): Single +} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IUploadRetrofitProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IUploadRetrofitProvider.kt deleted file mode 100644 index 33ec832e4..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IUploadRetrofitProvider.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.ragnarok.fenrir.api - -import io.reactivex.rxjava3.core.Single - -interface IUploadRetrofitProvider { - fun provideUploadRetrofit(): Single -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkMethodHttpClientFactory.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkMethodHttpClientFactory.kt index d23647f73..309dcfb8c 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkMethodHttpClientFactory.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkMethodHttpClientFactory.kt @@ -5,13 +5,16 @@ import dev.ragnarok.fenrir.model.ProxyConfig import okhttp3.OkHttpClient interface IVkMethodHttpClientFactory { - fun createDefaultVkHttpClient(accountId: Int, config: ProxyConfig?): OkHttpClient + fun createDefaultVkHttpClient(accountId: Int, config: ProxyConfig?): OkHttpClient.Builder fun createCustomVkHttpClient( accountId: Int, token: String, config: ProxyConfig? - ): OkHttpClient + ): OkHttpClient.Builder - fun createServiceVkHttpClient(config: ProxyConfig?): OkHttpClient - fun createRawVkApiOkHttpClient(@AccountType type: Int, config: ProxyConfig?): OkHttpClient + fun createServiceVkHttpClient(config: ProxyConfig?): OkHttpClient.Builder + fun createRawVkApiOkHttpClient( + @AccountType type: Int, + config: ProxyConfig? + ): OkHttpClient.Builder } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkRestProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkRestProvider.kt new file mode 100644 index 000000000..85aed4d31 --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkRestProvider.kt @@ -0,0 +1,14 @@ +package dev.ragnarok.fenrir.api + +import dev.ragnarok.fenrir.AccountType +import dev.ragnarok.fenrir.api.rest.SimplePostHttp +import io.reactivex.rxjava3.core.Single +import okhttp3.OkHttpClient + +interface IVkRestProvider { + fun provideNormalRest(accountId: Int): Single + fun provideCustomRest(accountId: Int, token: String): Single + fun provideServiceRest(): Single + fun provideNormalHttpClient(accountId: Int): Single + fun provideRawHttpClient(@AccountType type: Int): Single +} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkRetrofitProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkRetrofitProvider.kt deleted file mode 100644 index 68a73e3a7..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/IVkRetrofitProvider.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.ragnarok.fenrir.api - -import dev.ragnarok.fenrir.AccountType -import io.reactivex.rxjava3.core.Single -import okhttp3.OkHttpClient - -interface IVkRetrofitProvider { - fun provideNormalRetrofit(accountId: Int): Single - fun provideCustomRetrofit(accountId: Int, token: String): Single - fun provideServiceRetrofit(): Single - fun provideNormalHttpClient(accountId: Int): Single - fun provideRawHttpClient(@AccountType type: Int): Single -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/OtherVkRetrofitProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/OtherVkRestProvider.kt similarity index 61% rename from app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/OtherVkRetrofitProvider.kt rename to app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/OtherVkRestProvider.kt index 1ccdfba7d..f8c516128 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/OtherVkRetrofitProvider.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/OtherVkRestProvider.kt @@ -6,45 +6,40 @@ import dev.ragnarok.fenrir.Constants import dev.ragnarok.fenrir.Constants.USER_AGENT import dev.ragnarok.fenrir.api.HttpLoggerAndParser.toRequestBuilder import dev.ragnarok.fenrir.api.HttpLoggerAndParser.vkHeader -import dev.ragnarok.fenrir.api.RetrofitWrapper.Companion.wrap -import dev.ragnarok.fenrir.kJson +import dev.ragnarok.fenrir.api.rest.SimplePostHttp import dev.ragnarok.fenrir.nonNullNoEmpty import dev.ragnarok.fenrir.settings.IProxySettings import dev.ragnarok.fenrir.settings.Settings import dev.ragnarok.fenrir.util.UncompressDefaultInterceptor import dev.ragnarok.fenrir.util.Utils -import dev.ragnarok.fenrir.util.serializeble.msgpack.MsgPack -import dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization.jsonMsgPackConverterFactory -import dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3.RxJava3CallAdapterFactory import io.reactivex.rxjava3.core.Single import okhttp3.FormBody import okhttp3.Interceptor import okhttp3.OkHttpClient -import retrofit2.Retrofit import java.util.concurrent.TimeUnit -class OtherVkRetrofitProvider @SuppressLint("CheckResult") constructor(private val proxySettings: IProxySettings) : - IOtherVkRetrofitProvider { - private val longpollRetrofitLock = Any() - private val localServerRetrofitLock = Any() - private var longpollRetrofitInstance: RetrofitWrapper? = null - private var localServerRetrofitInstance: RetrofitWrapper? = null +class OtherVkRestProvider @SuppressLint("CheckResult") constructor(private val proxySettings: IProxySettings) : + IOtherVkRestProvider { + private val longpollRestLock = Any() + private val localServerRestLock = Any() + private var longpollRestInstance: SimplePostHttp? = null + private var localServerRestInstance: SimplePostHttp? = null private fun onProxySettingsChanged() { - synchronized(longpollRetrofitLock) { - if (longpollRetrofitInstance != null) { - longpollRetrofitInstance?.cleanup() - longpollRetrofitInstance = null + synchronized(longpollRestLock) { + if (longpollRestInstance != null) { + longpollRestInstance?.stop() + longpollRestInstance = null } } - synchronized(localServerRetrofitLock) { - if (localServerRetrofitInstance != null) { - localServerRetrofitInstance?.cleanup() - localServerRetrofitInstance = null + synchronized(localServerRestLock) { + if (localServerRestInstance != null) { + localServerRestInstance?.stop() + localServerRestInstance = null } } } - override fun provideAuthRetrofit(): Single { + override fun provideAuthRest(): Single { return Single.fromCallable { val builder: OkHttpClient.Builder = OkHttpClient.Builder() .readTimeout(15, TimeUnit.SECONDS) @@ -65,17 +60,11 @@ class OtherVkRetrofitProvider @SuppressLint("CheckResult") constructor(private v ProxyUtil.applyProxyConfig(builder, proxySettings.activeProxy) HttpLoggerAndParser.adjust(builder) HttpLoggerAndParser.configureToIgnoreCertificates(builder) - val retrofit = Retrofit.Builder() - .baseUrl("https://" + Settings.get().other().get_Auth_Domain() + "/") - .addConverterFactory(KCONVERTER_FACTORY) - .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) - .client(builder.build()) - .build() - wrap(retrofit, false) + SimplePostHttp("https://" + Settings.get().other().get_Auth_Domain(), builder) } } - override fun provideAuthServiceRetrofit(): Single { + override fun provideAuthServiceRest(): Single { return Single.fromCallable { val builder: OkHttpClient.Builder = OkHttpClient.Builder() .readTimeout(15, TimeUnit.SECONDS) @@ -96,17 +85,14 @@ class OtherVkRetrofitProvider @SuppressLint("CheckResult") constructor(private v ProxyUtil.applyProxyConfig(builder, proxySettings.activeProxy) HttpLoggerAndParser.adjust(builder) HttpLoggerAndParser.configureToIgnoreCertificates(builder) - val retrofit = Retrofit.Builder() - .baseUrl("https://" + Settings.get().other().get_Api_Domain() + "/method/") - .addConverterFactory(KCONVERTER_FACTORY) - .addCallAdapterFactory(RX_ADAPTER_FACTORY) - .client(builder.build()) - .build() - wrap(retrofit, false) + SimplePostHttp( + "https://" + Settings.get().other().get_Api_Domain() + "/method", + builder + ) } } - private fun createLocalServerRetrofit(): Retrofit { + private fun createLocalServerRest(): SimplePostHttp { val localSettings = Settings.get().other().localServer val builder: OkHttpClient.Builder = OkHttpClient.Builder() .readTimeout(15, TimeUnit.SECONDS) @@ -141,17 +127,10 @@ class OtherVkRetrofitProvider @SuppressLint("CheckResult") constructor(private v HttpLoggerAndParser.adjust(builder) HttpLoggerAndParser.configureToIgnoreCertificates(builder) val url = Utils.firstNonEmptyString(localSettings.url, "https://debug.dev")!! - return Retrofit.Builder() - .baseUrl("$url/method/") - .addConverterFactory( - KCONVERTER_FACTORY - ) - .addCallAdapterFactory(RX_ADAPTER_FACTORY) - .client(builder.build()) - .build() + return SimplePostHttp("$url/method", builder) } - private fun createLongpollRetrofitInstance(): Retrofit { + private fun createLongpollRestInstance(): SimplePostHttp { val builder: OkHttpClient.Builder = OkHttpClient.Builder() .readTimeout(25, TimeUnit.SECONDS) .connectTimeout(25, TimeUnit.SECONDS) @@ -170,45 +149,38 @@ class OtherVkRetrofitProvider @SuppressLint("CheckResult") constructor(private v ProxyUtil.applyProxyConfig(builder, proxySettings.activeProxy) HttpLoggerAndParser.adjust(builder) HttpLoggerAndParser.configureToIgnoreCertificates(builder) - return Retrofit.Builder() - .baseUrl("https://" + Settings.get().other().get_Api_Domain() + "/method/") // dummy - .addConverterFactory(KCONVERTER_FACTORY) - .addCallAdapterFactory(RX_ADAPTER_FACTORY) - .client(builder.build()) - .build() + return SimplePostHttp( + "https://" + Settings.get().other().get_Api_Domain() + "/method", + builder + ) } - override fun provideLocalServerRetrofit(): Single { + override fun provideLocalServerRest(): Single { return Single.fromCallable { - if (localServerRetrofitInstance == null) { - synchronized(localServerRetrofitLock) { - if (localServerRetrofitInstance == null) { - localServerRetrofitInstance = wrap(createLocalServerRetrofit()) + if (localServerRestInstance == null) { + synchronized(localServerRestLock) { + if (localServerRestInstance == null) { + localServerRestInstance = createLocalServerRest() } } } - localServerRetrofitInstance!! + localServerRestInstance!! } } - override fun provideLongpollRetrofit(): Single { + override fun provideLongpollRest(): Single { return Single.fromCallable { - if (longpollRetrofitInstance == null) { - synchronized(longpollRetrofitLock) { - if (longpollRetrofitInstance == null) { - longpollRetrofitInstance = wrap(createLongpollRetrofitInstance()) + if (longpollRestInstance == null) { + synchronized(longpollRestLock) { + if (longpollRestInstance == null) { + longpollRestInstance = createLongpollRestInstance() } } } - longpollRetrofitInstance!! + longpollRestInstance!! } } - companion object { - private val KCONVERTER_FACTORY = jsonMsgPackConverterFactory(kJson, MsgPack()) - private val RX_ADAPTER_FACTORY = RxJava3CallAdapterFactory.create() - } - init { proxySettings.observeActive() .subscribe { onProxySettingsChanged() } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/RetrofitWrapper.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/RetrofitWrapper.kt deleted file mode 100644 index b1e7721ae..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/RetrofitWrapper.kt +++ /dev/null @@ -1,40 +0,0 @@ -package dev.ragnarok.fenrir.api - -import retrofit2.Retrofit -import java.util.* - -class RetrofitWrapper private constructor( - private val retrofit: Retrofit, - private val withCaching: Boolean -) { - private val servicesCache: MutableMap, Any?>? = - if (withCaching) Collections.synchronizedMap(HashMap(4)) else null - - @Suppress("UNCHECKED_CAST") - fun create(serviceClass: Class): T { - if (!withCaching || servicesCache == null) { - return retrofit.create(serviceClass) - } - if (servicesCache.containsKey(serviceClass)) { - return servicesCache[serviceClass] as T - } - val service = retrofit.create(serviceClass) - servicesCache[serviceClass] = service - return service - } - - fun cleanup() { - servicesCache?.clear() - } - - companion object { - fun wrap(retrofit: Retrofit): RetrofitWrapper { - return RetrofitWrapper(retrofit, true) - } - - fun wrap(retrofit: Retrofit, withCaching: Boolean): RetrofitWrapper { - return RetrofitWrapper(retrofit, withCaching) - } - } - -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/UploadRetrofitProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/UploadRestProvider.kt similarity index 50% rename from app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/UploadRetrofitProvider.kt rename to app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/UploadRestProvider.kt index 429884c43..70ea5049a 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/UploadRetrofitProvider.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/UploadRestProvider.kt @@ -5,48 +5,43 @@ import dev.ragnarok.fenrir.AccountType import dev.ragnarok.fenrir.Constants.USER_AGENT import dev.ragnarok.fenrir.api.HttpLoggerAndParser.toRequestBuilder import dev.ragnarok.fenrir.api.HttpLoggerAndParser.vkHeader -import dev.ragnarok.fenrir.api.RetrofitWrapper.Companion.wrap -import dev.ragnarok.fenrir.kJson +import dev.ragnarok.fenrir.api.rest.SimplePostHttp import dev.ragnarok.fenrir.settings.IProxySettings import dev.ragnarok.fenrir.settings.Settings -import dev.ragnarok.fenrir.util.serializeble.msgpack.MsgPack -import dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization.jsonMsgPackConverterFactory -import dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3.RxJava3CallAdapterFactory import io.reactivex.rxjava3.core.Single import okhttp3.Interceptor import okhttp3.OkHttpClient -import retrofit2.Retrofit import java.util.concurrent.TimeUnit @SuppressLint("CheckResult") -class UploadRetrofitProvider(private val proxySettings: IProxySettings) : IUploadRetrofitProvider { - private val uploadRetrofitLock = Any() +class UploadRestProvider(private val proxySettings: IProxySettings) : IUploadRestProvider { + private val uploadRestLock = Any() @Volatile - private var uploadRetrofitInstance: RetrofitWrapper? = null + private var uploadRestInstance: SimplePostHttp? = null private fun onProxySettingsChanged() { - synchronized(uploadRetrofitLock) { - if (uploadRetrofitInstance != null) { - uploadRetrofitInstance?.cleanup() - uploadRetrofitInstance = null + synchronized(uploadRestLock) { + if (uploadRestInstance != null) { + uploadRestInstance?.stop() + uploadRestInstance = null } } } - override fun provideUploadRetrofit(): Single { + override fun provideUploadRest(): Single { return Single.fromCallable { - if (uploadRetrofitInstance == null) { - synchronized(uploadRetrofitLock) { - if (uploadRetrofitInstance == null) { - uploadRetrofitInstance = wrap(createUploadRetrofit(), true) + if (uploadRestInstance == null) { + synchronized(uploadRestLock) { + if (uploadRestInstance == null) { + uploadRestInstance = createUploadRest() } } } - uploadRetrofitInstance!! + uploadRestInstance!! } } - private fun createUploadRetrofit(): Retrofit { + private fun createUploadRest(): SimplePostHttp { val builder: OkHttpClient.Builder = OkHttpClient.Builder() .readTimeout(15, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS) @@ -64,17 +59,10 @@ class UploadRetrofitProvider(private val proxySettings: IProxySettings) : IUploa ProxyUtil.applyProxyConfig(builder, proxySettings.activeProxy) HttpLoggerAndParser.adjustUpload(builder) HttpLoggerAndParser.configureToIgnoreCertificates(builder) - return Retrofit.Builder() - .baseUrl("https://" + Settings.get().other().get_Api_Domain() + "/method/") // dummy - .addConverterFactory(KCONVERTER_FACTORY) - .addCallAdapterFactory(RX_ADAPTER_FACTORY) - .client(builder.build()) - .build() - } - - companion object { - private val KCONVERTER_FACTORY = jsonMsgPackConverterFactory(kJson, MsgPack()) - private val RX_ADAPTER_FACTORY = RxJava3CallAdapterFactory.create() + return SimplePostHttp( + "https://" + Settings.get().other().get_Api_Domain() + "/method", + builder + ) } init { diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkMethodHttpClientFactory.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkMethodHttpClientFactory.kt index 00f1c7b69..7c425a56e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkMethodHttpClientFactory.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkMethodHttpClientFactory.kt @@ -15,7 +15,7 @@ class VkMethodHttpClientFactory : IVkMethodHttpClientFactory { override fun createDefaultVkHttpClient( accountId: Int, config: ProxyConfig? - ): OkHttpClient { + ): OkHttpClient.Builder { return createDefaultVkApiOkHttpClient( DefaultVkApiInterceptor( accountId, @@ -28,7 +28,7 @@ class VkMethodHttpClientFactory : IVkMethodHttpClientFactory { accountId: Int, token: String, config: ProxyConfig? - ): OkHttpClient { + ): OkHttpClient.Builder { return createDefaultVkApiOkHttpClient( CustomTokenVkApiInterceptor( token, @@ -39,7 +39,7 @@ class VkMethodHttpClientFactory : IVkMethodHttpClientFactory { ) } - override fun createServiceVkHttpClient(config: ProxyConfig?): OkHttpClient { + override fun createServiceVkHttpClient(config: ProxyConfig?): OkHttpClient.Builder { return createDefaultVkApiOkHttpClient( CustomTokenVkApiInterceptor( BuildConfig.SERVICE_TOKEN, @@ -53,7 +53,7 @@ class VkMethodHttpClientFactory : IVkMethodHttpClientFactory { private fun createDefaultVkApiOkHttpClient( interceptor: AbsVkApiInterceptor, config: ProxyConfig? - ): OkHttpClient { + ): OkHttpClient.Builder { val builder: OkHttpClient.Builder = OkHttpClient.Builder() .addInterceptor(interceptor) .readTimeout(15, TimeUnit.SECONDS) @@ -68,13 +68,13 @@ class VkMethodHttpClientFactory : IVkMethodHttpClientFactory { ProxyUtil.applyProxyConfig(builder, config) HttpLoggerAndParser.adjust(builder) HttpLoggerAndParser.configureToIgnoreCertificates(builder) - return builder.build() + return builder } override fun createRawVkApiOkHttpClient( @AccountType type: Int, config: ProxyConfig? - ): OkHttpClient { + ): OkHttpClient.Builder { val builder: OkHttpClient.Builder = OkHttpClient.Builder() .readTimeout(15, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS) @@ -88,6 +88,6 @@ class VkMethodHttpClientFactory : IVkMethodHttpClientFactory { ProxyUtil.applyProxyConfig(builder, config) HttpLoggerAndParser.adjust(builder) HttpLoggerAndParser.configureToIgnoreCertificates(builder) - return builder.build() + return builder } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkRestProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkRestProvider.kt new file mode 100644 index 000000000..59f4b8fab --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkRestProvider.kt @@ -0,0 +1,107 @@ +package dev.ragnarok.fenrir.api + +import android.annotation.SuppressLint +import dev.ragnarok.fenrir.AccountType +import dev.ragnarok.fenrir.api.rest.SimplePostHttp +import dev.ragnarok.fenrir.settings.IProxySettings +import dev.ragnarok.fenrir.settings.Settings +import io.reactivex.rxjava3.core.Single +import okhttp3.OkHttpClient +import java.util.* + +@SuppressLint("CheckResult") +class VkRestProvider( + private val proxyManager: IProxySettings, + private val clientFactory: IVkMethodHttpClientFactory +) : IVkRestProvider { + private val restCacheLock = Any() + private val serviceRestLock = Any() + + private val restCache = Collections.synchronizedMap(HashMap(1)) + + @Volatile + private var serviceRest: SimplePostHttp? = null + private fun onProxySettingsChanged() { + synchronized(restCacheLock) { + for ((_, value) in restCache) { + value?.stop() + } + restCache.clear() + } + } + + override fun provideNormalRest(accountId: Int): Single { + return Single.fromCallable { + var rest: SimplePostHttp? + synchronized(restCacheLock) { + restCache[accountId]?.let { + return@fromCallable it + } + val client = clientFactory.createDefaultVkHttpClient( + accountId, + proxyManager.activeProxy + ) + rest = createDefaultVkApiRest(client) + restCache.put(accountId, rest) + } + rest!! + } + } + + override fun provideCustomRest(accountId: Int, token: String): Single { + return Single.fromCallable { + val client = clientFactory.createCustomVkHttpClient( + accountId, + token, + proxyManager.activeProxy + ) + createDefaultVkApiRest(client) + } + } + + override fun provideServiceRest(): Single { + return Single.fromCallable { + if (serviceRest == null) { + synchronized(serviceRestLock) { + if (serviceRest == null) { + val client = clientFactory.createServiceVkHttpClient( + proxyManager.activeProxy + ) + serviceRest = createDefaultVkApiRest(client) + } + } + } + serviceRest!! + } + } + + override fun provideNormalHttpClient(accountId: Int): Single { + return Single.fromCallable { + clientFactory.createDefaultVkHttpClient( + accountId, + proxyManager.activeProxy + ) + } + } + + override fun provideRawHttpClient(@AccountType type: Int): Single { + return Single.fromCallable { + clientFactory.createRawVkApiOkHttpClient( + type, + proxyManager.activeProxy + ) + } + } + + private fun createDefaultVkApiRest(okHttpClient: OkHttpClient.Builder): SimplePostHttp { + return SimplePostHttp( + "https://" + Settings.get().other().get_Api_Domain() + "/method", + okHttpClient + ) + } + + init { + proxyManager.observeActive() + .subscribe { onProxySettingsChanged() } + } +} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkRetrofitProvider.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkRetrofitProvider.kt deleted file mode 100644 index 8dc362027..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/VkRetrofitProvider.kt +++ /dev/null @@ -1,121 +0,0 @@ -package dev.ragnarok.fenrir.api - -import android.annotation.SuppressLint -import dev.ragnarok.fenrir.AccountType -import dev.ragnarok.fenrir.api.RetrofitWrapper.Companion.wrap -import dev.ragnarok.fenrir.kJson -import dev.ragnarok.fenrir.settings.IProxySettings -import dev.ragnarok.fenrir.settings.Settings -import dev.ragnarok.fenrir.util.serializeble.msgpack.MsgPack -import dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization.jsonMsgPackConverterFactory -import dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3.RxJava3CallAdapterFactory -import io.reactivex.rxjava3.core.Single -import okhttp3.OkHttpClient -import retrofit2.Retrofit -import java.util.* - -@SuppressLint("CheckResult") -class VkRetrofitProvider( - private val proxyManager: IProxySettings, - private val clientFactory: IVkMethodHttpClientFactory -) : IVkRetrofitProvider { - private val retrofitCacheLock = Any() - private val serviceRetrofitLock = Any() - - private val retrofitCache = Collections.synchronizedMap(HashMap(1)) - - @Volatile - private var serviceRetrofit: RetrofitWrapper? = null - private fun onProxySettingsChanged() { - synchronized(retrofitCacheLock) { - for ((_, value) in retrofitCache) { - value?.cleanup() - } - retrofitCache.clear() - } - } - - override fun provideNormalRetrofit(accountId: Int): Single { - return Single.fromCallable { - var retrofit: RetrofitWrapper? - synchronized(retrofitCacheLock) { - retrofitCache[accountId]?.let { - return@fromCallable it - } - val client = clientFactory.createDefaultVkHttpClient( - accountId, - proxyManager.activeProxy - ) - retrofit = createDefaultVkApiRetrofit(client) - retrofitCache.put(accountId, retrofit) - } - retrofit!! - } - } - - override fun provideCustomRetrofit(accountId: Int, token: String): Single { - return Single.fromCallable { - val client = clientFactory.createCustomVkHttpClient( - accountId, - token, - proxyManager.activeProxy - ) - createDefaultVkApiRetrofit(client) - } - } - - override fun provideServiceRetrofit(): Single { - return Single.fromCallable { - if (serviceRetrofit == null) { - synchronized(serviceRetrofitLock) { - if (serviceRetrofit == null) { - val client = clientFactory.createServiceVkHttpClient( - proxyManager.activeProxy - ) - serviceRetrofit = createDefaultVkApiRetrofit(client) - } - } - } - serviceRetrofit!! - } - } - - override fun provideNormalHttpClient(accountId: Int): Single { - return Single.fromCallable { - clientFactory.createDefaultVkHttpClient( - accountId, - proxyManager.activeProxy - ) - } - } - - override fun provideRawHttpClient(@AccountType type: Int): Single { - return Single.fromCallable { - clientFactory.createRawVkApiOkHttpClient( - type, - proxyManager.activeProxy - ) - } - } - - private fun createDefaultVkApiRetrofit(okHttpClient: OkHttpClient): RetrofitWrapper { - return wrap( - Retrofit.Builder() - .baseUrl("https://" + Settings.get().other().get_Api_Domain() + "/method/") - .addConverterFactory(KCONVERTER_FACTORY) - .addCallAdapterFactory(RX_ADAPTER_FACTORY) - .client(okHttpClient) - .build() - ) - } - - companion object { - private val KCONVERTER_FACTORY = jsonMsgPackConverterFactory(kJson, MsgPack()) - private val RX_ADAPTER_FACTORY = RxJava3CallAdapterFactory.create() - } - - init { - proxyManager.observeActive() - .subscribe { onProxySettingsChanged() } - } -} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AbsApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AbsApi.kt index adfce6b27..0b7471ef9 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AbsApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AbsApi.kt @@ -9,42 +9,42 @@ import dev.ragnarok.fenrir.api.model.IAttachmentToken import dev.ragnarok.fenrir.api.model.Params import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.VkResponse +import dev.ragnarok.fenrir.api.rest.HttpException +import dev.ragnarok.fenrir.api.rest.IServiceRest import dev.ragnarok.fenrir.service.ApiErrorCodes import dev.ragnarok.fenrir.settings.Settings import dev.ragnarok.fenrir.util.refresh.RefreshToken import dev.ragnarok.fenrir.util.serializeble.json.decodeFromStream import dev.ragnarok.fenrir.util.serializeble.msgpack.MsgPack -import dev.ragnarok.fenrir.util.serializeble.retrofit.HttpCodeException -import dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization.Serializer import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.SingleEmitter import io.reactivex.rxjava3.exceptions.Exceptions import io.reactivex.rxjava3.functions.Function +import kotlinx.serialization.KSerializer import okhttp3.* -import java.lang.reflect.Type import java.util.* -internal open class AbsApi(val accountId: Int, private val retrofitProvider: IServiceProvider) { - fun provideService(serviceClass: Class, vararg tokenTypes: Int): Single { +internal open class AbsApi(val accountId: Int, private val restProvider: IServiceProvider) { + fun provideService(serviceClass: T, vararg tokenTypes: Int): Single { var pTokenTypes: IntArray = tokenTypes if (pTokenTypes.nullOrEmpty()) { pTokenTypes = intArrayOf(TokenType.USER) // user by default } - return retrofitProvider.provideService(accountId, serviceClass, *pTokenTypes) + return restProvider.provideService(accountId, serviceClass, *pTokenTypes) } @Suppress("unchecked_cast") private fun rawVKRequest( method: String, postParams: Map, - javaClass: Type, serializerType: Serializer + serializerType: KSerializer<*> ): Single> { val bodyBuilder = FormBody.Builder() for ((key, value) in postParams) { bodyBuilder.add(key, value) } - return Includes.networkInterfaces.getVkRetrofitProvider().provideNormalHttpClient(accountId) + return Includes.networkInterfaces.getVkRestProvider().provideNormalHttpClient(accountId) .flatMap { client -> Single.create { emitter: SingleEmitter -> val request: Request = Request.Builder() @@ -53,35 +53,29 @@ internal open class AbsApi(val accountId: Int, private val retrofitProvider: ISe ) .post(bodyBuilder.build()) .build() - val call = client.newCall(request) + val call = client.build().newCall(request) emitter.setCancellable { call.cancel() } try { val response = call.execute() if (!response.isSuccessful) { - emitter.onError(HttpCodeException(response.code)) + emitter.tryOnError(HttpException(response.code)) } else { emitter.onSuccess(response) } response.close() } catch (e: Exception) { - emitter.onError(e) + emitter.tryOnError(e) } } } .map { response -> val k = if (response.body.isMsgPack()) MsgPack().decodeFromOkioStream( - serializerType.serializer( - javaClass - ), response.body.source() + serializerType, response.body.source() ) as BaseResponse else kJson.decodeFromStream( - serializerType.serializer( - javaClass - ), response.body.byteStream() + serializerType, response.body.byteStream() ) as BaseResponse k.error?.let { - it.type = javaClass it.serializer = serializerType - val o = ArrayList() for ((key, value) in postParams) { val tmp = Params() @@ -107,7 +101,7 @@ internal open class AbsApi(val accountId: Int, private val retrofitProvider: ISe for ((key, value) in postParams) { bodyBuilder.add(key, value) } - return Includes.networkInterfaces.getVkRetrofitProvider().provideNormalHttpClient(accountId) + return Includes.networkInterfaces.getVkRestProvider().provideNormalHttpClient(accountId) .flatMap { client -> Single.create { emitter: SingleEmitter -> val request: Request = Request.Builder() @@ -116,18 +110,18 @@ internal open class AbsApi(val accountId: Int, private val retrofitProvider: ISe ) .post(bodyBuilder.build()) .build() - val call = client.newCall(request) + val call = client.build().newCall(request) emitter.setCancellable { call.cancel() } try { val response = call.execute() if (!response.isSuccessful) { - emitter.onError(HttpCodeException(response.code)) + emitter.tryOnError(HttpException(response.code)) } else { emitter.onSuccess(response) } response.close() } catch (e: Exception) { - emitter.onError(e) + emitter.tryOnError(e) } } } @@ -229,7 +223,6 @@ internal open class AbsApi(val accountId: Int, private val retrofitProvider: ISe return@Function rawVKRequest( method, params, - it.type ?: throw UnsupportedOperationException(), it.serializer ?: throw UnsupportedOperationException() ) .map(extractResponseWithErrorHandling()) @@ -301,19 +294,16 @@ internal open class AbsApi(val accountId: Int, private val retrofitProvider: ISe return sb.toString() } - fun formatAttachmentToken(token: IAttachmentToken): String { return token.format() } - fun toQuotes(word: String?): String? { return if (word == null) { null } else "\"" + word + "\"" } - fun integerFromBoolean(value: Boolean?): Int? { return if (value == null) null else if (value) 1 else 0 } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AccountApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AccountApi.kt index eca885dff..ad51da362 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AccountApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AccountApi.kt @@ -14,7 +14,7 @@ import io.reactivex.rxjava3.core.Single internal class AccountApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), IAccountApi { override fun ban(ownerId: Int): Single { - return provideService(IAccountService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IAccountService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .ban(ownerId) @@ -23,7 +23,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : } override fun unban(ownerId: Int): Single { - return provideService(IAccountService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IAccountService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .unban(ownerId) @@ -36,7 +36,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : offset: Int?, fields: String? ): Single { - return provideService(IAccountService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IAccountService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .getBanned(count, offset, fields) @@ -45,7 +45,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : } override fun unregisterDevice(deviceId: String?): Single { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMap { service -> service.unregisterDevice(deviceId) .map(extractResponseWithErrorHandling()) @@ -65,7 +65,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : systemVersion: String?, settings: String? ): Single { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMap { service -> service .registerDevice( @@ -86,24 +86,24 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : } override fun setOffline(): Single { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMap { service -> service - .setOffline() + .setOffline .map(extractResponseWithErrorHandling()) .map { it == 1 } } } override val profileInfo: Single - get() = provideService(IAccountService::class.java, TokenType.USER) + get() = provideService(IAccountService(), TokenType.USER) .flatMap { service -> service .profileInfo .map(extractResponseWithErrorHandling()) } override val pushSettings: Single - get() = provideService(IAccountService::class.java, TokenType.USER) + get() = provideService(IAccountService(), TokenType.USER) .flatMap { service -> service .pushSettings @@ -119,7 +119,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : home_town: String?, sex: Int? ): Single { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMap { service -> service .saveProfileInfo( @@ -136,7 +136,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : } override fun getCounters(filter: String?): Single { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMap { service -> service .getCounters(filter) @@ -150,7 +150,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : nonce: String?, timestamp: Long? ): Single { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMap { service -> service .refreshToken(receipt, receipt2, nonce, timestamp) @@ -159,7 +159,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : } override fun importMessagesContacts(contacts: String?): Completable { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMapCompletable { service -> service .importMessagesContacts(contacts) @@ -168,7 +168,7 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : } override fun getContactList(offset: Int?, count: Int?): Single { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMap { service -> service .getContactList(offset, count, 1, VKApiUser.ALL_FIELDS) @@ -177,10 +177,10 @@ internal class AccountApi(accountId: Int, provider: IServiceProvider) : } override fun resetMessagesContacts(): Completable { - return provideService(IAccountService::class.java, TokenType.USER) + return provideService(IAccountService(), TokenType.USER) .flatMapCompletable { service -> service - .resetMessagesContacts() + .resetMessagesContacts .flatMapCompletable(checkResponseWithErrorHandling()) } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AudioApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AudioApi.kt index b0995e9c1..cf11d5ee1 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AudioApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AudioApi.kt @@ -20,7 +20,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : override fun setBroadcast(audio: AccessIdPair, targetIds: Collection): Single> { val f = join(setOf(audio), ",") { AccessIdPair.format(it) } val s = join(targetIds, ",") - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .setBroadcast(f, s) @@ -38,7 +38,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .search( @@ -60,7 +60,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .searchArtists(query, offset, count) @@ -73,7 +73,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .searchPlaylists(query, offset, count) @@ -82,7 +82,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun restore(audioId: Int, ownerId: Int?): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .restore(audioId, ownerId) @@ -91,7 +91,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun delete(audioId: Int, ownerId: Int): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .delete(audioId, ownerId) @@ -113,7 +113,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : title: String?, text: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .edit(ownerId, audioId, artist, title, text) @@ -122,7 +122,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun add(audioId: Int, ownerId: Int, groupId: Int?, accessKey: String?): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .add(audioId, ownerId, groupId, accessKey) @@ -135,7 +135,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : title: String?, description: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .createPlaylist(ownerId, title, description) @@ -149,7 +149,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : title: String?, description: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .editPlaylist(ownerId, playlist_id, title, description) @@ -162,7 +162,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : playlist_id: Int, audio_ids: Collection ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .removeFromPlaylist( @@ -178,7 +178,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : playlist_id: Int, audio_ids: Collection ): Single> { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .addToPlaylist( @@ -190,7 +190,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun reorder(ownerId: Int, audio_id: Int, before: Int?, after: Int?): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .reorder(ownerId, audio_id, before, after) @@ -199,7 +199,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun trackEvents(events: String?): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .trackEvents(events) @@ -210,7 +210,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : override fun getCatalogV2Sections( owner_id: Int, artist_id: String?, url: String?, query: String?, context: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> (if (artist_id.nonNullNoEmpty()) service.getCatalogV2Artist( artist_id, @@ -229,7 +229,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : override fun getCatalogV2BlockItems( block_id: String, start_from: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getCatalogV2BlockItems(block_id, start_from) @@ -241,7 +241,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : section_id: String, start_from: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service.getCatalogV2Section(section_id, start_from) .map(extractResponseWithErrorHandling()) @@ -249,7 +249,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun deletePlaylist(playlist_id: Int, ownerId: Int): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .deletePlaylist(playlist_id, ownerId) @@ -262,7 +262,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : ownerId: Int, accessKey: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .followPlaylist(playlist_id, ownerId, accessKey) @@ -271,7 +271,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun clonePlaylist(playlist_id: Int, ownerId: Int): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .clonePlaylist(playlist_id, ownerId) @@ -284,7 +284,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : ownerId: Int, accessKey: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getPlaylistById(playlist_id, ownerId, accessKey) @@ -299,7 +299,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : count: Int?, accessKey: String? ): Single> { - return provideService(IAudioService::class.java).flatMap { service -> + return provideService(IAudioService()).flatMap { service -> service[playlist_id, ownerId, offset, count, accessKey].map( extractResponseWithErrorHandling() ) @@ -311,7 +311,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IAudioService::class.java).flatMap { service -> + return provideService(IAudioService()).flatMap { service -> service.getAudiosByArtist(artist_id, offset, count).map( extractResponseWithErrorHandling() ) @@ -322,7 +322,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : foreign: Int?, genre: Int?, count: Int? ): Single> { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getPopular(foreign, genre, count) @@ -331,7 +331,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun getRecommendations(audioOwnerId: Int?, count: Int?): Single> { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getRecommendations(audioOwnerId, count) @@ -340,7 +340,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun getRecommendationsByAudio(audio: String?, count: Int?): Single> { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getRecommendationsByAudio(audio, count) @@ -353,7 +353,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : offset: Int, count: Int ): Single> { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getPlaylists(owner_id, offset, count) @@ -362,7 +362,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun getPlaylistsCustom(code: String?): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getPlaylistsCustom(code) @@ -375,7 +375,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : ids.add(AccessIdPair(i.id, i.ownerId, i.accessKey)) } val audio_string = join(ids, ",") { AccessIdPair.format(it) } - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getById(audio_string) @@ -389,7 +389,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : ids.add(AccessIdPair(i.id, i.ownerId, i.accessKey)) } val audio_string = join(ids, ",") { AccessIdPair.format(it) } - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getByIdVersioned(audio_string, "5.90") @@ -398,7 +398,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override fun getLyrics(lyrics_id: Int): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getLyrics(lyrics_id) @@ -407,7 +407,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : } override val uploadServer: Single - get() = provideService(IAudioService::class.java) + get() = provideService(IAudioService()) .flatMap { service -> service.uploadServer .map(extractResponseWithErrorHandling()) @@ -420,7 +420,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : artist: String?, title: String? ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service.save(server, audio, hash, artist, title) .map(extractResponseWithErrorHandling()) @@ -430,7 +430,7 @@ internal class AudioApi(accountId: Int, provider: IServiceProvider) : override fun getArtistById( artist_id: String ): Single { - return provideService(IAudioService::class.java) + return provideService(IAudioService()) .flatMap { service -> service .getArtistById(artist_id) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AuthApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AuthApi.kt index 43e89ffe2..e8e7705d8 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AuthApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/AuthApi.kt @@ -6,15 +6,11 @@ import dev.ragnarok.fenrir.api.interfaces.IAuthApi import dev.ragnarok.fenrir.api.model.LoginResponse import dev.ragnarok.fenrir.api.model.VKApiValidationResponse import dev.ragnarok.fenrir.api.model.response.BaseResponse -import dev.ragnarok.fenrir.kJson import dev.ragnarok.fenrir.nonNullNoEmpty import dev.ragnarok.fenrir.util.Utils.getDeviceId -import dev.ragnarok.fenrir.util.serializeble.json.decodeFromStream import io.reactivex.rxjava3.core.Single -import io.reactivex.rxjava3.core.SingleTransformer import io.reactivex.rxjava3.exceptions.Exceptions import io.reactivex.rxjava3.functions.Function -import retrofit2.HttpException class AuthApi(private val service: IDirectLoginSeviceProvider) : IAuthApi { override fun directLogin( @@ -43,7 +39,36 @@ class AuthApi(private val service: IDirectLoginSeviceProvider) : IAuthApi { ), 1 ) - .compose(withHttpErrorHandling()) + .flatMap { response -> + when { + "need_captcha".equals(response.error, ignoreCase = true) -> { + Single.error( + CaptchaNeedException( + response.captchaSid, + response.captchaImg + ) + ) + } + "need_validation".equals(response.error, ignoreCase = true) -> { + Single.error( + NeedValidationException( + response.validationType, + response.redirect_uri, + response.validation_sid + ) + ) + } + response.error.nonNullNoEmpty() -> { + Single.error( + AuthException( + response.error.orEmpty(), + response.errorDescription + ) + ) + } + else -> Single.just(response) + } + } } } @@ -63,7 +88,6 @@ class AuthApi(private val service: IDirectLoginSeviceProvider) : IAuthApi { } companion object { - private val BASE_RESPONSE_KSERIALIZER = kJson fun extractResponseWithErrorHandling(): Function, T> { return Function { response: BaseResponse -> response.error?.let { throw Exceptions.propagate(ApiException(it)) } @@ -71,51 +95,5 @@ class AuthApi(private val service: IDirectLoginSeviceProvider) : IAuthApi { ?: throw NullPointerException("VK return null response")) } } - - internal fun withHttpErrorHandling(): SingleTransformer { - return SingleTransformer { single: Single -> - single.onErrorResumeNext { throwable -> - if (throwable is HttpException) { - try { - val body = throwable.response()?.errorBody() - val response: LoginResponse = - BASE_RESPONSE_KSERIALIZER.decodeFromStream( - body?.byteStream()!! - ) - - //{"error":"need_captcha","captcha_sid":"846773809328","captcha_img":"https:\/\/api.vk.com\/captcha.php?sid=846773809328"} - if ("need_captcha".equals(response.error, ignoreCase = true)) { - return@onErrorResumeNext Single.error( - CaptchaNeedException( - response.captchaSid, - response.captchaImg - ) - ) - } - if ("need_validation".equals(response.error, ignoreCase = true)) { - return@onErrorResumeNext Single.error( - NeedValidationException( - response.validationType, - response.redirect_uri, - response.validation_sid - ) - ) - } - response.error.nonNullNoEmpty { - return@onErrorResumeNext Single.error( - AuthException( - it, - response.errorDescription - ) - ) - } - } catch (e: Exception) { - e.printStackTrace() - } - } - Single.error(throwable) - } - } - } } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/BoardApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/BoardApi.kt index a1da0e24d..14245aead 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/BoardApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/BoardApi.kt @@ -23,7 +23,7 @@ internal class BoardApi(accountId: Int, provider: IServiceProvider) : sort: String?, fields: String? ): Single { - return provideService(IBoardService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IBoardService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getComments( @@ -42,7 +42,7 @@ internal class BoardApi(accountId: Int, provider: IServiceProvider) : } override fun restoreComment(groupId: Int, topicId: Int, commentId: Int): Single { - return provideService(IBoardService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IBoardService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.restoreComment(groupId, topicId, commentId) .map(extractResponseWithErrorHandling()) @@ -51,7 +51,7 @@ internal class BoardApi(accountId: Int, provider: IServiceProvider) : } override fun deleteComment(groupId: Int, topicId: Int, commentId: Int): Single { - return provideService(IBoardService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IBoardService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.deleteComment(groupId, topicId, commentId) .map(extractResponseWithErrorHandling()) @@ -64,7 +64,7 @@ internal class BoardApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int?, extended: Boolean?, preview: Int?, previewLength: Int?, fields: String? ): Single { - return provideService(IBoardService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IBoardService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getTopics( @@ -95,7 +95,7 @@ internal class BoardApi(accountId: Int, provider: IServiceProvider) : groupId: Int, topicId: Int, commentId: Int, message: String?, attachments: Collection? ): Single { - return provideService(IBoardService::class.java, TokenType.USER) + return provideService(IBoardService(), TokenType.USER) .flatMap { service -> service.editComment( groupId, @@ -120,7 +120,7 @@ internal class BoardApi(accountId: Int, provider: IServiceProvider) : stickerId: Int?, generatedUniqueId: Int? ): Single { - return provideService(IBoardService::class.java, TokenType.USER) + return provideService(IBoardService(), TokenType.USER) .flatMap { service -> service .addComment( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/CommentsApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/CommentsApi.kt index 5ad1a36d6..8954c4908 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/CommentsApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/CommentsApi.kt @@ -23,7 +23,7 @@ internal class CommentsApi(accountId: Int, provider: IServiceProvider) : fields: String? ): Single { val thread_id = threadComment ?: 0 - return provideService(ICommentsService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(ICommentsService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service["var comment_id = Args.comment_id;\n" + "var owner_id = Args.owner_id;\n" + diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/DatabaseApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/DatabaseApi.kt index ec586e113..9a97b620d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/DatabaseApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/DatabaseApi.kt @@ -13,7 +13,7 @@ import io.reactivex.rxjava3.core.Single internal class DatabaseApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), IDatabaseApi { override fun getCitiesById(cityIds: Collection): Single> { - return provideService(IDatabaseService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IDatabaseService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getCitiesById(join(cityIds, ",") { obj: Any -> obj.toString() }) @@ -27,7 +27,7 @@ internal class DatabaseApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IDatabaseService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IDatabaseService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service.getCountries(integerFromBoolean(needAll), code, offset, count) .map(extractResponseWithErrorHandling()) @@ -35,7 +35,7 @@ internal class DatabaseApi(accountId: Int, provider: IServiceProvider) : } override fun getSchoolClasses(countryId: Int?): Single> { - return provideService(IDatabaseService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IDatabaseService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getSchoolClasses(countryId) @@ -44,7 +44,7 @@ internal class DatabaseApi(accountId: Int, provider: IServiceProvider) : } override fun getChairs(facultyId: Int, offset: Int?, count: Int?): Single> { - return provideService(IDatabaseService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IDatabaseService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getChairs(facultyId, offset, count) @@ -57,7 +57,7 @@ internal class DatabaseApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IDatabaseService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IDatabaseService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getFaculties(universityId, offset, count) @@ -72,7 +72,7 @@ internal class DatabaseApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IDatabaseService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IDatabaseService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getUniversities(query, countryId, cityId, offset, count) @@ -86,7 +86,7 @@ internal class DatabaseApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IDatabaseService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IDatabaseService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getSchools(query, cityId, offset, count) @@ -102,7 +102,7 @@ internal class DatabaseApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IDatabaseService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IDatabaseService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getCities( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/DocsApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/DocsApi.kt index 096d7017b..53d66ffc8 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/DocsApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/DocsApi.kt @@ -14,7 +14,7 @@ import io.reactivex.rxjava3.core.Single internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), IDocsApi { override fun delete(ownerId: Int?, docId: Int): Single { - return provideService(IDocsService::class.java, TokenType.USER) + return provideService(IDocsService(), TokenType.USER) .flatMap { service -> service.delete(ownerId, docId) .map(extractResponseWithErrorHandling()) @@ -23,7 +23,7 @@ internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun add(ownerId: Int, docId: Int, accessKey: String?): Single { - return provideService(IDocsService::class.java, TokenType.USER) + return provideService(IDocsService(), TokenType.USER) .flatMap { service -> service.add(ownerId, docId, accessKey) .map(extractResponseWithErrorHandling()) @@ -33,7 +33,7 @@ internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco override fun getById(pairs: Collection): Single> { val ids = join(pairs, ",") { AccessIdPair.format(it) } - return provideService(IDocsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IDocsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getById(ids) .map(extractResponseWithErrorHandling()) @@ -41,7 +41,7 @@ internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun search(query: String?, count: Int?, offset: Int?): Single> { - return provideService(IDocsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IDocsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.search(query, count, offset) .map(extractResponseWithErrorHandling()) @@ -49,7 +49,7 @@ internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun save(file: String?, title: String?, tags: String?): Single { - return provideService(IDocsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IDocsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.save(file, title, tags) .map(extractResponseWithErrorHandling()) @@ -60,7 +60,7 @@ internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco peerId: Int?, type: String? ): Single { - return provideService(IDocsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IDocsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getMessagesUploadServer(peerId, type) .map(extractResponseWithErrorHandling()) @@ -68,7 +68,7 @@ internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun getUploadServer(groupId: Int?): Single { - return provideService(IDocsService::class.java, TokenType.USER) + return provideService(IDocsService(), TokenType.USER) .flatMap { service -> service.getUploadServer(groupId) .map(extractResponseWithErrorHandling()) @@ -80,7 +80,7 @@ internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco group_id: Int?, name: String? ): Single { - return provideService(IDocsService::class.java, TokenType.USER) + return provideService(IDocsService(), TokenType.USER) .flatMap { service -> service.getVideoServer(isPrivate, group_id, name) .map(extractResponseWithErrorHandling()) @@ -93,7 +93,7 @@ internal class DocsApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco offset: Int?, type: Int? ): Single> { - return provideService(IDocsService::class.java, TokenType.USER) + return provideService(IDocsService(), TokenType.USER) .flatMap { service -> service[ownerId, count, offset, type] .map(extractResponseWithErrorHandling()) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/FaveApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/FaveApi.kt index a809132c0..0a5fff83c 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/FaveApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/FaveApi.kt @@ -20,7 +20,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco fields: String?, type: String? ): Single> { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.getPages(offset, count, type, fields) .map(extractResponseWithErrorHandling()) @@ -28,7 +28,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun getPhotos(offset: Int?, count: Int?): Single> { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.getPhotos(offset, count) .map(extractResponseWithErrorHandling()) @@ -36,7 +36,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun getVideos(offset: Int?, count: Int?): Single> { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.getVideos(offset, count, "video", 1, UserColumns.API_FIELDS) .map(extractResponseWithErrorHandling()) @@ -52,7 +52,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun getArticles(offset: Int?, count: Int?): Single> { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.getArticles(offset, count, "article", 1, UserColumns.API_FIELDS) .map(extractResponseWithErrorHandling()) @@ -72,7 +72,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco offset: Int?, count: Int? ): Single> { - return provideService(IFaveService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IFaveService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getOwnerPublishedArticles( owner_id, @@ -87,7 +87,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun getPosts(offset: Int?, count: Int?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.getPosts(offset, count, "post", 1, UserColumns.API_FIELDS) .map(extractResponseWithErrorHandling()) @@ -95,7 +95,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun getLinks(offset: Int?, count: Int?): Single> { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.getLinks(offset, count, "link", 1, UserColumns.API_FIELDS) .map(extractResponseWithErrorHandling()) @@ -103,7 +103,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun getProducts(offset: Int?, count: Int?): Single> { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.getProducts(offset, count, "product", 1, UserColumns.API_FIELDS) .map(extractResponseWithErrorHandling()) @@ -119,7 +119,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun addPage(userId: Int?, groupId: Int?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.addPage(userId, groupId) .map(extractResponseWithErrorHandling()) @@ -128,7 +128,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun addLink(link: String?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.addLink(link) .map(extractResponseWithErrorHandling()) @@ -137,7 +137,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun addVideo(owner_id: Int?, id: Int?, access_key: String?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.addVideo(owner_id, id, access_key) .map(extractResponseWithErrorHandling()) @@ -146,7 +146,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun addArticle(url: String?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.addArticle(url) .map(extractResponseWithErrorHandling()) @@ -155,7 +155,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun addProduct(id: Int, owner_id: Int, access_key: String?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.addProduct(id, owner_id, access_key) .map(extractResponseWithErrorHandling()) @@ -164,7 +164,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun addPost(owner_id: Int?, id: Int?, access_key: String?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.addPost(owner_id, id, access_key) .map(extractResponseWithErrorHandling()) @@ -173,7 +173,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun removePage(userId: Int?, groupId: Int?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.removePage(userId, groupId) .map(extractResponseWithErrorHandling()) @@ -182,7 +182,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun removeLink(linkId: String?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.removeLink(linkId) .map(extractResponseWithErrorHandling()) @@ -191,7 +191,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun removeArticle(owner_id: Int?, article_id: Int?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.removeArticle(owner_id, article_id) .map(extractResponseWithErrorHandling()) @@ -200,7 +200,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun removeProduct(id: Int?, owner_id: Int?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.removeProduct(id, owner_id) .map(extractResponseWithErrorHandling()) @@ -209,7 +209,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun removePost(owner_id: Int?, id: Int?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.removePost(owner_id, id) .map(extractResponseWithErrorHandling()) @@ -218,7 +218,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun removeVideo(owner_id: Int?, id: Int?): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.removeVideo(owner_id, id) .map(extractResponseWithErrorHandling()) @@ -227,7 +227,7 @@ internal class FaveApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun pushFirst(owner_id: Int): Single { - return provideService(IFaveService::class.java, TokenType.USER) + return provideService(IFaveService(), TokenType.USER) .flatMap { service -> service.pushFirst( "var owner_id = Args.owner_id;\n" + diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/FriendsApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/FriendsApi.kt index 779cddfd0..525e8afe0 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/FriendsApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/FriendsApi.kt @@ -39,7 +39,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : "\n" + "return {\"uids\":uids, \"profiles\":profiles};" val formattedCode = String.format(code, userId, count, offset, targetFields, targetOrder) - return provideService(IFriendsService::class.java, TokenType.USER) + return provideService(IFriendsService(), TokenType.USER) .flatMap { service -> service .getOnline(formattedCode) @@ -73,7 +73,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : userId: Int?, order: String?, listId: Int?, count: Int?, offset: Int?, fields: String?, nameCase: String? ): Single> { - return provideService(IFriendsService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IFriendsService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service[userId, order, listId, count, offset, fields, nameCase] .map(extractResponseWithErrorHandling()) @@ -81,7 +81,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : } override fun getByPhones(phones: String?, fields: String?): Single> { - return provideService(IFriendsService::class.java, TokenType.USER) + return provideService(IFriendsService(), TokenType.USER) .flatMap { service -> service.getByPhones(phones, fields) .map(extractResponseWithErrorHandling()) @@ -93,7 +93,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : fields: String?, nameCase: String? ): Single> { - return provideService(IFriendsService::class.java, TokenType.USER) + return provideService(IFriendsService(), TokenType.USER) .flatMap { service -> service.getRecommendations(count, fields, nameCase) .map(extractResponseWithErrorHandling()) @@ -101,7 +101,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : } override fun getLists(userId: Int?, returnSystem: Boolean?): Single> { - return provideService(IFriendsService::class.java, TokenType.USER) + return provideService(IFriendsService(), TokenType.USER) .flatMap { service -> service.getLists(userId, integerFromBoolean(returnSystem)) .map(extractResponseWithErrorHandling()) @@ -109,7 +109,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : } override fun delete(userId: Int): Single { - return provideService(IFriendsService::class.java, TokenType.USER) + return provideService(IFriendsService(), TokenType.USER) .flatMap { service -> service.delete(userId) .map(extractResponseWithErrorHandling()) @@ -117,7 +117,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : } override fun add(userId: Int, text: String?, follow: Boolean?): Single { - return provideService(IFriendsService::class.java, TokenType.USER) + return provideService(IFriendsService(), TokenType.USER) .flatMap { service -> service.add(userId, text, integerFromBoolean(follow)) .map(extractResponseWithErrorHandling()) @@ -132,7 +132,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IFriendsService::class.java, TokenType.USER) + return provideService(IFriendsService(), TokenType.USER) .flatMap { service -> service.search(userId, query, fields, nameCase, offset, count) .map(extractResponseWithErrorHandling()) @@ -171,7 +171,7 @@ internal class FriendsApi(accountId: Int, provider: IServiceProvider) : // MutualFriendsResponse data = convertJsonResponse(response.get(), MutualFriendsResponse.class); // return data.profiles; // }); - return provideService(IFriendsService::class.java, TokenType.USER) + return provideService(IFriendsService(), TokenType.USER) .flatMap { service -> service.getMutual(formattedCode) .map(extractResponseWithErrorHandling()) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/GroupsApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/GroupsApi.kt index e8c633222..5db1ba6bb 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/GroupsApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/GroupsApi.kt @@ -25,7 +25,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : contactEmail: String?, contactPhone: String?, ): Completable { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMapCompletable { service -> service .editManager( @@ -56,7 +56,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : obscene_stopwords: Int?, obscene_words: String? ): Completable { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMapCompletable { service -> service .edit( @@ -74,7 +74,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : } override fun unban(groupId: Int, ownerId: Int): Completable { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMapCompletable { service -> service .unban(groupId, ownerId) @@ -91,7 +91,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : comment: String?, commentVisible: Boolean? ): Completable { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMapCompletable { service -> service .ban( @@ -108,7 +108,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : } override fun getSettings(groupId: Int): Single { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .getSettings(groupId) @@ -121,7 +121,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : offset: Int, count: Int ): Single> { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getMarketAlbums(owner_id, offset, count) .map(extractResponseWithErrorHandling()) @@ -135,7 +135,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : count: Int, extended: Int? ): Single> { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getMarket(owner_id, album_id, offset, count, extended) .map(extractResponseWithErrorHandling()) @@ -148,7 +148,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : count: Int, extended: Int? ): Single> { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getMarketServices(owner_id, offset, count, extended) .map(extractResponseWithErrorHandling()) @@ -158,7 +158,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : override fun getMarketById(ids: Collection): Single> { val markets = join(ids, ",") { AccessIdPair.format(it) } - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getMarketById(markets, 1) .map(extractResponseWithErrorHandling()) @@ -172,7 +172,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : fields: String?, userId: Int? ): Single> { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getBanned(groupId, offset, count, fields, userId) .map(extractResponseWithErrorHandling()) @@ -181,7 +181,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : override fun getWallInfo(groupId: String?, fields: String?): Single { return provideService( - IGroupsService::class.java, + IGroupsService(), TokenType.USER, TokenType.SERVICE, TokenType.COMMUNITY @@ -211,7 +211,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : filter: String? ): Single> { return provideService( - IGroupsService::class.java, + IGroupsService(), TokenType.USER, TokenType.COMMUNITY, TokenType.SERVICE @@ -234,7 +234,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IGroupsService::class.java, TokenType.USER) + return provideService(IGroupsService(), TokenType.USER) .flatMap { service -> service .search( @@ -246,7 +246,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : } override fun leave(groupId: Int): Single { - return provideService(IGroupsService::class.java, TokenType.USER) + return provideService(IGroupsService(), TokenType.USER) .flatMap { service -> service.leave(groupId) .map(extractResponseWithErrorHandling()) @@ -255,7 +255,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : } override fun join(groupId: Int, notSure: Int?): Single { - return provideService(IGroupsService::class.java, TokenType.USER) + return provideService(IGroupsService(), TokenType.USER) .flatMap { service -> service.join(groupId, notSure) .map(extractResponseWithErrorHandling()) @@ -271,7 +271,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IGroupsService::class.java, TokenType.USER) + return provideService(IGroupsService(), TokenType.USER) .flatMap { service -> service[userId, integerFromBoolean(extended), filter, fields, offset, count] .map(extractResponseWithErrorHandling()) @@ -279,7 +279,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : } override fun getLongPollServer(groupId: Int): Single { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .getLongPollServer(groupId) @@ -297,7 +297,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : join(ids, ",")?.let { pds.add(it) } join(domains, ",")?.let { pds.add(it) } return provideService( - IGroupsService::class.java, + IGroupsService(), TokenType.USER, TokenType.COMMUNITY, TokenType.SERVICE @@ -310,7 +310,7 @@ internal class GroupsApi(accountId: Int, provider: IServiceProvider) : } override fun getChats(groupId: Int, offset: Int?, count: Int?): Single> { - return provideService(IGroupsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IGroupsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .getChats(groupId, offset, count) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/LikesApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/LikesApi.kt index 7aaf9ed04..c9769e91e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/LikesApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/LikesApi.kt @@ -15,7 +15,7 @@ internal class LikesApi(accountId: Int, provider: IServiceProvider) : filter: String?, friendsOnly: Boolean?, offset: Int?, count: Int?, skipOwn: Boolean?, fields: String? ): Single { - return provideService(ILikesService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(ILikesService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getList( @@ -32,7 +32,7 @@ internal class LikesApi(accountId: Int, provider: IServiceProvider) : itemId: Int, accessKey: String? ): Single { - return provideService(ILikesService::class.java, TokenType.USER) + return provideService(ILikesService(), TokenType.USER) .flatMap { service -> service.delete(type, ownerId, itemId, accessKey) .map(extractResponseWithErrorHandling()) @@ -41,7 +41,7 @@ internal class LikesApi(accountId: Int, provider: IServiceProvider) : } override fun add(type: String?, ownerId: Int?, itemId: Int, accessKey: String?): Single { - return provideService(ILikesService::class.java, TokenType.USER) + return provideService(ILikesService(), TokenType.USER) .flatMap { service -> service.add(type, ownerId, itemId, accessKey) .map(extractResponseWithErrorHandling()) @@ -50,7 +50,7 @@ internal class LikesApi(accountId: Int, provider: IServiceProvider) : } override fun isLiked(type: String?, ownerId: Int?, itemId: Int): Single { - return provideService(ILikesService::class.java, TokenType.USER) + return provideService(ILikesService(), TokenType.USER) .flatMap { service -> service.isLiked(type, ownerId, itemId) .map(extractResponseWithErrorHandling()) @@ -64,7 +64,7 @@ internal class LikesApi(accountId: Int, provider: IServiceProvider) : itemId: Int, accessKey: String? ): Single { - return provideService(ILikesService::class.java, TokenType.USER) + return provideService(ILikesService(), TokenType.USER) .flatMap { service -> service.checkAndAddLike( "var type = Args.type; var owner_id = Args.owner_id; var item_id = Args.item_id; var access_key = Args.access_key; if(API.likes.isLiked({\"v\":\"" + Constants.API_VERSION + "\", \"type\": type, \"owner_id\": owner_id, \"item_id\": item_id}).liked == 0) {return API.likes.add({\"v\":\"" + Constants.API_VERSION + "\", \"type\": type, \"owner_id\": owner_id, \"item_id\": item_id, \"access_key\": access_key}).likes;} return 0;", diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/LongpollApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/LongpollApi.kt index 08e1dd0e2..d856fdbf1 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/LongpollApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/LongpollApi.kt @@ -1,43 +1,41 @@ package dev.ragnarok.fenrir.api.impl -import dev.ragnarok.fenrir.api.IOtherVkRetrofitProvider +import dev.ragnarok.fenrir.api.IOtherVkRestProvider import dev.ragnarok.fenrir.api.interfaces.ILongpollApi import dev.ragnarok.fenrir.api.model.longpoll.VkApiGroupLongpollUpdates import dev.ragnarok.fenrir.api.model.longpoll.VkApiLongpollUpdates import dev.ragnarok.fenrir.api.services.ILongpollUpdatesService import io.reactivex.rxjava3.core.Single -class LongpollApi internal constructor(private val provider: IOtherVkRetrofitProvider) : +class LongpollApi internal constructor(private val provider: IOtherVkRestProvider) : ILongpollApi { override fun getUpdates( - server: String?, + server: String, key: String?, ts: Long, wait: Int, mode: Int, version: Int ): Single { - return provider.provideLongpollRetrofit() - .flatMap { wrapper -> - wrapper.create( - ILongpollUpdatesService::class.java - ) - .getUpdates(server, "a_check", key, ts, wait, mode, version) + return provider.provideLongpollRest() + .flatMap { + val ret = ILongpollUpdatesService() + ret.addon(it) + ret.getUpdates(server, "a_check", key, ts, wait, mode, version) } } override fun getGroupUpdates( - server: String?, + server: String, key: String?, ts: String?, wait: Int ): Single { - return provider.provideLongpollRetrofit() - .flatMap { wrapper -> - wrapper.create( - ILongpollUpdatesService::class.java - ) - .getGroupUpdates(server, "a_check", key, ts, wait) + return provider.provideLongpollRest() + .flatMap { + val ret = ILongpollUpdatesService() + ret.addon(it) + ret.getGroupUpdates(server, "a_check", key, ts, wait) } } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/MessagesApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/MessagesApi.kt index 7d92cc66d..e2ad1565d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/MessagesApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/MessagesApi.kt @@ -13,7 +13,7 @@ import io.reactivex.rxjava3.core.Single internal class MessagesApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), IMessagesApi { private fun serviceRx(vararg tokenTypes: Int): Single { - return provideService(IMessageService::class.java, *tokenTypes) + return provideService(IMessageService(), *tokenTypes) } override fun edit( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/Networker.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/Networker.kt index b169a2de6..7be7e8b20 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/Networker.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/Networker.kt @@ -8,29 +8,29 @@ import dev.ragnarok.fenrir.settings.IProxySettings import io.reactivex.rxjava3.core.Single class Networker(settings: IProxySettings) : INetworker { - private val otherVkRetrofitProvider: IOtherVkRetrofitProvider - private val vkRetrofitProvider: IVkRetrofitProvider - private val uploadRetrofitProvider: IUploadRetrofitProvider + private val otherVkRestProvider: IOtherVkRestProvider + private val vkRestProvider: IVkRestProvider + private val uploadRestProvider: IUploadRestProvider override fun vkDefault(accountId: Int): IAccountApis { - return VkApies[accountId, vkRetrofitProvider] + return VkApies[accountId, vkRestProvider] } override fun vkManual(accountId: Int, accessToken: String): IAccountApis { - return VkApies.create(accountId, accessToken, vkRetrofitProvider) + return VkApies.create(accountId, accessToken, vkRestProvider) } - override fun getVkRetrofitProvider(): IVkRetrofitProvider { - return vkRetrofitProvider + override fun getVkRestProvider(): IVkRestProvider { + return vkRestProvider } override fun vkDirectAuth(): IAuthApi { return AuthApi(object : IDirectLoginSeviceProvider { override fun provideAuthService(): Single { - return otherVkRetrofitProvider.provideAuthRetrofit() - .map { wrapper -> - wrapper.create( - IAuthService::class.java - ) + return otherVkRestProvider.provideAuthRest() + .map { + val ret = IAuthService() + ret.addon(it) + ret } } }) @@ -39,11 +39,11 @@ class Networker(settings: IProxySettings) : INetworker { override fun vkAuth(): IAuthApi { return AuthApi(object : IDirectLoginSeviceProvider { override fun provideAuthService(): Single { - return otherVkRetrofitProvider.provideAuthServiceRetrofit() - .map { wrapper -> - wrapper.create( - IAuthService::class.java - ) + return otherVkRestProvider.provideAuthServiceRest() + .map { + val ret = IAuthService() + ret.addon(it) + ret } } }) @@ -52,27 +52,27 @@ class Networker(settings: IProxySettings) : INetworker { override fun localServerApi(): ILocalServerApi { return LocalServerApi(object : ILocalServerServiceProvider { override fun provideLocalServerService(): Single { - return otherVkRetrofitProvider.provideLocalServerRetrofit() - .map { wrapper -> - wrapper.create( - ILocalServerService::class.java - ) + return otherVkRestProvider.provideLocalServerRest() + .map { + val ret = ILocalServerService() + ret.addon(it) + ret } } }) } override fun longpoll(): ILongpollApi { - return LongpollApi(otherVkRetrofitProvider) + return LongpollApi(otherVkRestProvider) } override fun uploads(): IUploadApi { - return UploadApi(uploadRetrofitProvider) + return UploadApi(uploadRestProvider) } init { - otherVkRetrofitProvider = OtherVkRetrofitProvider(settings) - vkRetrofitProvider = VkRetrofitProvider(settings, VkMethodHttpClientFactory()) - uploadRetrofitProvider = UploadRetrofitProvider(settings) + otherVkRestProvider = OtherVkRestProvider(settings) + vkRestProvider = VkRestProvider(settings, VkMethodHttpClientFactory()) + uploadRestProvider = UploadRestProvider(settings) } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/NewsfeedApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/NewsfeedApi.kt index ece9abd64..60b745f9e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/NewsfeedApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/NewsfeedApi.kt @@ -17,7 +17,7 @@ import kotlin.math.abs internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), INewsfeedApi { override fun getLists(listIds: Collection?): Single> { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service.getLists(join(listIds, ","), 1) .map(extractResponseWithErrorHandling()) @@ -25,7 +25,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : } override fun saveList(title: String?, listIds: Collection?): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service.saveList(title, join(listIds, ",")) .map(extractResponseWithErrorHandling()) @@ -42,7 +42,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : users.add(i) } } - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service.addBan(join(users, ","), join(groups, ",")) .map(extractResponseWithErrorHandling()) @@ -50,7 +50,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : } override fun deleteList(list_id: Int?): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service.deleteList(list_id) .map(extractResponseWithErrorHandling()) @@ -58,7 +58,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : } override fun ignoreItem(type: String?, owner_id: Int?, item_id: Int?): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service.ignoreItem(type, owner_id, item_id) .map(extractResponseWithErrorHandling()) @@ -76,7 +76,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : startFrom: String?, fields: String? ): Single { - return provideService(INewsfeedService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(INewsfeedService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .search( @@ -97,7 +97,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : startFrom: String?, fields: String? ): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service .getComments( @@ -122,7 +122,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : startTime: Long?, endTime: Long? ): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service .getMentions(owner_id, count, offset, startTime, endTime) @@ -135,7 +135,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : endTime: Long?, maxPhotoCount: Int?, sourceIds: String?, startFrom: String?, count: Int?, fields: String? ): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service[filters, integerFromBoolean(returnBanned), startTime, endTime, maxPhotoCount, sourceIds, startFrom, count, fields] .map(extractResponseWithErrorHandling()) @@ -147,7 +147,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : endTime: Long?, maxPhotoCount: Int?, sourceIds: String?, startFrom: String?, count: Int?, fields: String? ): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service.getByType( "top", @@ -169,7 +169,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : startTime: Long?, endTime: Long?, maxPhotoCount: Int?, startFrom: String?, count: Int?, fields: String? ): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service .getRecommended( @@ -186,7 +186,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : count: Int?, fields: String? ): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service .getFeedLikes(maxPhotoCount, startFrom, count, fields) @@ -204,7 +204,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : users.add(i) } } - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service.deleteBan(join(users, ","), join(groups, ",")) .map(extractResponseWithErrorHandling()) @@ -212,7 +212,7 @@ internal class NewsfeedApi(accountId: Int, provider: IServiceProvider) : } override fun getBanned(): Single { - return provideService(INewsfeedService::class.java, TokenType.USER) + return provideService(INewsfeedService(), TokenType.USER) .flatMap { service -> service .getBanned( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/NotificationsApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/NotificationsApi.kt index d597bcebb..a816b3d0c 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/NotificationsApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/NotificationsApi.kt @@ -14,9 +14,9 @@ import io.reactivex.rxjava3.core.Single internal class NotificationsApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), INotificationsApi { override fun markAsViewed(): Single { - return provideService(INotificationsService::class.java, TokenType.USER) + return provideService(INotificationsService(), TokenType.USER) .flatMap { service -> - service.markAsViewed() + service.markAsViewed .map(extractResponseWithErrorHandling()) } } @@ -28,7 +28,7 @@ internal class NotificationsApi(accountId: Int, provider: IServiceProvider) : startTime: Long?, endTime: Long? ): Single { - return provideService(INotificationsService::class.java, TokenType.USER) + return provideService(INotificationsService(), TokenType.USER) .flatMap { service -> service[count, startFrom, filters, startTime, endTime] .map(extractResponseWithErrorHandling()) @@ -57,7 +57,7 @@ internal class NotificationsApi(accountId: Int, provider: IServiceProvider) : startTime: Long?, endTime: Long? ): Single { - return provideService(INotificationsService::class.java, TokenType.USER) + return provideService(INotificationsService(), TokenType.USER) .flatMap { service -> service.getOfficial( count, @@ -72,7 +72,7 @@ internal class NotificationsApi(accountId: Int, provider: IServiceProvider) : } override fun hide(query: String?): Single { - return provideService(INotificationsService::class.java, TokenType.USER) + return provideService(INotificationsService(), TokenType.USER) .flatMap { service -> service.hide(query) .map(extractResponseWithErrorHandling()) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/OtherApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/OtherApi.kt index 940bf3472..b36332bd3 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/OtherApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/OtherApi.kt @@ -1,11 +1,11 @@ package dev.ragnarok.fenrir.api.impl -import dev.ragnarok.fenrir.api.IVkRetrofitProvider +import dev.ragnarok.fenrir.api.IVkRestProvider import dev.ragnarok.fenrir.api.interfaces.IOtherApi +import dev.ragnarok.fenrir.api.rest.HttpException import dev.ragnarok.fenrir.settings.Settings import dev.ragnarok.fenrir.util.Optional import dev.ragnarok.fenrir.util.Optional.Companion.wrap -import dev.ragnarok.fenrir.util.serializeble.retrofit.HttpCodeException import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.SingleEmitter import okhttp3.FormBody @@ -13,7 +13,7 @@ import okhttp3.Request import okhttp3.Response import okhttp3.ResponseBody -class OtherApi(private val accountId: Int, private val provider: IVkRetrofitProvider) : IOtherApi { +class OtherApi(private val accountId: Int, private val provider: IVkRestProvider) : IOtherApi { override fun rawRequest( method: String, postParams: Map @@ -33,18 +33,18 @@ class OtherApi(private val accountId: Int, private val provider: IVkRetrofitProv ) .post(bodyBuilder.build()) .build() - val call = client.newCall(request) + val call = client.build().newCall(request) emitter.setCancellable { call.cancel() } try { val response = call.execute() if (!response.isSuccessful) { - emitter.onError(HttpCodeException(response.code)) + emitter.tryOnError(HttpException(response.code)) } else { emitter.onSuccess(response) } response.close() } catch (e: Exception) { - emitter.onError(e) + emitter.tryOnError(e) } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PagesApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PagesApi.kt index d188f38e1..c792ae766 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PagesApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PagesApi.kt @@ -18,7 +18,7 @@ internal class PagesApi(accountId: Int, provider: IServiceProvider) : needSource: Boolean?, needHtml: Boolean? ): Single { - return provideService(IPagesService::class.java, TokenType.USER) + return provideService(IPagesService(), TokenType.USER) .flatMap { service -> service[ownerId, pageId, integerFromBoolean(global), integerFromBoolean(sitePreview), title, integerFromBoolean( needSource diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PhotosApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PhotosApi.kt index 55381ab21..d2500a772 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PhotosApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PhotosApi.kt @@ -15,7 +15,7 @@ import io.reactivex.rxjava3.core.Single internal class PhotosApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), IPhotosApi { override fun deleteAlbum(albumId: Int, groupId: Int?): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.deleteAlbum(albumId, groupId) .map(extractResponseWithErrorHandling()) @@ -24,7 +24,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun restore(ownerId: Int?, photoId: Int): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.restore(ownerId, photoId) .map(extractResponseWithErrorHandling()) @@ -33,7 +33,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun delete(ownerId: Int?, photoId: Int): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.delete(ownerId, photoId) .map(extractResponseWithErrorHandling()) @@ -42,7 +42,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun deleteComment(ownerId: Int?, commentId: Int): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.deleteComment(ownerId, commentId) .map(extractResponseWithErrorHandling()) @@ -51,7 +51,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun restoreComment(ownerId: Int?, commentId: Int): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.restoreComment(ownerId, commentId) .map(extractResponseWithErrorHandling()) @@ -63,7 +63,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : ownerId: Int?, commentId: Int, message: String?, attachments: Collection? ): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.editComment( ownerId, @@ -89,7 +89,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : ): Single { val privacyViewTxt = privacyView?.buildJsonArray() val privacyCommentTxt = privacyComment?.buildJsonArray() - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .createAlbum( @@ -112,7 +112,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : ): Single { val privacyViewTxt = privacyView?.buildJsonArray() val privacyCommentTxt = privacyComment?.buildJsonArray() - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .editAlbum( @@ -125,7 +125,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun copy(ownerId: Int, photoId: Int, accessKey: String?): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.copy(ownerId, photoId, accessKey) .map(extractResponseWithErrorHandling()) @@ -137,7 +137,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : replyToComment: Int?, attachments: Collection?, stickerId: Int?, accessKey: String?, generatedUniqueId: Int? ): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .createComment( @@ -170,7 +170,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : extended: Boolean?, fields: String? ): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .getComments( @@ -186,7 +186,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : ids, "," ) { pair -> pair.ownerId.toString() + "_" + pair.id + if (pair.accessKey == null) "" else "_" + pair.accessKey } - return provideService(IPhotosService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IPhotosService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service.getById(line, 1, 1) .map(extractResponseWithErrorHandling()) @@ -204,7 +204,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun getUploadServer(albumId: Int, groupId: Int?): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .getUploadServer(albumId, groupId) @@ -217,7 +217,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : hash: String?, photo: String? ): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .saveOwnerPhoto(server, hash, photo) @@ -226,7 +226,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun getOwnerPhotoUploadServer(ownerId: Int?): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .getOwnerPhotoUploadServer(ownerId) @@ -235,7 +235,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun getChatUploadServer(chat_id: Int?): Single { - return provideService(IPhotosService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IPhotosService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .getChatUploadServer(chat_id) @@ -244,7 +244,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun setChatPhoto(file: String?): Single { - return provideService(IPhotosService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IPhotosService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .setChatPhoto(file) @@ -257,7 +257,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : server: Int, hash: String?, latitude: Double?, longitude: Double?, caption: String? ): Single> { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .saveWallPhoto( @@ -275,7 +275,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override fun getWallUploadServer(groupId: Int?): Single { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .getWallUploadServer(groupId) @@ -287,7 +287,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : albumId: Int, groupId: Int?, server: Int, photosList: String?, hash: String?, latitude: Double?, longitude: Double?, caption: String? ): Single> { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service .save(albumId, groupId, server, photosList, hash, latitude, longitude, caption) @@ -300,7 +300,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : rev: Boolean?, offset: Int?, count: Int? ): Single> { val photos = join(photoIds, ",") - return provideService(IPhotosService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IPhotosService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service[ownerId, albumId, photos, integerFromBoolean(rev), 1, 1, offset, count] .map(extractResponseWithErrorHandling()) @@ -314,7 +314,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.getUserPhotos(ownerId, extended, sort, offset, count) .map(extractResponseWithErrorHandling()) @@ -328,7 +328,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.getAll(ownerId, extended, photo_sizes, offset, count, 0, 1, 0) .map(extractResponseWithErrorHandling()) @@ -336,7 +336,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : } override val messagesUploadServer: Single - get() = provideService(IPhotosService::class.java, TokenType.USER, TokenType.COMMUNITY) + get() = provideService(IPhotosService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.messagesUploadServer .map(extractResponseWithErrorHandling()) @@ -347,7 +347,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : photo: String?, hash: String? ): Single> { - return provideService(IPhotosService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IPhotosService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.saveMessagesPhoto(server, photo, hash) .map(extractResponseWithErrorHandling()) @@ -360,7 +360,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : needCovers: Boolean? ): Single> { val ids = join(albumIds, ",") - return provideService(IPhotosService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IPhotosService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service.getAlbums( ownerId, @@ -380,7 +380,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : photo_id: Int?, access_key: String? ): Single> { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.getTags(ownerId, photo_id, access_key).map( extractResponseWithErrorHandling() @@ -395,7 +395,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IPhotosService::class.java, TokenType.USER) + return provideService(IPhotosService(), TokenType.USER) .flatMap { service -> service.getAllComments(ownerId, album_id, need_likes, offset, count).map( extractResponseWithErrorHandling() @@ -414,7 +414,7 @@ internal class PhotosApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IPhotosService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IPhotosService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service.search( q, diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PollsApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PollsApi.kt index 2e023eb8d..476caf779 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PollsApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/PollsApi.kt @@ -22,7 +22,7 @@ internal class PollsApi(accountId: Int, provider: IServiceProvider) : ownerId: Int, addAnswers: List ): Single { - return provideService(IPollsService::class.java, TokenType.USER) + return provideService(IPollsService(), TokenType.USER) .flatMap { service -> service .create( @@ -42,7 +42,7 @@ internal class PollsApi(accountId: Int, provider: IServiceProvider) : answerId: Long, isBoard: Boolean? ): Single { - return provideService(IPollsService::class.java, TokenType.USER) + return provideService(IPollsService(), TokenType.USER) .flatMap { service -> service.deleteVote(ownerId, pollId, answerId, integerFromBoolean(isBoard)) .map(extractResponseWithErrorHandling()) @@ -56,7 +56,7 @@ internal class PollsApi(accountId: Int, provider: IServiceProvider) : answerIds: Set, isBoard: Boolean? ): Single { - return provideService(IPollsService::class.java, TokenType.USER) + return provideService(IPollsService(), TokenType.USER) .flatMap { service -> service.addVote(ownerId, pollId, join(answerIds, ","), integerFromBoolean(isBoard)) .map(extractResponseWithErrorHandling()) @@ -65,7 +65,7 @@ internal class PollsApi(accountId: Int, provider: IServiceProvider) : } override fun getById(ownerId: Int, isBoard: Boolean?, pollId: Int): Single { - return provideService(IPollsService::class.java, TokenType.USER) + return provideService(IPollsService(), TokenType.USER) .flatMap { service -> service.getById(ownerId, integerFromBoolean(isBoard), pollId) .map(extractResponseWithErrorHandling()) @@ -81,7 +81,7 @@ internal class PollsApi(accountId: Int, provider: IServiceProvider) : val ids = join(answer_ids, ",") { obj: Any -> obj.toString() } ?: return Single.just( emptyList() ) - return provideService(IPollsService::class.java, TokenType.USER) + return provideService(IPollsService(), TokenType.USER) .flatMap { service -> service.getVoters( ownerId, diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StatusApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StatusApi.kt index be1b68755..9102a6255 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StatusApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StatusApi.kt @@ -9,7 +9,7 @@ import io.reactivex.rxjava3.core.Single internal class StatusApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), IStatusApi { override fun set(text: String?, groupId: Int?): Single { - return provideService(IStatusService::class.java, TokenType.USER) + return provideService(IStatusService(), TokenType.USER) .flatMap { service -> service.set(text, groupId) .map(extractResponseWithErrorHandling()) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StoreApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StoreApi.kt index 5b13c3823..1452225bd 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StoreApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StoreApi.kt @@ -12,14 +12,14 @@ import io.reactivex.rxjava3.core.Single internal class StoreApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), IStoreApi { override val stickerKeywords: Single - get() = provideService(IStoreService::class.java, TokenType.USER) + get() = provideService(IStoreService(), TokenType.USER) .flatMap { service -> service .getStickersKeywords("var dic=API.store.getStickersKeywords({'v':'" + Constants.API_VERSION + "','aliases':1,'all_products':1}).dictionary;return {'keywords': dic@.words, 'words_stickers': dic@.user_stickers};") .map(extractResponseWithErrorHandling()) } override val stickers: Single - get() = provideService(IStoreService::class.java, TokenType.USER) + get() = provideService(IStoreService(), TokenType.USER) .flatMap { service -> service .getStickers("var pack = API.store.getProducts({'v':'" + Constants.API_VERSION + "','extended':1,'filters':'active','type':'stickers'}); var recent = API.messages.getRecentStickers(); return {'sticker_pack': pack, 'recent': recent};") diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UploadApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UploadApi.kt index d0a4ebf5d..0908f768b 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UploadApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UploadApi.kt @@ -1,6 +1,6 @@ package dev.ragnarok.fenrir.api.impl -import dev.ragnarok.fenrir.api.IUploadRetrofitProvider +import dev.ragnarok.fenrir.api.IUploadRestProvider import dev.ragnarok.fenrir.api.PercentagePublisher import dev.ragnarok.fenrir.api.interfaces.IUploadApi import dev.ragnarok.fenrir.api.model.response.BaseResponse @@ -13,13 +13,17 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import java.io.InputStream -class UploadApi internal constructor(private val provider: IUploadRetrofitProvider) : IUploadApi { - private fun service(): IUploadService { - return provider.provideUploadRetrofit().blockingGet().create(IUploadService::class.java) +class UploadApi internal constructor(private val provider: IUploadRestProvider) : IUploadApi { + private fun service(): Single { + return provider.provideUploadRest().map { + val ret = IUploadService() + ret.addon(it) + ret + } } override fun uploadDocumentRx( - server: String?, + server: String, filename: String?, doc: InputStream, listener: PercentagePublisher? @@ -29,11 +33,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "*/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("file", filename, body) - return service().uploadDocumentRx(server, part) + return service().flatMap { service -> service.uploadDocumentRx(server, part) } } override fun uploadAudioRx( - server: String?, + server: String, filename: String?, `is`: InputStream, listener: PercentagePublisher? @@ -43,11 +47,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "*/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("file", filename, body) - return service().uploadAudioRx(server, part) + return service().flatMap { service -> service.uploadAudioRx(server, part) } } override fun remotePlayAudioRx( - server: String?, + server: String, filename: String?, `is`: InputStream, listener: PercentagePublisher? @@ -57,11 +61,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "*/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("audio", filename, body) - return service().remotePlayAudioRx(server, part) + return service().flatMap { service -> service.remotePlayAudioRx(server, part) } } override fun uploadStoryRx( - server: String?, + server: String, filename: String?, `is`: InputStream, listener: PercentagePublisher?, @@ -77,11 +81,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid filename, body ) - return service().uploadStoryRx(server, part) + return service().flatMap { service -> service.uploadStoryRx(server, part) } } override fun uploadVideoRx( - server: String?, + server: String, filename: String?, video: InputStream, listener: PercentagePublisher? @@ -91,11 +95,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "*/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("file", filename, body) - return service().uploadVideoRx(server, part) + return service().flatMap { service -> service.uploadVideoRx(server, part) } } override fun uploadOwnerPhotoRx( - server: String?, + server: String, photo: InputStream, listener: PercentagePublisher? ): Single { @@ -105,11 +109,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "image/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("photo", "photo.jpg", body) - return service().uploadOwnerPhotoRx(server, part) + return service().flatMap { service -> service.uploadOwnerPhotoRx(server, part) } } override fun uploadChatPhotoRx( - server: String?, + server: String, photo: InputStream, listener: PercentagePublisher? ): Single { @@ -119,11 +123,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "image/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("photo", "photo.jpg", body) - return service().uploadChatPhotoRx(server, part) + return service().flatMap { service -> service.uploadChatPhotoRx(server, part) } } override fun uploadPhotoToWallRx( - server: String?, + server: String, photo: InputStream, listener: PercentagePublisher? ): Single { @@ -133,11 +137,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "image/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("photo", "photo.jpg", body) - return service().uploadPhotoToWallRx(server, part) + return service().flatMap { service -> service.uploadPhotoToWallRx(server, part) } } override fun uploadPhotoToMessageRx( - server: String?, + server: String, `is`: InputStream, listener: PercentagePublisher? ): Single { @@ -147,11 +151,11 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "image/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("photo", "photo.jpg", body) - return service().uploadPhotoToMessageRx(server, part) + return service().flatMap { service -> service.uploadPhotoToMessageRx(server, part) } } override fun uploadPhotoToAlbumRx( - server: String?, + server: String, file1: InputStream, listener: PercentagePublisher? ): Single { @@ -161,7 +165,7 @@ class UploadApi internal constructor(private val provider: IUploadRetrofitProvid "image/*".toMediaTypeOrNull() ) val part: MultipartBody.Part = MultipartBody.Part.createFormData("file1", "photo.jpg", body) - return service().uploadPhotoToAlbumRx(server, part) + return service().flatMap { service -> service.uploadPhotoToAlbumRx(server, part) } } companion object { diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UsersApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UsersApi.kt index cebce9b31..54fb22f5d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UsersApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UsersApi.kt @@ -22,7 +22,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : fields: String?, nameCase: String? ): Single { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IUsersService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getUserWallInfo( @@ -65,7 +65,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : fields: String?, nameCase: String? ): Single> { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IUsersService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service.getFollowers(userId, offset, count, fields, nameCase) .map(extractResponseWithErrorHandling()) @@ -79,7 +79,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : out: Int?, fields: String? ): Single> { - return provideService(IUsersService::class.java, TokenType.USER) + return provideService(IUsersService(), TokenType.USER) .flatMap { service -> service.getRequests(offset, count, extended, out, fields) .map(extractResponseWithErrorHandling()) @@ -121,7 +121,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : groupId: Int?, fromList: String? ): Single> { - return provideService(IUsersService::class.java, TokenType.USER) + return provideService(IUsersService(), TokenType.USER) .flatMap { service -> service .search( @@ -164,7 +164,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : } override fun report(userId: Int?, type: String?, comment: String?): Single { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUsersService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .report(userId, type, comment) @@ -173,7 +173,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : } override fun checkAndAddFriend(userId: Int?): Single { - return provideService(IUsersService::class.java, TokenType.USER) + return provideService(IUsersService(), TokenType.USER) .flatMap { service -> service .checkAndAddFriend( @@ -185,7 +185,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : } override fun getStory(owner_id: Int?, extended: Int?, fields: String?): Single { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUsersService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getStory(owner_id, extended, fields) .map(extractResponseWithErrorHandling()) @@ -197,7 +197,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : offset: Int?, count: Int? ): Single> { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUsersService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getNarratives(owner_id, offset, count) .map(extractResponseWithErrorHandling()) @@ -210,7 +210,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : fields: String? ): Single { val storyString = join(stories, ",") { AccessIdPair.format(it) } - return provideService(IUsersService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUsersService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getStoryById(storyString, extended, fields) .map(extractResponseWithErrorHandling()) @@ -218,7 +218,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : } override fun getGifts(user_id: Int?, count: Int?, offset: Int?): Single> { - return provideService(IUsersService::class.java, TokenType.USER) + return provideService(IUsersService(), TokenType.USER) .flatMap { service -> service.getGifts(user_id, count, offset) .map(extractResponseWithErrorHandling()) @@ -232,7 +232,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : extended: Int?, fields: String? ): Single { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUsersService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.searchStory(q, mentioned_id, count, extended, fields) .map(extractResponseWithErrorHandling()) @@ -253,7 +253,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : ids.add(join(domains, ",")) } return provideService( - IUsersService::class.java, + IUsersService(), TokenType.USER, TokenType.COMMUNITY, TokenType.SERVICE @@ -265,7 +265,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : } override fun stories_getPhotoUploadServer(): Single { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUsersService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.stories_getPhotoUploadServer(1) .map(extractResponseWithErrorHandling()) @@ -273,7 +273,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : } override fun stories_getVideoUploadServer(): Single { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUsersService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.stories_getVideoUploadServer(1) .map(extractResponseWithErrorHandling()) @@ -281,7 +281,7 @@ internal class UsersApi(accountId: Int, provider: IServiceProvider) : } override fun stories_save(upload_results: String?): Single> { - return provideService(IUsersService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUsersService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.stories_save(upload_results) .map(extractResponseWithErrorHandling()) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UtilsApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UtilsApi.kt index 2172fe948..87db88bd6 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UtilsApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/UtilsApi.kt @@ -16,7 +16,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : AbsApi(accountId, provider), IUtilsApi { override fun resolveScreenName(screenName: String?): Single { return provideService( - IUtilsService::class.java, + IUtilsService(), TokenType.USER, TokenType.COMMUNITY, TokenType.SERVICE @@ -29,7 +29,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : override fun getShortLink(url: String?, t_private: Int?): Single { return provideService( - IUtilsService::class.java, + IUtilsService(), TokenType.USER, TokenType.COMMUNITY, TokenType.SERVICE @@ -41,7 +41,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : } override fun getLastShortenedLinks(count: Int?, offset: Int?): Single> { - return provideService(IUtilsService::class.java, TokenType.USER) + return provideService(IUtilsService(), TokenType.USER) .flatMap { service -> service.getLastShortenedLinks(count, offset) .map(extractResponseWithErrorHandling()) @@ -49,7 +49,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : } override fun deleteFromLastShortened(key: String?): Single { - return provideService(IUtilsService::class.java, TokenType.USER) + return provideService(IUtilsService(), TokenType.USER) .flatMap { service -> service.deleteFromLastShortened(key) .map(extractResponseWithErrorHandling()) @@ -58,7 +58,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : override fun checkLink(url: String?): Single { return provideService( - IUtilsService::class.java, + IUtilsService(), TokenType.USER, TokenType.COMMUNITY, TokenType.SERVICE @@ -70,7 +70,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : } override fun joinChatByInviteLink(link: String?): Single { - return provideService(IUtilsService::class.java, TokenType.USER) + return provideService(IUtilsService(), TokenType.USER) .flatMap { service -> service.joinChatByInviteLink(link) .map(extractResponseWithErrorHandling()) @@ -78,7 +78,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : } override fun getInviteLink(peer_id: Int?, reset: Int?): Single { - return provideService(IUtilsService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IUtilsService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service.getInviteLink(peer_id, reset) .map(extractResponseWithErrorHandling()) @@ -87,7 +87,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : override fun customScript(code: String?): Single { return provideService( - IUtilsService::class.java, + IUtilsService(), TokenType.USER, TokenType.COMMUNITY, TokenType.SERVICE @@ -100,7 +100,7 @@ internal class UtilsApi(accountId: Int, provider: IServiceProvider) : override fun getServerTime(): Single { return provideService( - IUtilsService::class.java, + IUtilsService(), TokenType.USER, TokenType.COMMUNITY, TokenType.SERVICE diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/VideoApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/VideoApi.kt index 956c794dd..37ad8218c 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/VideoApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/VideoApi.kt @@ -15,7 +15,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : ownerId: Int?, videoId: Int, needLikes: Boolean?, startCommentId: Int?, offset: Int?, count: Int?, sort: String?, extended: Boolean?, fields: String? ): Single { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service .getComments( @@ -34,7 +34,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : } override fun addVideo(targetId: Int?, videoId: Int?, ownerId: Int?): Single { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service.addVideo(targetId, videoId, ownerId) .map(extractResponseWithErrorHandling()) @@ -42,7 +42,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : } override fun deleteVideo(videoId: Int?, ownerId: Int?, targetId: Int?): Single { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service.deleteVideo(videoId, ownerId, targetId) .map(extractResponseWithErrorHandling()) @@ -55,7 +55,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : count: Int?, needSystem: Boolean? ): Single> { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service.getAlbums(ownerId, offset, count, 1, integerFromBoolean(needSystem)) .map(extractResponseWithErrorHandling()) @@ -67,7 +67,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : owner_id: Int?, video_id: Int? ): Single> { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service.getAlbumsByVideo(target_id, owner_id, video_id, 1) .map(extractResponseWithErrorHandling()) @@ -79,7 +79,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : filters: String?, searchOwn: Boolean?, offset: Int?, longer: Int?, shorter: Int?, count: Int?, extended: Boolean? ): Single { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service .search( @@ -100,7 +100,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : } override fun restoreComment(ownerId: Int?, commentId: Int): Single { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service.restoreComment(ownerId, commentId) .map(extractResponseWithErrorHandling()) @@ -109,7 +109,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : } override fun deleteComment(ownerId: Int?, commentId: Int): Single { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service.deleteComment(ownerId, commentId) .map(extractResponseWithErrorHandling()) @@ -124,7 +124,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : ): Single> { val videos = join(ids, ",") { AccessIdPair.format(it) } - return provideService(IVideoService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IVideoService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service[ownerId, videos, albumId, count, offset, integerFromBoolean(extended)] .map(extractResponseWithErrorHandling()) @@ -139,7 +139,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : val atts = join(attachments, ",") { formatAttachmentToken(it) } - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service .createComment( @@ -155,7 +155,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : ownerId: Int, commentId: Int, message: String?, attachments: Collection? ): Single { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service.editComment( ownerId, @@ -172,7 +172,7 @@ internal class VideoApi(accountId: Int, provider: IServiceProvider) : override fun edit(ownerId: Int, video_id: Int, name: String?, desc: String?): Single { - return provideService(IVideoService::class.java, TokenType.USER) + return provideService(IVideoService(), TokenType.USER) .flatMap { service -> service.edit(ownerId, video_id, name, desc) .map(extractResponseWithErrorHandling()) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/VkApies.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/VkApies.kt index 8a04344a2..caab4f28b 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/VkApies.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/VkApies.kt @@ -2,10 +2,11 @@ package dev.ragnarok.fenrir.api.impl import android.annotation.SuppressLint import dev.ragnarok.fenrir.api.IServiceProvider -import dev.ragnarok.fenrir.api.IVkRetrofitProvider -import dev.ragnarok.fenrir.api.RetrofitWrapper +import dev.ragnarok.fenrir.api.IVkRestProvider import dev.ragnarok.fenrir.api.TokenType import dev.ragnarok.fenrir.api.interfaces.* +import dev.ragnarok.fenrir.api.rest.IServiceRest +import dev.ragnarok.fenrir.api.rest.SimplePostHttp import dev.ragnarok.fenrir.util.Utils import io.reactivex.rxjava3.core.Single @@ -13,7 +14,7 @@ internal class VkApies private constructor( accountId: Int, useCustomToken: Boolean, customAccessToken: String?, - provider: IVkRetrofitProvider + provider: IVkRestProvider ) : IAccountApis { private val messagesApi: IMessagesApi private val photosApi: IPhotosApi @@ -133,12 +134,12 @@ internal class VkApies private constructor( companion object { @SuppressLint("UseSparseArrays") private val APIS: MutableMap = HashMap(1) - fun create(accountId: Int, accessToken: String?, provider: IVkRetrofitProvider): VkApies { + fun create(accountId: Int, accessToken: String?, provider: IVkRestProvider): VkApies { return VkApies(accountId, true, accessToken, provider) } @Synchronized - operator fun get(accountId: Int, provider: IVkRetrofitProvider): VkApies { + operator fun get(accountId: Int, provider: IVkRestProvider): VkApies { var apies = APIS[accountId] if (apies == null) { apies = VkApies(accountId, false, null, provider) @@ -149,30 +150,33 @@ internal class VkApies private constructor( } init { - val retrofitProvider: IServiceProvider = object : IServiceProvider { - override fun provideService( + val restProvider: IServiceProvider = object : IServiceProvider { + override fun provideService( accountId: Int, - serviceClass: Class, + serviceClass: T, vararg tokenTypes: Int ): Single { - return provideRetrofit( + return provideRest( accountId, *tokenTypes - ).map { retrofit -> retrofit.create(serviceClass) } + ).map { + serviceClass.addon(it) + serviceClass + } } - fun provideRetrofit(aid: Int, vararg tokenPolicy: Int): Single { + fun provideRest(aid: Int, vararg tokenPolicy: Int): Single { if (useCustomToken) { - return provider.provideCustomRetrofit(aid, customAccessToken!!) + return provider.provideCustomRest(aid, customAccessToken!!) } val isCommunity = aid < 0 return if (isCommunity) { when { Utils.intValueIn(TokenType.COMMUNITY, *tokenPolicy) -> { - provider.provideNormalRetrofit(aid) + provider.provideNormalRest(aid) } Utils.intValueIn(TokenType.SERVICE, *tokenPolicy) -> { - provider.provideServiceRetrofit() + provider.provideServiceRest() } else -> { Single.error( @@ -185,10 +189,10 @@ internal class VkApies private constructor( } else { when { Utils.intValueIn(TokenType.USER, *tokenPolicy) -> { - provider.provideNormalRetrofit(aid) + provider.provideNormalRest(aid) } Utils.intValueIn(TokenType.SERVICE, *tokenPolicy) -> { - provider.provideServiceRetrofit() + provider.provideServiceRest() } else -> { Single.error( @@ -201,28 +205,28 @@ internal class VkApies private constructor( } } } - accountApi = AccountApi(accountId, retrofitProvider) - audioApi = AudioApi(accountId, retrofitProvider) - boardApi = BoardApi(accountId, retrofitProvider) - commentsApi = CommentsApi(accountId, retrofitProvider) - databaseApi = DatabaseApi(accountId, retrofitProvider) - docsApi = DocsApi(accountId, retrofitProvider) - faveApi = FaveApi(accountId, retrofitProvider) - friendsApi = FriendsApi(accountId, retrofitProvider) - groupsApi = GroupsApi(accountId, retrofitProvider) - likesApi = LikesApi(accountId, retrofitProvider) - messagesApi = MessagesApi(accountId, retrofitProvider) - newsfeedApi = NewsfeedApi(accountId, retrofitProvider) - notificationsApi = NotificationsApi(accountId, retrofitProvider) - pagesApi = PagesApi(accountId, retrofitProvider) - photosApi = PhotosApi(accountId, retrofitProvider) - pollsApi = PollsApi(accountId, retrofitProvider) - statusApi = StatusApi(accountId, retrofitProvider) - storeApi = StoreApi(accountId, retrofitProvider) - usersApi = UsersApi(accountId, retrofitProvider) - utilsApi = UtilsApi(accountId, retrofitProvider) - videoApi = VideoApi(accountId, retrofitProvider) - wallApi = WallApi(accountId, retrofitProvider) + accountApi = AccountApi(accountId, restProvider) + audioApi = AudioApi(accountId, restProvider) + boardApi = BoardApi(accountId, restProvider) + commentsApi = CommentsApi(accountId, restProvider) + databaseApi = DatabaseApi(accountId, restProvider) + docsApi = DocsApi(accountId, restProvider) + faveApi = FaveApi(accountId, restProvider) + friendsApi = FriendsApi(accountId, restProvider) + groupsApi = GroupsApi(accountId, restProvider) + likesApi = LikesApi(accountId, restProvider) + messagesApi = MessagesApi(accountId, restProvider) + newsfeedApi = NewsfeedApi(accountId, restProvider) + notificationsApi = NotificationsApi(accountId, restProvider) + pagesApi = PagesApi(accountId, restProvider) + photosApi = PhotosApi(accountId, restProvider) + pollsApi = PollsApi(accountId, restProvider) + statusApi = StatusApi(accountId, restProvider) + storeApi = StoreApi(accountId, restProvider) + usersApi = UsersApi(accountId, restProvider) + utilsApi = UtilsApi(accountId, restProvider) + videoApi = VideoApi(accountId, restProvider) + wallApi = WallApi(accountId, restProvider) otherApi = OtherApi(accountId, provider) } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/WallApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/WallApi.kt index cd9c750d4..7e4720b4d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/WallApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/WallApi.kt @@ -20,7 +20,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco extended: Boolean?, fields: String? ): Single { - return provideService(IWallService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IWallService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .search( @@ -45,7 +45,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco placeId: Int?, markAsAds: Boolean? ): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service .edit( @@ -71,7 +71,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun pin(ownerId: Int?, postId: Int): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service.pin(ownerId, postId) .map(extractResponseWithErrorHandling()) @@ -80,7 +80,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun unpin(ownerId: Int?, postId: Int): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service.unpin(ownerId, postId) .map(extractResponseWithErrorHandling()) @@ -96,7 +96,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco markAsAds: Boolean? ): Single { val `object` = "wall" + postOwnerId + "_" + postId - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service.repost(`object`, message, groupId, integerFromBoolean(markAsAds)) .map(extractResponseWithErrorHandling()) @@ -120,7 +120,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco markAsAds: Boolean?, adsPromotedStealth: Boolean? ): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service .post( @@ -149,7 +149,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun delete(ownerId: Int?, postId: Int): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service.delete(ownerId, postId) .map(extractResponseWithErrorHandling()) @@ -158,7 +158,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun restoreComment(ownerId: Int?, commentId: Int): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service.restoreComment(ownerId, commentId) .map(extractResponseWithErrorHandling()) @@ -167,7 +167,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun deleteComment(ownerId: Int?, commentId: Int): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service.deleteComment(ownerId, commentId) .map(extractResponseWithErrorHandling()) @@ -176,7 +176,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun restore(ownerId: Int?, postId: Int): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service.restore(ownerId, postId) .map(extractResponseWithErrorHandling()) @@ -188,7 +188,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco ownerId: Int?, commentId: Int, message: String?, attachments: Collection? ): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service.editComment( ownerId, @@ -208,7 +208,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco replyToComment: Int?, attachments: Collection?, stickerId: Int?, generatedUniqueId: Int? ): Single { - return provideService(IWallService::class.java, TokenType.USER, TokenType.COMMUNITY) + return provideService(IWallService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> service .createComment( @@ -238,7 +238,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco extended: Boolean?, fields: String? ): Single { - return provideService(IWallService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IWallService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service[ownerId, domain, offset, count, filter, if (extended != null) if (extended) 1 else 0 else null, fields] .map(extractResponseWithErrorHandling()) @@ -252,7 +252,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco fields: String? ): Single { val line = join(ids, ",") { orig -> orig.ownerId.toString() + "_" + orig.id } - return provideService(IWallService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IWallService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getById( @@ -266,7 +266,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun reportPost(owner_id: Int?, post_id: Int?, reason: Int?): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service .reportPost(owner_id, post_id, reason) @@ -275,7 +275,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun subscribe(owner_id: Int?): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service .subscribe(owner_id) @@ -284,7 +284,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun unsubscribe(owner_id: Int?): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service .unsubscribe(owner_id) @@ -293,7 +293,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco } override fun reportComment(owner_id: Int?, post_id: Int?, reason: Int?): Single { - return provideService(IWallService::class.java, TokenType.USER) + return provideService(IWallService(), TokenType.USER) .flatMap { service -> service .reportComment(owner_id, post_id, reason) @@ -306,7 +306,7 @@ internal class WallApi(accountId: Int, provider: IServiceProvider) : AbsApi(acco startCommentId: Int?, offset: Int?, count: Int?, sort: String?, extended: Boolean?, fields: String? ): Single { - return provideService(IWallService::class.java, TokenType.USER, TokenType.SERVICE) + return provideService(IWallService(), TokenType.USER, TokenType.SERVICE) .flatMap { service -> service .getComments( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/ILongpollApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/ILongpollApi.kt index 05890709f..153ea07ff 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/ILongpollApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/ILongpollApi.kt @@ -6,7 +6,7 @@ import io.reactivex.rxjava3.core.Single interface ILongpollApi { fun getUpdates( - server: String?, + server: String, key: String?, ts: Long, wait: Int, @@ -15,7 +15,7 @@ interface ILongpollApi { ): Single fun getGroupUpdates( - server: String?, + server: String, key: String?, ts: String?, wait: Int diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/INetworker.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/INetworker.kt index 8ec434786..ca29330ad 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/INetworker.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/INetworker.kt @@ -1,9 +1,9 @@ package dev.ragnarok.fenrir.api.interfaces -import dev.ragnarok.fenrir.api.IVkRetrofitProvider +import dev.ragnarok.fenrir.api.IVkRestProvider interface INetworker { - fun getVkRetrofitProvider(): IVkRetrofitProvider + fun getVkRestProvider(): IVkRestProvider fun vkDefault(accountId: Int): IAccountApis fun vkManual(accountId: Int, accessToken: String): IAccountApis fun vkDirectAuth(): IAuthApi diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/IUploadApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/IUploadApi.kt index 14a70f542..92d00ed6f 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/IUploadApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/IUploadApi.kt @@ -8,28 +8,28 @@ import java.io.InputStream interface IUploadApi { fun uploadDocumentRx( - server: String?, + server: String, filename: String?, doc: InputStream, listener: PercentagePublisher? ): Single fun uploadAudioRx( - server: String?, + server: String, filename: String?, `is`: InputStream, listener: PercentagePublisher? ): Single fun remotePlayAudioRx( - server: String?, + server: String, filename: String?, `is`: InputStream, listener: PercentagePublisher? ): Single> fun uploadStoryRx( - server: String?, + server: String, filename: String?, `is`: InputStream, listener: PercentagePublisher?, @@ -37,38 +37,38 @@ interface IUploadApi { ): Single> fun uploadVideoRx( - server: String?, + server: String, filename: String?, video: InputStream, listener: PercentagePublisher? ): Single fun uploadOwnerPhotoRx( - server: String?, + server: String, photo: InputStream, listener: PercentagePublisher? ): Single fun uploadChatPhotoRx( - server: String?, + server: String, photo: InputStream, listener: PercentagePublisher? ): Single fun uploadPhotoToWallRx( - server: String?, + server: String, photo: InputStream, listener: PercentagePublisher? ): Single fun uploadPhotoToMessageRx( - server: String?, + server: String, `is`: InputStream, listener: PercentagePublisher? ): Single fun uploadPhotoToAlbumRx( - server: String?, + server: String, file1: InputStream, listener: PercentagePublisher? ): Single diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/Error.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/Error.kt index b6c70722d..e546c0c5b 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/Error.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/Error.kt @@ -1,11 +1,10 @@ package dev.ragnarok.fenrir.api.model import dev.ragnarok.fenrir.orZero -import dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization.Serializer +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -import java.lang.reflect.Type @Serializable class Error { @@ -28,10 +27,7 @@ class Error { var requestParams: List? = null @Transient - var type: Type? = null - - @Transient - var serializer: Serializer? = null + var serializer: KSerializer<*>? = null fun requests(): HashMap { val params: HashMap = HashMap(requestParams?.size.orZero()) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/HttpCodeException.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/HttpException.kt similarity index 58% rename from app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/HttpCodeException.kt rename to app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/HttpException.kt index 605d6a60f..1b15e1079 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/HttpCodeException.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/HttpException.kt @@ -1,6 +1,6 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit +package dev.ragnarok.fenrir.api.rest -class HttpCodeException(val code: Int) : RuntimeException( +class HttpException(val code: Int) : RuntimeException( getMessage( code ) @@ -10,4 +10,4 @@ class HttpCodeException(val code: Int) : RuntimeException( return "HTTP $code" } } -} \ No newline at end of file +} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/IServiceRest.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/IServiceRest.kt new file mode 100644 index 000000000..322d1f80c --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/IServiceRest.kt @@ -0,0 +1,77 @@ +package dev.ragnarok.fenrir.api.rest + +import dev.ragnarok.fenrir.api.model.Items +import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.kJson +import kotlinx.serialization.KSerializer +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.serializer +import okhttp3.FormBody +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import java.lang.ref.WeakReference + +abstract class IServiceRest { + private var restClient: WeakReference? = null + + val rest: SimplePostHttp + get() = restClient?.get() ?: throw HttpException(-1) + + fun addon(client: SimplePostHttp?) { + restClient = WeakReference(client) + } + + companion object { + val baseInt: KSerializer> + get() = BaseResponse.serializer(Int.serializer()) + + val baseLong: KSerializer> + get() = BaseResponse.serializer(Long.serializer()) + + val baseString: KSerializer> + get() = BaseResponse.serializer(String.serializer()) + + inline fun base(serial: KSerializer): KSerializer> { + return BaseResponse.serializer(serial) + } + + inline fun baseList(serial: KSerializer): KSerializer>> { + return BaseResponse.serializer(ListSerializer(serial)) + } + + inline fun items(serial: KSerializer): KSerializer>> { + return BaseResponse.serializer(Items.serializer(serial)) + } + + private fun toSerialStr(obj: Any?): String? { + return when (obj) { + is String -> { + obj + } + is Byte, is Short, is Int, is Long, is Float, is Double -> { + obj.toString() + } + is Boolean -> { + if (obj) "1" else "0" + } + else -> null + } + } + + inline fun jsonForm(obj: T, serial: KSerializer): RequestBody { + return kJson.encodeToString(serial, obj) + .toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) + } + + fun form(vararg pairs: Pair): FormBody { + val formBuilder = FormBody.Builder() + for ((first, second) in pairs) { + toSerialStr(second)?.let { + formBuilder.add(first, it) + } + } + return formBuilder.build() + } + } +} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/SimplePostHttp.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/SimplePostHttp.kt new file mode 100644 index 000000000..239a8e2bd --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/rest/SimplePostHttp.kt @@ -0,0 +1,137 @@ +package dev.ragnarok.fenrir.api.rest + +import dev.ragnarok.fenrir.api.HttpLoggerAndParser +import dev.ragnarok.fenrir.api.model.Params +import dev.ragnarok.fenrir.api.model.response.VkResponse +import dev.ragnarok.fenrir.ifNonNull +import dev.ragnarok.fenrir.isMsgPack +import dev.ragnarok.fenrir.kJson +import dev.ragnarok.fenrir.util.serializeble.json.decodeFromStream +import dev.ragnarok.fenrir.util.serializeble.msgpack.MsgPack +import io.reactivex.rxjava3.core.Single +import kotlinx.serialization.KSerializer +import okhttp3.* + +class SimplePostHttp( + private val baseUrl: String?, + okHttpClient: OkHttpClient.Builder +) { + private val client = okHttpClient.build() + + fun stop() { + client.dispatcher.cancelAll() + } + + fun requestFullUrl( + url: String, + body: RequestBody?, + serial: KSerializer, + onlySuccessful: Boolean = true + ): Single { + return requestInternal( + url, + body, + serial, onlySuccessful + ) + } + + fun request( + methodOrFullUrl: String, + body: RequestBody?, + serial: KSerializer, + onlySuccessful: Boolean = true + ): Single { + return requestInternal( + if (baseUrl.isNullOrEmpty()) methodOrFullUrl else "$baseUrl/$methodOrFullUrl", + body, + serial, onlySuccessful + ) + } + + private fun requestInternal( + url: String, + body: RequestBody?, + serial: KSerializer, + onlySuccessful: Boolean + ): Single { + return Single.create { emitter -> + val request = Request.Builder() + .url( + url + ) + body.ifNonNull( + { request.post(it) }, { request.get() } + ) + val call = client.newCall(request.build()) + emitter.setCancellable { call.cancel() } + try { + val response = call.execute() + if (!response.isSuccessful && onlySuccessful) { + emitter.tryOnError(HttpException(response.code)) + } else { + val ret = if (response.body.isMsgPack()) MsgPack().decodeFromOkioStream( + serial, response.body.source() + ) else kJson.decodeFromStream( + serial, response.body.byteStream() + ) + if (ret is VkResponse) { + ret.error?.let { + it.serializer = serial + val o: ArrayList = when (val stmp = response.request.body) { + is FormBody -> { + val f = ArrayList(stmp.size) + for (i in 0 until stmp.size) { + val tmp = Params() + tmp.key = stmp.name(i) + tmp.value = stmp.value(i) + f.add(tmp) + } + f + } + is HttpLoggerAndParser.GzipFormBody -> { + val f = ArrayList(stmp.original.size) + f.addAll(stmp.original) + f + } + else -> { + ArrayList() + } + } + val tmp = Params() + tmp.key = "post_url" + tmp.value = response.request.url.toString() + o.add(tmp) + it.requestParams = o + } + } + emitter.onSuccess( + ret + ) + } + response.close() + } catch (e: Exception) { + emitter.tryOnError(e) + } + } + } + + fun doMultipartForm( + methodOrFullUrl: String, + part: MultipartBody.Part, + serial: KSerializer, onlySuccessful: Boolean = true + ): Single { + val requestBodyMultipart: RequestBody = + MultipartBody.Builder().setType(MultipartBody.FORM).addPart(part).build() + return request(methodOrFullUrl, requestBodyMultipart, serial, onlySuccessful) + } + + fun doMultipartFormFullUrl( + url: String, + part: MultipartBody.Part, + serial: KSerializer, onlySuccessful: Boolean = true + ): Single { + val requestBodyMultipart: RequestBody = + MultipartBody.Builder().setType(MultipartBody.FORM).addPart(part).build() + return requestFullUrl(url, requestBodyMultipart, serial, onlySuccessful) + } +} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAccountService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAccountService.kt index fbe44595a..a3c132d4e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAccountService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAccountService.kt @@ -5,28 +5,30 @@ import dev.ragnarok.fenrir.api.model.RefreshToken import dev.ragnarok.fenrir.api.model.VKApiProfileInfo import dev.ragnarok.fenrir.api.model.VKApiProfileInfoResponse import dev.ragnarok.fenrir.api.model.response.* +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.GET -import retrofit2.http.POST -interface IAccountService { - @POST("account.ban") - @FormUrlEncoded - fun ban(@Field("owner_id") owner_id: Int): Single> +class IAccountService : IServiceRest() { + fun ban(owner_id: Int): Single> { + return rest.request("account.ban", form("owner_id" to owner_id), baseInt) + } - @POST("account.unban") - @FormUrlEncoded - fun unban(@Field("owner_id") owner_id: Int): Single> + fun unban(owner_id: Int): Single> { + return rest.request("account.unban", form("owner_id" to owner_id), baseInt) + } - @POST("account.getBanned") - @FormUrlEncoded fun getBanned( - @Field("count") count: Int?, - @Field("offset") offset: Int?, - @Field("fields") fields: String? - ): Single> + count: Int?, + offset: Int?, + fields: String? + ): Single> { + return rest.request( + "account.getBanned", + form("count" to count, "offset" to offset, "fields" to fields), + base(AccountsBannedResponse.serializer()) + ) + } + //https://vk.com/dev/account.getCounters /** * @param filter friends — новые заявки в друзья; @@ -41,81 +43,131 @@ interface IAccountService { * sdk — запросы в мобильных играх; * app_requests — уведомления от приложений. */ - @POST("account.getCounters") - @FormUrlEncoded - fun getCounters(@Field("filter") filter: String?): Single> + fun getCounters(filter: String?): Single> { + return rest.request( + "account.getCounters", + form("filter" to filter), + base(CountersDto.serializer()) + ) + } //https://vk.com/dev/account.unregisterDevice - @FormUrlEncoded - @POST("account.unregisterDevice") - fun unregisterDevice(@Field("device_id") deviceId: String?): Single> + fun unregisterDevice(deviceId: String?): Single> { + return rest.request("account.unregisterDevice", form("device_id" to deviceId), baseInt) + } //https://vk.com/dev/account.registerDevice - @FormUrlEncoded - @POST("account.registerDevice") fun registerDevice( - @Field("token") token: String?, - @Field("pushes_granted") pushes_granted: Int?, - @Field("app_version") app_version: String?, - @Field("push_provider") push_provider: String?, - @Field("companion_apps") companion_apps: String?, - @Field("type") type: Int?, - @Field("device_model") deviceModel: String?, - @Field("device_id") deviceId: String?, - @Field("system_version") systemVersion: String?, - @Field("settings") settings: String? - ): Single> + token: String?, + pushes_granted: Int?, + app_version: String?, + push_provider: String?, + companion_apps: String?, + type: Int?, + deviceModel: String?, + deviceId: String?, + systemVersion: String?, + settings: String? + ): Single> { + return rest.request( + "account.registerDevice", + form( + "token" to token, + "pushes_granted" to pushes_granted, + "app_version" to app_version, + "push_provider" to push_provider, + "companion_apps" to companion_apps, + "type" to type, + "device_model" to deviceModel, + "device_id" to deviceId, + "system_version" to systemVersion, + "settings" to settings + ), baseInt + ) + } /** * Marks a current user as offline. * * @return In case of success returns 1. */ - @GET("account.setOffline") - fun setOffline(): Single> + val setOffline: Single> + get() = rest.request("account.setOffline", null, baseInt) - @get:GET("account.getProfileInfo") val profileInfo: Single> + get() = rest.request("account.getProfileInfo", null, base(VKApiProfileInfo.serializer())) - @get:GET("account.getPushSettings") val pushSettings: Single> + get() = rest.request( + "account.getPushSettings", + null, + base(PushSettingsResponse.serializer()) + ) - @FormUrlEncoded - @POST("account.saveProfileInfo") fun saveProfileInfo( - @Field("first_name") first_name: String?, - @Field("last_name") last_name: String?, - @Field("maiden_name") maiden_name: String?, - @Field("screen_name") screen_name: String?, - @Field("bdate") bdate: String?, - @Field("home_town") home_town: String?, - @Field("sex") sex: Int? - ): Single> + first_name: String?, + last_name: String?, + maiden_name: String?, + screen_name: String?, + bdate: String?, + home_town: String?, + sex: Int? + ): Single> { + return rest.request( + "account.saveProfileInfo", + form( + "first_name" to first_name, + "last_name" to last_name, + "maiden_name" to maiden_name, + "screen_name" to screen_name, + "bdate" to bdate, + "home_town" to home_town, + "sex" to sex + ), base(VKApiProfileInfoResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("auth.refreshToken") fun refreshToken( - @Field("receipt") receipt: String?, - @Field("receipt2") receipt2: String?, - @Field("nonce") nonce: String?, - @Field("timestamp") timestamp: Long? - ): Single> + receipt: String?, + receipt2: String?, + nonce: String?, + timestamp: Long? + ): Single> { + return rest.request( + "auth.refreshToken", + form( + "receipt" to receipt, + "receipt2" to receipt2, + "nonce" to nonce, + "timestamp" to timestamp + ), + base(RefreshToken.serializer()) + ) + } - @FormUrlEncoded - @POST("account.importMessagesContacts") - fun importMessagesContacts( - @Field("contacts") contacts: String? - ): Single + val resetMessagesContacts: Single> + get() = rest.request("account.resetMessagesContacts", null, baseInt) - @GET("account.resetMessagesContacts") - fun resetMessagesContacts(): Single> + fun importMessagesContacts( + contacts: String? + ): Single { + return rest.request( + "account.importMessagesContacts", + form("contacts" to contacts), + VkResponse.serializer() + ) + } - @FormUrlEncoded - @POST("account.getContactList") fun getContactList( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + offset: Int?, + count: Int?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "account.getContactList", + form("offset" to offset, "count" to count, "extended" to extended, "fields" to fields), + base(ContactsResponse.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAudioService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAudioService.kt index e7dfb889a..72eeb3c78 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAudioService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAudioService.kt @@ -4,78 +4,120 @@ import dev.ragnarok.fenrir.api.model.* import dev.ragnarok.fenrir.api.model.catalog_v2_audio.VKApiCatalogV2BlockResponse import dev.ragnarok.fenrir.api.model.catalog_v2_audio.VKApiCatalogV2ListResponse import dev.ragnarok.fenrir.api.model.catalog_v2_audio.VKApiCatalogV2SectionResponse -import dev.ragnarok.fenrir.api.model.response.* +import dev.ragnarok.fenrir.api.model.response.AddToPlaylistResponse +import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.api.model.response.ServicePlaylistResponse import dev.ragnarok.fenrir.api.model.server.VKApiAudioUploadServer +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST +import kotlinx.serialization.builtins.serializer -interface IAudioService { - @FormUrlEncoded - @POST("audio.setBroadcast") +class IAudioService : IServiceRest() { fun setBroadcast( - @Field("audio") audio: String?, - @Field("target_ids") targetIds: String? - ): Single>> + audio: String?, + targetIds: String? + ): Single>> { + return rest.request( + "audio.setBroadcast", form("audio" to audio, "target_ids" to targetIds), baseList( + Int.serializer() + ) + ) + } //https://vk.com/dev/audio.search - @FormUrlEncoded - @POST("audio.search") fun search( - @Field("q") query: String?, - @Field("auto_complete") autoComplete: Int?, - @Field("lyrics") lyrics: Int?, - @Field("performer_only") performerOnly: Int?, - @Field("sort") sort: Int?, - @Field("search_own") searchOwn: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + query: String?, + autoComplete: Int?, + lyrics: Int?, + performerOnly: Int?, + sort: Int?, + searchOwn: Int?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "audio.search", + form( + "q" to query, + "auto_complete" to autoComplete, + "lyrics" to lyrics, + "performer_only" to performerOnly, + "sort" to sort, + "search_own" to searchOwn, + "offset" to offset, + "count" to count + ), items(VKApiAudio.serializer()) + ) + } //https://vk.com/dev/audio.searchArtists - @FormUrlEncoded - @POST("audio.searchArtists") fun searchArtists( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + query: String?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "audio.searchArtists", + form("q" to query, "offset" to offset, "count" to count), + items(VKApiArtist.serializer()) + ) + } //https://vk.com/dev/audio.searchPlaylists - @FormUrlEncoded - @POST("audio.searchPlaylists") fun searchPlaylists( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + query: String?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "audio.searchPlaylists", + form("q" to query, "offset" to offset, "count" to count), + items(VKApiAudioPlaylist.serializer()) + ) + } //https://vk.com/dev/audio.restore - @FormUrlEncoded - @POST("audio.restore") fun restore( - @Field("audio_id") audioId: Int, - @Field("owner_id") ownerId: Int? - ): Single> + audioId: Int, + ownerId: Int? + ): Single> { + return rest.request( + "audio.restore", + form("audio_id" to audioId, "owner_id" to ownerId), + base(VKApiAudio.serializer()) + ) + } //https://vk.com/dev/audio.delete - @FormUrlEncoded - @POST("audio.delete") fun delete( - @Field("audio_id") audioId: Int, - @Field("owner_id") ownerId: Int - ): Single> + audioId: Int, + ownerId: Int + ): Single> { + return rest.request( + "audio.delete", + form("audio_id" to audioId, "owner_id" to ownerId), + baseInt + ) + } //https://vk.com/dev/audio.add - @FormUrlEncoded - @POST("audio.add") fun add( - @Field("audio_id") audioId: Int, - @Field("owner_id") ownerId: Int, - @Field("group_id") groupId: Int?, - @Field("access_key") accessKey: String? - ): Single> + audioId: Int, + ownerId: Int, + groupId: Int?, + accessKey: String? + ): Single> { + return rest.request( + "audio.add", + form( + "audio_id" to audioId, + "owner_id" to ownerId, + "group_id" to groupId, + "access_key" to accessKey + ), + baseInt + ) + } /** * Returns a list of audio files of a user or community. @@ -88,213 +130,347 @@ interface IAudioService { * @return Returns the total results number in count field and an array of objects describing audio in items field. */ //https://vk.com/dev/audio.get - @FormUrlEncoded - @POST("audio.get") operator fun get( - @Field("playlist_id") playlist_id: Int?, - @Field("owner_id") ownerId: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("access_key") accessKey: String? - ): Single>> + playlist_id: Int?, + ownerId: Int?, + offset: Int?, + count: Int?, + accessKey: String? + ): Single>> { + return rest.request( + "audio.get", + form( + "playlist_id" to playlist_id, + "owner_id" to ownerId, + "offset" to offset, + "count" to count, + "access_key" to accessKey + ), items(VKApiAudio.serializer()) + ) + } //https://vk.com/dev/audio.getAudiosByArtist - @FormUrlEncoded - @POST("audio.getAudiosByArtist") fun getAudiosByArtist( - @Field("artist_id") artist_id: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + artist_id: String?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "audio.getAudiosByArtist", + form("artist_id" to artist_id, "offset" to offset, "count" to count), + items(VKApiAudio.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.getPopular") fun getPopular( - @Field("only_eng") foreign: Int?, - @Field("genre_id") genre: Int?, - @Field("count") count: Int? - ): Single>> + only_eng: Int?, + genre: Int?, + count: Int? + ): Single>> { + return rest.request( + "audio.getPopular", + form("only_eng" to only_eng, "genre_id" to genre, "count" to count), + baseList(VKApiAudio.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.getRecommendations") fun getRecommendations( - @Field("user_id") user_id: Int?, - @Field("count") count: Int? - ): Single>> + user_id: Int?, + count: Int? + ): Single>> { + return rest.request( + "audio.getRecommendations", + form("user_id" to user_id, "count" to count), + items(VKApiAudio.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.getRecommendations") fun getRecommendationsByAudio( - @Field("target_audio") audio: String?, - @Field("count") count: Int? - ): Single>> + audio: String?, + count: Int? + ): Single>> { + return rest.request( + "audio.getRecommendations", + form("target_audio" to audio, "count" to count), + items(VKApiAudio.serializer()) + ) + } + + fun getById(audios: String?): Single>> { + return rest.request( + "audio.getById", + form("audios" to audios), + baseList(VKApiAudio.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.getById") - fun getById(@Field("audios") audios: String?): Single>> - - @FormUrlEncoded - @POST("audio.getById") fun getByIdVersioned( - @Field("audios") audios: String?, - @Field("v") version: String? - ): Single>> - - @FormUrlEncoded - @POST("audio.getLyrics") - fun getLyrics(@Field("lyrics_id") lyrics_id: Int): Single> + audios: String?, + version: String? + ): Single>> { + return rest.request( + "audio.getById", + form("audios" to audios, "v" to version), + baseList(VKApiAudio.serializer()) + ) + } + + fun getLyrics(lyrics_id: Int): Single> { + return rest.request( + "audio.getLyrics", + form("lyrics_id" to lyrics_id), + base(VKApiLyrics.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.getPlaylists") fun getPlaylists( - @Field("owner_id") owner_id: Int, - @Field("offset") offset: Int, - @Field("count") count: Int - ): Single>> + owner_id: Int, + offset: Int, + count: Int + ): Single>> { + return rest.request( + "audio.getPlaylists", + form("owner_id" to owner_id, "offset" to offset, "count" to count), + items(VKApiAudioPlaylist.serializer()) + ) + } + + fun getPlaylistsCustom(code: String?): Single { + return rest.request("execute", form("code" to code), ServicePlaylistResponse.serializer()) + } - @FormUrlEncoded - @POST("execute") - fun getPlaylistsCustom(@Field("code") code: String?): Single - - @FormUrlEncoded - @POST("audio.deletePlaylist") fun deletePlaylist( - @Field("playlist_id") playlist_id: Int, - @Field("owner_id") ownerId: Int - ): Single> + playlist_id: Int, + ownerId: Int + ): Single> { + return rest.request( + "audio.deletePlaylist", + form("playlist_id" to playlist_id, "owner_id" to ownerId), + baseInt + ) + } - @FormUrlEncoded - @POST("audio.followPlaylist") fun followPlaylist( - @Field("playlist_id") playlist_id: Int, - @Field("owner_id") ownerId: Int, - @Field("access_key") accessKey: String? - ): Single> + playlist_id: Int, + ownerId: Int, + accessKey: String? + ): Single> { + return rest.request( + "audio.followPlaylist", + form("playlist_id" to playlist_id, "owner_id" to ownerId, "access_key" to accessKey), + base(VKApiAudioPlaylist.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.savePlaylistAsCopy") fun clonePlaylist( - @Field("playlist_id") playlist_id: Int, - @Field("owner_id") ownerId: Int - ): Single> + playlist_id: Int, + ownerId: Int + ): Single> { + return rest.request( + "audio.savePlaylistAsCopy", + form("playlist_id" to playlist_id, "owner_id" to ownerId), + base(VKApiAudioPlaylist.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.getPlaylistById") fun getPlaylistById( - @Field("playlist_id") playlist_id: Int, - @Field("owner_id") ownerId: Int, - @Field("access_key") accessKey: String? - ): Single> + playlist_id: Int, + ownerId: Int, + accessKey: String? + ): Single> { + return rest.request( + "audio.getPlaylistById", + form("playlist_id" to playlist_id, "owner_id" to ownerId, "access_key" to accessKey), + base(VKApiAudioPlaylist.serializer()) + ) + } - @get:POST("audio.getUploadServer") val uploadServer: Single> + get() = rest.request( + "audio.getUploadServer", + null, + base(VKApiAudioUploadServer.serializer()) + ) - @FormUrlEncoded - @POST("audio.save") fun save( - @Field("server") server: String?, - @Field("audio") audio: String?, - @Field("hash") hash: String?, - @Field("artist") artist: String?, - @Field("title") title: String? - ): Single> - - @FormUrlEncoded - @POST("audio.edit") + server: String?, + audio: String?, + hash: String?, + artist: String?, + title: String? + ): Single> { + return rest.request( + "audio.save", + form( + "server" to server, + "audio" to audio, + "hash" to hash, + "artist" to artist, + "title" to title + ), + base(VKApiAudio.serializer()) + ) + } + fun edit( - @Field("owner_id") ownerId: Int, - @Field("audio_id") audioId: Int, - @Field("artist") artist: String?, - @Field("title") title: String?, - @Field("text") text: String? - ): Single> - - @FormUrlEncoded - @POST("audio.createPlaylist") + ownerId: Int, + audioId: Int, + artist: String?, + title: String?, + text: String? + ): Single> { + return rest.request( + "audio.edit", + form( + "owner_id" to ownerId, + "audio_id" to audioId, + "artist" to artist, + "title" to title, + "text" to text + ), + baseInt + ) + } + fun createPlaylist( - @Field("owner_id") ownerId: Int, - @Field("title") title: String?, - @Field("description") description: String? - ): Single> + ownerId: Int, + title: String?, + description: String? + ): Single> { + return rest.request( + "audio.createPlaylist", + form("owner_id" to ownerId, "title" to title, "description" to description), + base(VKApiAudioPlaylist.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.editPlaylist") fun editPlaylist( - @Field("owner_id") ownerId: Int, - @Field("playlist_id") playlist_id: Int, - @Field("title") title: String?, - @Field("description") description: String? - ): Single> - - @FormUrlEncoded - @POST("audio.removeFromPlaylist") + ownerId: Int, + playlist_id: Int, + title: String?, + description: String? + ): Single> { + return rest.request( + "audio.editPlaylist", + form( + "owner_id" to ownerId, + "playlist_id" to playlist_id, + "title" to title, + "description" to description + ), baseInt + ) + } + fun removeFromPlaylist( - @Field("owner_id") ownerId: Int, - @Field("playlist_id") playlist_id: Int, - @Field("audio_ids") audio_ids: String? - ): Single> + ownerId: Int, + playlist_id: Int, + audio_ids: String? + ): Single> { + return rest.request( + "audio.removeFromPlaylist", + form("owner_id" to ownerId, "playlist_id" to playlist_id, "audio_ids" to audio_ids), + baseInt + ) + } - @FormUrlEncoded - @POST("audio.addToPlaylist") fun addToPlaylist( - @Field("owner_id") ownerId: Int, - @Field("playlist_id") playlist_id: Int, - @Field("audio_ids") audio_ids: String? - ): Single>> + ownerId: Int, + playlist_id: Int, + audio_ids: String? + ): Single>> { + return rest.request( + "audio.addToPlaylist", + form("owner_id" to ownerId, "playlist_id" to playlist_id, "audio_ids" to audio_ids), + baseList(AddToPlaylistResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.reorder") fun reorder( - @Field("owner_id") ownerId: Int, - @Field("audio_id") audio_id: Int, - @Field("before") before: Int?, - @Field("after") after: Int? - ): Single> - - @FormUrlEncoded - @POST("stats.trackEvents") - fun trackEvents(@Field("events") events: String?): Single> - - @FormUrlEncoded - @POST("catalog.getAudio") + ownerId: Int, + audio_id: Int, + before: Int?, + after: Int? + ): Single> { + return rest.request( + "audio.reorder", + form( + "owner_id" to ownerId, + "audio_id" to audio_id, + "before" to before, + "after" to after + ), + baseInt + ) + } + + fun trackEvents(events: String?): Single> { + return rest.request("stats.trackEvents", form("events" to events), baseInt) + } + fun getCatalogV2Sections( - @Field("owner_id") owner_id: Int, - @Field("need_blocks") need_blocks: Int, - @Field("url") url: String? - ): Single> + owner_id: Int, + need_blocks: Int, + url: String? + ): Single> { + return rest.request( + "catalog.getAudio", + form("owner_id" to owner_id, "need_blocks" to need_blocks, "url" to url), + base(VKApiCatalogV2ListResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("catalog.getAudioArtist") fun getCatalogV2Artist( - @Field("artist_id") artist_id: String, - @Field("need_blocks") need_blocks: Int - ): Single> + artist_id: String, + need_blocks: Int + ): Single> { + return rest.request( + "catalog.getAudioArtist", + form("artist_id" to artist_id, "need_blocks" to need_blocks), + base(VKApiCatalogV2ListResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("catalog.getSection") fun getCatalogV2Section( - @Field("section_id") section_id: String, - @Field("start_from") start_from: String? - ): Single> + section_id: String, + start_from: String? + ): Single> { + return rest.request( + "catalog.getSection", + form("section_id" to section_id, "start_from" to start_from), + base(VKApiCatalogV2SectionResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("catalog.getBlockItems") fun getCatalogV2BlockItems( - @Field("block_id") block_id: String, - @Field("start_from") start_from: String? - ): Single> + block_id: String, + start_from: String? + ): Single> { + return rest.request( + "catalog.getBlockItems", + form("block_id" to block_id, "start_from" to start_from), + base(VKApiCatalogV2BlockResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("catalog.getAudioSearch") fun getCatalogV2AudioSearch( - @Field("query") query: String?, - @Field("context") context: String?, - @Field("need_blocks") need_blocks: Int - ): Single> + query: String?, + context: String?, + need_blocks: Int + ): Single> { + return rest.request( + "catalog.getAudioSearch", + form("query" to query, "context" to context, "need_blocks" to need_blocks), + base(VKApiCatalogV2ListResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.getArtistById") fun getArtistById( - @Field("artist_id") artist_id: String - ): Single> + artist_id: String + ): Single> { + return rest.request( + "audio.getArtistById", + form("artist_id" to artist_id), + base(ArtistInfo.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAuthService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAuthService.kt index 90b271418..b0fb3b49f 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAuthService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IAuthService.kt @@ -3,38 +3,64 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.LoginResponse import dev.ragnarok.fenrir.api.model.VKApiValidationResponse import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IAuthService { - @FormUrlEncoded - @POST("token") +class IAuthService : IServiceRest() { fun directLogin( - @Field("grant_type") grantType: String?, - @Field("client_id") clientId: Int, - @Field("client_secret") clientSecret: String?, - @Field("username") username: String?, - @Field("password") password: String?, - @Field("v") v: String?, - @Field("2fa_supported") twoFaSupported: Int?, - @Field("scope") scope: String?, - @Field("code") smscode: String?, - @Field("captcha_sid") captchaSid: String?, - @Field("captcha_key") captchaKey: String?, - @Field("force_sms") forceSms: Int?, - @Field("device_id") device_id: String?, - @Field("libverify_support") libverify_support: Int? - ): Single + grantType: String?, + clientId: Int, + clientSecret: String?, + username: String?, + password: String?, + v: String?, + twoFaSupported: Int?, + scope: String?, + smscode: String?, + captchaSid: String?, + captchaKey: String?, + forceSms: Int?, + device_id: String?, + libverify_support: Int? + ): Single { + return rest.request( + "token", + form( + "grant_type" to grantType, + "client_id" to clientId, + "client_secret" to clientSecret, + "username" to username, + "password" to password, + "v" to v, + "2fa_supported" to twoFaSupported, + "scope" to scope, + "code" to smscode, + "captcha_sid" to captchaSid, + "captcha_key" to captchaKey, + "force_sms" to forceSms, + "device_id" to device_id, + "libverify_support" to libverify_support + ), LoginResponse.serializer(), false + ) + } - @FormUrlEncoded - @POST("auth.validatePhone") fun validatePhone( - @Field("api_id") apiId: Int, - @Field("client_id") clientId: Int, - @Field("client_secret") clientSecret: String?, - @Field("sid") sid: String?, - @Field("v") v: String? - ): Single> + apiId: Int, + clientId: Int, + clientSecret: String?, + sid: String?, + v: String? + ): Single> { + return rest.request( + "auth.validatePhone", + form( + "api_id" to apiId, + "client_id" to clientId, + "client_secret" to clientSecret, + "sid" to sid, + "v" to v + ), + base(VKApiValidationResponse.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IBoardService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IBoardService.kt index 7755291e0..11b215f31 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IBoardService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IBoardService.kt @@ -3,44 +3,63 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.DefaultCommentsResponse import dev.ragnarok.fenrir.api.model.response.TopicsResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IBoardService { +class IBoardService : IServiceRest() { //https://vk.com/dev/board.getComments - @FormUrlEncoded - @POST("board.getComments") fun getComments( - @Field("group_id") groupId: Int, - @Field("topic_id") topicId: Int, - @Field("need_likes") needLikes: Int?, - @Field("start_comment_id") startCommentId: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("extended") extended: Int?, - @Field("sort") sort: String?, - @Field("fields") fields: String? - ): Single> + groupId: Int, + topicId: Int, + needLikes: Int?, + startCommentId: Int?, + offset: Int?, + count: Int?, + extended: Int?, + sort: String?, + fields: String? + ): Single> { + return rest.request( + "board.getComments", + form( + "group_id" to groupId, + "topic_id" to topicId, + "need_likes" to needLikes, + "start_comment_id" to startCommentId, + "offset" to offset, + "count" to count, + "extended" to extended, + "sort" to sort, + "fields" to fields + ), base(DefaultCommentsResponse.serializer()) + ) + } //https://vk.com/dev/board.restoreComment - @FormUrlEncoded - @POST("board.restoreComment") fun restoreComment( - @Field("group_id") groupId: Int, - @Field("topic_id") topicId: Int, - @Field("comment_id") commentId: Int - ): Single> + groupId: Int, + topicId: Int, + commentId: Int + ): Single> { + return rest.request( + "board.restoreComment", + form("group_id" to groupId, "topic_id" to topicId, "comment_id" to commentId), + baseInt + ) + } //https://vk.com/dev/board.deleteComment - @FormUrlEncoded - @POST("board.deleteComment") fun deleteComment( - @Field("group_id") groupId: Int, - @Field("topic_id") topicId: Int, - @Field("comment_id") commentId: Int - ): Single> + groupId: Int, + topicId: Int, + commentId: Int + ): Single> { + return rest.request( + "board.deleteComment", + form("group_id" to groupId, "topic_id" to topicId, "comment_id" to commentId), + baseInt + ) + } /** * Returns a list of topics on a community's discussion board. @@ -68,19 +87,32 @@ interface IBoardService { * To preview the full comment, specify 0. Default 90 * @return array of objects describing topics. */ - @FormUrlEncoded - @POST("board.getTopics") fun getTopics( - @Field("group_id") groupId: Int, - @Field("topic_ids") topicIds: String?, - @Field("order") order: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("extended") extended: Int?, - @Field("preview") preview: Int?, - @Field("preview_length") previewLength: Int?, - @Field("fields") fields: String? - ): Single> // not doccumented + groupId: Int, + topicIds: String?, + order: Int?, + offset: Int?, + count: Int?, + extended: Int?, + preview: Int?, + previewLength: Int?, + fields: String? + ): Single> { + return rest.request( + "board.getTopics", + form( + "group_id" to groupId, + "topic_ids" to topicIds, + "order" to order, + "offset" to offset, + "count" to count, + "extended" to extended, + "preview" to preview, + "preview_length" to previewLength, + "fields" to fields + ), base(TopicsResponse.serializer()) + ) + } /** * Edits a comment on a topic on a community's discussion board. @@ -103,25 +135,45 @@ interface IBoardService { * List of comma-separated words * @return 1 */ - @FormUrlEncoded - @POST("board.editComment") fun editComment( - @Field("group_id") groupId: Int, - @Field("topic_id") topicId: Int, - @Field("comment_id") commentId: Int, - @Field("message") message: String?, - @Field("attachments") attachments: String? - ): Single> + groupId: Int, + topicId: Int, + commentId: Int, + message: String?, + attachments: String? + ): Single> { + return rest.request( + "board.editComment", + form( + "group_id" to groupId, + "topic_id" to topicId, + "comment_id" to commentId, + "message" to message, + "attachments" to attachments + ), baseInt + ) + } - @FormUrlEncoded - @POST("board.addComment") fun addComment( - @Field("group_id") groupId: Int?, - @Field("topic_id") topicId: Int, - @Field("message") message: String?, - @Field("attachments") attachments: String?, - @Field("from_group") fromGroup: Int?, - @Field("sticker_id") stickerId: Int?, - @Field("guid") generatedUniqueId: Int? - ): Single> + groupId: Int?, + topicId: Int, + message: String?, + attachments: String?, + fromGroup: Int?, + stickerId: Int?, + generatedUniqueId: Int? + ): Single> { + return rest.request( + "board.addComment", + form( + "group_id" to groupId, + "topic_id" to topicId, + "message" to message, + "attachments" to attachments, + "from_group" to fromGroup, + "sticker_id" to stickerId, + "guid" to generatedUniqueId + ), baseInt + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ICommentsService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ICommentsService.kt index e9acf97e9..3658b9ff4 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ICommentsService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ICommentsService.kt @@ -2,25 +2,38 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.CustomCommentsResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface ICommentsService { - @FormUrlEncoded - @POST("execute") +class ICommentsService : IServiceRest() { operator fun get( - @Field("code") code: String?, - @Field("source_type") sourceType: String?, - @Field("owner_id") ownerId: Int, - @Field("source_id") sourceId: Int, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("sort") sort: String?, - @Field("start_comment_id") startCommentId: Int?, - @Field("comment_id") thread_id: Int, - @Field("access_key") accessKey: String?, - @Field("fields") fields: String? - ): Single> + code: String?, + sourceType: String?, + ownerId: Int, + sourceId: Int, + offset: Int?, + count: Int?, + sort: String?, + startCommentId: Int?, + comment_id: Int, + accessKey: String?, + fields: String? + ): Single> { + return rest.request( + "execute", + form( + "code" to code, + "source_type" to sourceType, + "owner_id" to ownerId, + "source_id" to sourceId, + "offset" to offset, + "count" to count, + "sort" to sort, + "start_comment_id" to startCommentId, + "comment_id" to comment_id, + "access_key" to accessKey, + "fields" to fields + ), base(CustomCommentsResponse.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IDatabaseService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IDatabaseService.kt index 54d011954..55431f4ff 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IDatabaseService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IDatabaseService.kt @@ -5,16 +5,18 @@ import dev.ragnarok.fenrir.api.model.VKApiCity import dev.ragnarok.fenrir.api.model.VKApiCountry import dev.ragnarok.fenrir.api.model.database.* import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IDatabaseService { +class IDatabaseService : IServiceRest() { //https://vk.com/dev/database.getCitiesById - @FormUrlEncoded - @POST("database.getCities") - fun getCitiesById(@Field("city_ids") cityIds: String?): Single>> + fun getCitiesById(cityIds: String?): Single>> { + return rest.request( + "database.getCities", + form("city_ids" to cityIds), + baseList(VKApiCity.serializer()) + ) + } /** * Returns a list of countries. @@ -26,14 +28,18 @@ interface IDatabaseService { * @param count Number of countries to return. Default 100, maximum value 1000 * @return Returns the total results number in count field and an array of objects describing countries in items field */ - @FormUrlEncoded - @POST("database.getCountries") fun getCountries( - @Field("need_all") needAll: Int?, - @Field("code") code: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + needAll: Int?, + code: String?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "database.getCountries", + form("need_all" to needAll, "code" to code, "offset" to offset, "count" to count), + items(VKApiCountry.serializer()) + ) + } /** * Returns a list of school classes specified for the country. @@ -41,9 +47,13 @@ interface IDatabaseService { * @param countryId Country ID. * @return Returns an array of objects, each of them is a pair of class ID and definition. */ - @FormUrlEncoded - @POST("database.getSchoolClasses") - fun getSchoolClasses(@Field("country_id") countryId: Int?): Single>> + fun getSchoolClasses(countryId: Int?): Single>> { + return rest.request( + "database.getSchoolClasses", + form("country_id" to countryId), + baseList(SchoolClazzDto.serializer()) + ) + } /** * Returns list of chairs on a specified faculty. @@ -53,13 +63,17 @@ interface IDatabaseService { * @param count amount of chairs to get. Default 100, maximum value 10000 * @return the total results number in count field and an array of objects describing chairs in items field */ - @FormUrlEncoded - @POST("database.getChairs") fun getChairs( - @Field("faculty_id") facultyId: Int, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + facultyId: Int, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "database.getChairs", + form("faculty_id" to facultyId, "offset" to offset, "count" to count), + items(ChairDto.serializer()) + ) + } /** * Returns a list of faculties (i.e., university departments). @@ -70,13 +84,17 @@ interface IDatabaseService { * @return the total results number in count field and an array * of objects describing faculties in items field */ - @FormUrlEncoded - @POST("database.getFaculties") fun getFaculties( - @Field("university_id") universityId: Int, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + universityId: Int, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "database.getFaculties", + form("university_id" to universityId, "offset" to offset, "count" to count), + items(FacultyDto.serializer()) + ) + } /** * Returns a list of higher education institutions. @@ -88,15 +106,23 @@ interface IDatabaseService { * @param count Number of universities to return. Default 100, maximum value 10000 * @return an array of objects describing universities */ - @FormUrlEncoded - @POST("database.getUniversities") fun getUniversities( - @Field("q") query: String?, - @Field("country_id") countryId: Int?, - @Field("city_id") cityId: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + query: String?, + countryId: Int?, + cityId: Int?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "database.getUniversities", form( + "q" to query, + "country_id" to countryId, + "city_id" to cityId, + "offset" to offset, + "count" to count + ), items(UniversityDto.serializer()) + ) + } /** * Returns a list of schools. @@ -107,14 +133,21 @@ interface IDatabaseService { * @param count Number of schools to return. Default 100, maximum value 10000 * @return an array of objects describing schools */ - @FormUrlEncoded - @POST("database.getSchools") fun getSchools( - @Field("q") query: String?, - @Field("city_id") cityId: Int, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + query: String?, + cityId: Int, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "database.getSchools", form( + "q" to query, + "city_id" to cityId, + "offset" to offset, + "count" to count + ), items(SchoolDto.serializer()) + ) + } /** * Returns a list of cities. @@ -128,14 +161,23 @@ interface IDatabaseService { * @param count Number of cities to return. Default 100, maximum value 1000 * @return the total results number in count field and an array of objects describing cities in items field */ - @FormUrlEncoded - @POST("database.getCities") fun getCities( - @Field("country_id") countryId: Int, - @Field("region_id") regionId: Int?, - @Field("q") query: String?, - @Field("need_all") needAll: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + countryId: Int, + regionId: Int?, + query: String?, + needAll: Int?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "database.getCities", form( + "country_id" to countryId, + "region_id" to regionId, + "q" to query, + "need_all" to needAll, + "offset" to offset, + "count" to count + ), items(VKApiCity.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IDocsService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IDocsService.kt index 2ca37ffa3..9b9921eed 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IDocsService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IDocsService.kt @@ -5,19 +5,22 @@ import dev.ragnarok.fenrir.api.model.VKApiDoc import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.server.VKApiDocsUploadServer import dev.ragnarok.fenrir.api.model.server.VKApiVideosUploadServer +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IDocsService { +class IDocsService : IServiceRest() { //https://vk.com/dev/docs.delete - @FormUrlEncoded - @POST("docs.delete") fun delete( - @Field("owner_id") ownerId: Int?, - @Field("doc_id") docId: Int - ): Single> + ownerId: Int?, + docId: Int + ): Single> { + return rest.request( + "docs.delete", form( + "owner_id" to ownerId, + "doc_id" to docId + ), baseInt + ) + } /** * Copies a document to a user's or community's document list. @@ -27,13 +30,19 @@ interface IDocsService { * @param accessKey Access key. This parameter is required if access_key was returned with the document's data. * @return the ID of the created document. */ - @FormUrlEncoded - @POST("docs.add") fun add( - @Field("owner_id") ownerId: Int, - @Field("doc_id") docId: Int, - @Field("access_key") accessKey: String? - ): Single> + ownerId: Int, + docId: Int, + accessKey: String? + ): Single> { + return rest.request( + "docs.add", form( + "owner_id" to ownerId, + "doc_id" to docId, + "access_key" to accessKey + ), baseInt + ) + } /** * Returns information about documents by their IDs. @@ -42,9 +51,9 @@ interface IDocsService { * List of comma-separated words, required parameter * @return an array of objects describing documents */ - @FormUrlEncoded - @POST("docs.getById") - fun getById(@Field("docs") ids: String?): Single>> + fun getById(ids: String?): Single>> { + return rest.request("docs.getById", form("docs" to ids), baseList(VKApiDoc.serializer())) + } /** * Returns a list of documents matching the search criteria. @@ -54,13 +63,19 @@ interface IDocsService { * @param offset Offset needed to return a specific subset of results. * @return Returns the total results number in count field and an array of objects describing documents in items field */ - @FormUrlEncoded - @POST("docs.search") fun search( - @Field("q") query: String?, - @Field("count") count: Int?, - @Field("offset") offset: Int? - ): Single>> + query: String?, + count: Int?, + offset: Int? + ): Single>> { + return rest.request( + "docs.search", form( + "q" to query, + "count" to count, + "offset" to offset + ), items(VKApiDoc.serializer()) + ) + } /** * Saves a document after uploading it to a server. @@ -70,13 +85,19 @@ interface IDocsService { * @param tags Document tags. * @return Returns an array of uploaded document objects. */ - @FormUrlEncoded - @POST("docs.save") fun save( - @Field("file") file: String?, - @Field("title") title: String?, - @Field("tags") tags: String? - ): Single> + file: String?, + title: String?, + tags: String? + ): Single> { + return rest.request( + "docs.save", form( + "file" to file, + "title" to title, + "tags" to tags + ), base(VKApiDoc.Entry.serializer()) + ) + } /** * Returns the server address for document upload. @@ -85,24 +106,39 @@ interface IDocsService { * @param type type of document, null or "audio_message" (undocumented option) * @return an object with an upload_url field. After the document is uploaded, use the [.save] method. */ - @FormUrlEncoded - @POST("docs.getMessagesUploadServer") fun getMessagesUploadServer( - @Field("peer_id") peer_id: Int?, - @Field("type") type: String? - ): Single> + peer_id: Int?, + type: String? + ): Single> { + return rest.request( + "docs.getMessagesUploadServer", form( + "peer_id" to peer_id, + "type" to type + ), base(VKApiDocsUploadServer.serializer()) + ) + } - @FormUrlEncoded - @POST("docs.getUploadServer") - fun getUploadServer(@Field("group_id") groupId: Int?): Single> + fun getUploadServer(groupId: Int?): Single> { + return rest.request( + "docs.getUploadServer", + form("group_id" to groupId), + base(VKApiDocsUploadServer.serializer()) + ) + } - @FormUrlEncoded - @POST("video.save") fun getVideoServer( - @Field("is_private") is_private: Int?, - @Field("group_id") group_id: Int?, - @Field("name") name: String? - ): Single> + is_private: Int?, + group_id: Int?, + name: String? + ): Single> { + return rest.request( + "video.save", form( + "is_private" to is_private, + "group_id" to group_id, + "name" to name + ), base(VKApiVideosUploadServer.serializer()) + ) + } /** * Returns detailed information about user or community documents. @@ -123,12 +159,19 @@ interface IDocsService { * 8 — unknown. * @return Returns the total results number in count field and an array of objects describing documents in items field */ - @FormUrlEncoded - @POST("docs.get") operator fun get( - @Field("owner_id") ownerId: Int?, - @Field("count") count: Int?, - @Field("offset") offset: Int?, - @Field("type") type: Int? - ): Single>> + ownerId: Int?, + count: Int?, + offset: Int?, + type: Int? + ): Single>> { + return rest.request( + "docs.get", form( + "owner_id" to ownerId, + "count" to count, + "offset" to offset, + "type" to type + ), items(VKApiDoc.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IFaveService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IFaveService.kt index 844ec6cec..e638b794d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IFaveService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IFaveService.kt @@ -4,172 +4,284 @@ import dev.ragnarok.fenrir.api.model.* import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.FavePageResponse import dev.ragnarok.fenrir.api.model.response.FavePostsResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IFaveService { - @FormUrlEncoded - @POST("fave.getPages") +class IFaveService : IServiceRest() { fun getPages( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("type") type: String?, - @Field("fields") fields: String? - ): Single>> - - @FormUrlEncoded - @POST("fave.get") + offset: Int?, + count: Int?, + type: String?, + fields: String? + ): Single>> { + return rest.request( + "fave.getPages", form( + "offset" to offset, + "count" to count, + "type" to type, + "fields" to fields + ), items(FavePageResponse.serializer()) + ) + } + fun getVideos( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("item_type") item_type: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single>> - - @FormUrlEncoded - @POST("fave.get") + offset: Int?, + count: Int?, + item_type: String?, + extended: Int?, + fields: String? + ): Single>> { + return rest.request( + "fave.get", form( + "offset" to offset, + "count" to count, + "item_type" to item_type, + "extended" to extended, + "fields" to fields + ), items(VKApiAttachments.Entry.serializer()) + ) + } + fun getArticles( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("item_type") item_type: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single>> - - @FormUrlEncoded - @POST("articles.getOwnerPublished") + offset: Int?, + count: Int?, + item_type: String?, + extended: Int?, + fields: String? + ): Single>> { + return rest.request( + "fave.get", form( + "offset" to offset, + "count" to count, + "item_type" to item_type, + "extended" to extended, + "fields" to fields + ), items(VKApiAttachments.Entry.serializer()) + ) + } + fun getOwnerPublishedArticles( - @Field("owner_id") owner_id: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("sort_by") sort_by: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single>> - - @FormUrlEncoded - @POST("fave.get") + owner_id: Int?, + offset: Int?, + count: Int?, + sort_by: String?, + extended: Int?, + fields: String? + ): Single>> { + return rest.request( + "articles.getOwnerPublished", form( + "owner_id" to owner_id, + "offset" to offset, + "count" to count, + "sort_by" to sort_by, + "extended" to extended, + "fields" to fields + ), items(VKApiArticle.serializer()) + ) + } + fun getPosts( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("item_type") item_type: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> - - @FormUrlEncoded - @POST("fave.get") + offset: Int?, + count: Int?, + item_type: String?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "fave.get", form( + "offset" to offset, + "count" to count, + "item_type" to item_type, + "extended" to extended, + "fields" to fields + ), base(FavePostsResponse.serializer()) + ) + } + fun getLinks( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("item_type") item_type: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single>> - - @FormUrlEncoded - @POST("fave.get") + offset: Int?, + count: Int?, + item_type: String?, + extended: Int?, + fields: String? + ): Single>> { + return rest.request( + "fave.get", form( + "offset" to offset, + "count" to count, + "item_type" to item_type, + "extended" to extended, + "fields" to fields + ), items(FaveLinkDto.serializer()) + ) + } + fun getProducts( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("item_type") item_type: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single>> - - @FormUrlEncoded - @POST("fave.getPhotos") + offset: Int?, + count: Int?, + item_type: String?, + extended: Int?, + fields: String? + ): Single>> { + return rest.request( + "fave.get", form( + "offset" to offset, + "count" to count, + "item_type" to item_type, + "extended" to extended, + "fields" to fields + ), items(VKApiAttachments.Entry.serializer()) + ) + } + fun getPhotos( - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "fave.getPhotos", form( + "offset" to offset, + "count" to count + ), items(VKApiPhoto.serializer()) + ) + } - @FormUrlEncoded - @POST("fave.addLink") - fun addLink(@Field("link") link: String?): Single> + fun addLink(link: String?): Single> { + return rest.request("fave.addLink", form("link" to link), baseInt) + } - @FormUrlEncoded - @POST("fave.addPage") fun addPage( - @Field("user_id") userId: Int?, - @Field("group_id") groupId: Int? - ): Single> + userId: Int?, + groupId: Int? + ): Single> { + return rest.request( + "fave.addPage", form( + "user_id" to userId, + "group_id" to groupId + ), baseInt + ) + } - @FormUrlEncoded - @POST("fave.addVideo") fun addVideo( - @Field("owner_id") owner_id: Int?, - @Field("id") id: Int?, - @Field("access_key") access_key: String? - ): Single> + owner_id: Int?, + id: Int?, + access_key: String? + ): Single> { + return rest.request( + "fave.addVideo", form( + "owner_id" to owner_id, + "id" to id, + "access_key" to access_key + ), baseInt + ) + } - @FormUrlEncoded - @POST("fave.addArticle") - fun addArticle(@Field("url") url: String?): Single> + fun addArticle(url: String?): Single> { + return rest.request("fave.addArticle", form("url" to url), baseInt) + } - @FormUrlEncoded - @POST("fave.addProduct") fun addProduct( - @Field("id") id: Int, - @Field("owner_id") owner_id: Int, - @Field("access_key") access_key: String? - ): Single> + id: Int, + owner_id: Int, + access_key: String? + ): Single> { + return rest.request( + "fave.addProduct", form( + "id" to id, + "owner_id" to owner_id, + "access_key" to access_key + ), baseInt + ) + } - @FormUrlEncoded - @POST("fave.addPost") fun addPost( - @Field("owner_id") owner_id: Int?, - @Field("id") id: Int?, - @Field("access_key") access_key: String? - ): Single> + owner_id: Int?, + id: Int?, + access_key: String? + ): Single> { + return rest.request( + "fave.addPost", form( + "owner_id" to owner_id, + "id" to id, + "access_key" to access_key + ), baseInt + ) + } //https://vk.com/dev/fave.removePage - @FormUrlEncoded - @POST("fave.removePage") fun removePage( - @Field("user_id") userId: Int?, - @Field("group_id") groupId: Int? - ): Single> + userId: Int?, + groupId: Int? + ): Single> { + return rest.request( + "fave.removePage", form( + "user_id" to userId, + "group_id" to groupId + ), baseInt + ) + } - @FormUrlEncoded - @POST("fave.removeLink") - fun removeLink(@Field("link_id") linkId: String?): Single> + fun removeLink(linkId: String?): Single> { + return rest.request("fave.removeLink", form("link_id" to linkId), baseInt) + } - @FormUrlEncoded - @POST("fave.removeArticle") fun removeArticle( - @Field("owner_id") owner_id: Int?, - @Field("article_id") article_id: Int? - ): Single> + owner_id: Int?, + article_id: Int? + ): Single> { + return rest.request( + "fave.removeArticle", form( + "owner_id" to owner_id, + "article_id" to article_id + ), baseInt + ) + } - @FormUrlEncoded - @POST("fave.removeProduct") fun removeProduct( - @Field("id") id: Int?, - @Field("owner_id") owner_id: Int? - ): Single> + id: Int?, + owner_id: Int? + ): Single> { + return rest.request( + "fave.removeProduct", form( + "id" to id, + "owner_id" to owner_id + ), baseInt + ) + } - @FormUrlEncoded - @POST("fave.removePost") fun removePost( - @Field("owner_id") owner_id: Int?, - @Field("id") id: Int? - ): Single> + owner_id: Int?, + id: Int? + ): Single> { + return rest.request( + "fave.removePost", form( + "owner_id" to owner_id, + "id" to id + ), baseInt + ) + } - @FormUrlEncoded - @POST("fave.removeVideo") fun removeVideo( - @Field("owner_id") owner_id: Int?, - @Field("id") id: Int? - ): Single> + owner_id: Int?, + id: Int? + ): Single> { + return rest.request( + "fave.removeVideo", form( + "owner_id" to owner_id, + "id" to id + ), baseInt + ) + } - @FormUrlEncoded - @POST("execute") fun pushFirst( - @Field("code") code: String?, - @Field("owner_id") ownerId: Int - ): Single> + code: String?, + ownerId: Int + ): Single> { + return rest.request( + "execute", form( + "code" to code, + "owner_id" to ownerId + ), baseInt + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IFriendsService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IFriendsService.kt index b5a38c70d..542bdd03e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IFriendsService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IFriendsService.kt @@ -7,82 +7,133 @@ import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.DeleteFriendResponse import dev.ragnarok.fenrir.api.model.response.MutualFriendsResponse import dev.ragnarok.fenrir.api.model.response.OnlineFriendsResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IFriendsService { - @FormUrlEncoded - @POST("execute") - fun getOnline(@Field("code") code: String?): Single> +class IFriendsService : IServiceRest() { + fun getOnline(code: String?): Single> { + return rest.request( + "execute", + form("code" to code), + base(OnlineFriendsResponse.serializer()) + ) + } /*@FormUrlEncoded @POST("execute") Single> getWithMyCounters(@Field("code") String code);*/ - @FormUrlEncoded - @POST("friends.get") operator fun get( - @Field("user_id") userId: Int?, - @Field("order") order: String?, - @Field("list_id") listId: Int?, - @Field("count") count: Int?, - @Field("offset") offset: Int?, - @Field("fields") fields: String?, - @Field("name_case") nameCase: String? - ): Single>> + userId: Int?, + order: String?, + listId: Int?, + count: Int?, + offset: Int?, + fields: String?, + nameCase: String? + ): Single>> { + return rest.request( + "friends.get", form( + "user_id" to userId, + "order" to order, + "list_id" to listId, + "count" to count, + "offset" to offset, + "fields" to fields, + "name_case" to nameCase + ), items(VKApiUser.serializer()) + ) + } - @FormUrlEncoded - @POST("friends.getRecommendations") fun getRecommendations( - @Field("count") count: Int?, - @Field("fields") fields: String?, - @Field("name_case") nameCase: String? - ): Single>> + count: Int?, + fields: String?, + nameCase: String? + ): Single>> { + return rest.request( + "friends.getRecommendations", form( + "count" to count, + "fields" to fields, + "name_case" to nameCase + ), items(VKApiUser.serializer()) + ) + } //https://vk.com/dev/friends.getLists - @FormUrlEncoded - @POST("friends.getLists") fun getLists( - @Field("user_id") userId: Int?, - @Field("return_system") returnSystem: Int? - ): Single>> + userId: Int?, + returnSystem: Int? + ): Single>> { + return rest.request( + "friends.getLists", form( + "user_id" to userId, + "return_system" to returnSystem + ), items(VKApiFriendList.serializer()) + ) + } //https://vk.com/dev/friends.delete - @FormUrlEncoded - @POST("friends.delete") - fun delete(@Field("user_id") userId: Int): Single> + fun delete(userId: Int): Single> { + return rest.request( + "friends.delete", + form("user_id" to userId), + base(DeleteFriendResponse.serializer()) + ) + } //https://vk.com/dev/friends.add - @FormUrlEncoded - @POST("friends.add") fun add( - @Field("user_id") userId: Int, - @Field("text") text: String?, - @Field("follow") follow: Int? - ): Single> + userId: Int, + text: String?, + follow: Int? + ): Single> { + return rest.request( + "friends.add", form( + "user_id" to userId, + "text" to text, + "follow" to follow + ), baseInt + ) + } //https://vk.com/dev/friends.search - @FormUrlEncoded - @POST("friends.search") fun search( - @Field("user_id") userId: Int, - @Field("q") query: String?, - @Field("fields") fields: String?, - @Field("name_case") nameCase: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + userId: Int, + query: String?, + fields: String?, + nameCase: String?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "friends.search", form( + "user_id" to userId, + "q" to query, + "fields" to fields, + "name_case" to nameCase, + "offset" to offset, + "count" to count + ), items(VKApiUser.serializer()) + ) + } - @FormUrlEncoded - @POST("execute") - fun getMutual(@Field("code") code: String?): Single> + fun getMutual(code: String?): Single> { + return rest.request( + "execute", + form("code" to code), + base(MutualFriendsResponse.serializer()) + ) + } //https://vk.com/dev/friends.getByPhones - @FormUrlEncoded - @POST("friends.getByPhones") fun getByPhones( - @Field("phones") phones: String?, - @Field("fields") fields: String? - ): Single>> + phones: String?, + fields: String? + ): Single>> { + return rest.request( + "friends.getByPhones", form( + "phones" to phones, + "fields" to fields + ), baseList(VKApiUser.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IGroupsService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IGroupsService.kt index 0d7cb74d8..58acd88b7 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IGroupsService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IGroupsService.kt @@ -4,172 +4,292 @@ import dev.ragnarok.fenrir.api.model.* import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.GroupLongpollServer import dev.ragnarok.fenrir.api.model.response.GroupWallInfoResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IGroupsService { - @FormUrlEncoded - @POST("groups.editManager") +class IGroupsService : IServiceRest() { fun editManager( - @Field("group_id") groupId: Int, - @Field("user_id") userId: Int, - @Field("role") role: String?, - @Field("is_contact") isContact: Int?, - @Field("contact_position") contactPosition: String?, - @Field("contact_email") contactEmail: String?, - @Field("contact_phone") contactPhone: String? - ): Single> - - @FormUrlEncoded - @POST("groups.edit") + groupId: Int, + userId: Int, + role: String?, + isContact: Int?, + contactPosition: String?, + contactEmail: String?, + contactPhone: String? + ): Single> { + return rest.request( + "groups.editManager", form( + "group_id" to groupId, + "user_id" to userId, + "role" to role, + "is_contact" to isContact, + "contact_position" to contactPosition, + "contact_email" to contactEmail, + "contact_phone" to contactPhone + ), baseInt + ) + } + fun edit( - @Field("group_id") groupId: Int, - @Field("title") title: String?, - @Field("description") description: String?, - @Field("screen_name") screen_name: String?, - @Field("access") access: Int?, - @Field("website") website: String?, - //@Field("public_category") public_category: Int?, - //@Field("public_subcategory") public_subcategory: Int?, - @Field("public_date") public_date: String?, - @Field("age_limits") age_limits: Int?, - @Field("obscene_filter") obscene_filter: Int?, - @Field("obscene_stopwords") obscene_stopwords: Int?, - @Field("obscene_words") obscene_words: String? - ): Single> - - @FormUrlEncoded - @POST("groups.unban") + groupId: Int, + title: String?, + description: String?, + screen_name: String?, + access: Int?, + website: String?, + //public_category: Int?, + //public_subcategory: Int?, + public_date: String?, + age_limits: Int?, + obscene_filter: Int?, + obscene_stopwords: Int?, + obscene_words: String? + ): Single> { + return rest.request( + "groups.edit", form( + "group_id" to groupId, + "title" to title, + "description" to description, + "screen_name" to screen_name, + "access" to access, + "website" to website, + "public_date" to public_date, + "age_limits" to age_limits, + "obscene_filter" to obscene_filter, + "obscene_stopwords" to obscene_stopwords, + "obscene_words" to obscene_words + ), baseInt + ) + } + fun unban( - @Field("group_id") groupId: Int, - @Field("owner_id") ownerId: Int - ): Single> + groupId: Int, + ownerId: Int + ): Single> { + return rest.request( + "groups.unban", form( + "group_id" to groupId, + "owner_id" to ownerId + ), baseInt + ) + } - @FormUrlEncoded - @POST("market.getAlbums") fun getMarketAlbums( - @Field("owner_id") owner_id: Int, - @Field("offset") offset: Int, - @Field("count") count: Int - ): Single>> + owner_id: Int, + offset: Int, + count: Int + ): Single>> { + return rest.request( + "market.getAlbums", form( + "owner_id" to owner_id, + "offset" to offset, + "count" to count + ), items(VKApiMarketAlbum.serializer()) + ) + } - @FormUrlEncoded - @POST("market.get") fun getMarket( - @Field("owner_id") owner_id: Int, - @Field("album_id") album_id: Int?, - @Field("offset") offset: Int, - @Field("count") count: Int, - @Field("extended") extended: Int? - ): Single>> - - @FormUrlEncoded - @POST("market.getServices") + owner_id: Int, + album_id: Int?, + offset: Int, + count: Int, + extended: Int? + ): Single>> { + return rest.request( + "market.get", form( + "owner_id" to owner_id, + "album_id" to album_id, + "offset" to offset, + "count" to count, + "extended" to extended + ), items(VKApiMarket.serializer()) + ) + } + fun getMarketServices( - @Field("owner_id") owner_id: Int, - @Field("offset") offset: Int, - @Field("count") count: Int, - @Field("extended") extended: Int? - ): Single>> - - @FormUrlEncoded - @POST("market.getById") + owner_id: Int, + offset: Int, + count: Int, + extended: Int? + ): Single>> { + return rest.request( + "market.getServices", form( + "owner_id" to owner_id, + "offset" to offset, + "count" to count, + "extended" to extended + ), items(VKApiMarket.serializer()) + ) + } + fun getMarketById( - @Field("item_ids") item_ids: String?, - @Field("extended") extended: Int? - ): Single>> + item_ids: String?, + extended: Int? + ): Single>> { + return rest.request( + "market.getById", form( + "item_ids" to item_ids, + "extended" to extended + ), items(VKApiMarket.serializer()) + ) + } - @POST("groups.ban") - @FormUrlEncoded fun ban( - @Field("group_id") groupId: Int, - @Field("owner_id") ownerId: Int, - @Field("end_date") endDate: Long?, - @Field("reason") reason: Int?, - @Field("comment") comment: String?, - @Field("comment_visible") commentVisible: Int? - ): Single> - - @FormUrlEncoded - @POST("groups.getSettings") - fun getSettings(@Field("group_id") groupId: Int): Single> + groupId: Int, + ownerId: Int, + endDate: Long?, + reason: Int?, + comment: String?, + commentVisible: Int? + ): Single> { + return rest.request( + "groups.ban", form( + "group_id" to groupId, + "owner_id" to ownerId, + "end_date" to endDate, + "reason" to reason, + "comment" to comment, + "comment_visible" to commentVisible + ), baseInt + ) + } + + fun getSettings(groupId: Int): Single> { + return rest.request( + "groups.getSettings", + form("group_id" to groupId), + base(GroupSettingsDto.serializer()) + ) + } //https://vk.com/dev/groups.getBanned - @FormUrlEncoded - @POST("groups.getBanned") fun getBanned( - @Field("group_id") groupId: Int, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("fields") fields: String?, - @Field("user_id") userId: Int? - ): Single>> - - @FormUrlEncoded - @POST("execute") + groupId: Int, + offset: Int?, + count: Int?, + fields: String?, + userId: Int? + ): Single>> { + return rest.request( + "groups.getBanned", form( + "group_id" to groupId, + "offset" to offset, + "count" to count, + "fields" to fields, + "user_id" to userId + ), items(VKApiBanned.serializer()) + ) + } + fun getGroupWallInfo( - @Field("code") code: String?, - @Field("group_id") groupId: String?, - @Field("fields") fields: String? - ): Single> + code: String?, + groupId: String?, + fields: String? + ): Single> { + return rest.request( + "execute", form( + "code" to code, + "group_id" to groupId, + "fields" to fields + ), base(GroupWallInfoResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("groups.getMembers") fun getMembers( - @Field("group_id") groupId: String?, - @Field("sort") sort: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("fields") fields: String?, - @Field("filter") filter: String? - ): Single>> + groupId: String?, + sort: Int?, + offset: Int?, + count: Int?, + fields: String?, + filter: String? + ): Single>> { + return rest.request( + "groups.getMembers", form( + "group_id" to groupId, + "sort" to sort, + "offset" to offset, + "count" to count, + "fields" to fields, + "filter" to filter + ), items(VKApiUser.serializer()) + ) + } //https://vk.com/dev/groups.search - @FormUrlEncoded - @POST("groups.search") fun search( - @Field("q") query: String?, - @Field("type") type: String?, - @Field("fields") fields: String?, - @Field("country_id") countryId: Int?, - @Field("city_id") cityId: Int?, - @Field("future") future: Int?, - @Field("market") market: Int?, - @Field("sort") sort: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> - - @FormUrlEncoded - @POST("groups.getLongPollServer") - fun getLongPollServer(@Field("group_id") groupId: Int): Single> + query: String?, + type: String?, + fields: String?, + countryId: Int?, + cityId: Int?, + future: Int?, + market: Int?, + sort: Int?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "groups.search", form( + "q" to query, + "type" to type, + "fields" to fields, + "country_id" to countryId, + "city_id" to cityId, + "future" to future, + "market" to market, + "sort" to sort, + "offset" to offset, + "count" to count + ), items(VKApiCommunity.serializer()) + ) + } + + fun getLongPollServer(groupId: Int): Single> { + return rest.request( + "groups.getLongPollServer", + form("group_id" to groupId), + base(GroupLongpollServer.serializer()) + ) + } //https://vk.com/dev/groups.leave - @FormUrlEncoded - @POST("groups.leave") - fun leave(@Field("group_id") groupId: Int): Single> + fun leave(groupId: Int): Single> { + return rest.request("groups.leave", form("group_id" to groupId), baseInt) + } //https://vk.com/dev/groups.join - @FormUrlEncoded - @POST("groups.join") fun join( - @Field("group_id") groupId: Int, - @Field("not_sure") notSure: Int? - ): Single> + groupId: Int, + notSure: Int? + ): Single> { + return rest.request( + "groups.join", form( + "group_id" to groupId, + "not_sure" to notSure + ), baseInt + ) + } //https://vk.com/dev/groups.get - @FormUrlEncoded - @POST("groups.get") operator fun get( - @Field("user_id") userId: Int?, - @Field("extended") extended: Int?, - @Field("filter") filter: String?, - @Field("fields") fields: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + userId: Int?, + extended: Int?, + filter: String?, + fields: String?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "groups.get", form( + "user_id" to userId, + "extended" to extended, + "filter" to filter, + "fields" to fields, + "offset" to offset, + "count" to count + ), items(VKApiCommunity.serializer()) + ) + } /** * Returns information about communities by their IDs. @@ -180,19 +300,31 @@ interface IGroupsService { * @param fields Group fields to return. List of comma-separated words * @return an array of objects describing communities */ - @FormUrlEncoded - @POST("groups.getById") fun getById( - @Field("group_ids") groupIds: String?, - @Field("group_id") groupId: String?, - @Field("fields") fields: String? - ): Single>> + groupIds: String?, + groupId: String?, + fields: String? + ): Single>> { + return rest.request( + "groups.getById", form( + "group_ids" to groupIds, + "group_id" to groupId, + "fields" to fields + ), baseList(VKApiCommunity.serializer()) + ) + } - @FormUrlEncoded - @POST("groups.getChats") fun getChats( - @Field("group_id") groupId: Int, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + groupId: Int, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "groups.getChats", form( + "group_id" to groupId, + "offset" to offset, + "count" to count + ), items(VKApiGroupChats.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILikesService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILikesService.kt index 8c7e13c4f..58acb929d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILikesService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILikesService.kt @@ -4,65 +4,105 @@ import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.LikeResponse import dev.ragnarok.fenrir.api.model.response.LikesListResponse import dev.ragnarok.fenrir.api.model.response.isLikeResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface ILikesService { +class ILikesService : IServiceRest() { //https://vk.com/dev/likes.getList - @FormUrlEncoded - @POST("likes.getList") fun getList( - @Field("type") type: String?, - @Field("owner_id") ownerId: Int?, - @Field("item_id") itemId: Int?, - @Field("page_url") pageUrl: String?, - @Field("filter") filter: String?, - @Field("friends_only") friendsOnly: Int?, - @Field("extended") extended: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("skip_own") skipOwn: Int?, - @Field("fields") fields: String? - ): Single> + type: String?, + ownerId: Int?, + itemId: Int?, + pageUrl: String?, + filter: String?, + friendsOnly: Int?, + extended: Int?, + offset: Int?, + count: Int?, + skipOwn: Int?, + fields: String? + ): Single> { + return rest.request( + "likes.getList", form( + "type" to type, + "owner_id" to ownerId, + "item_id" to itemId, + "page_url" to pageUrl, + "filter" to filter, + "friends_only" to friendsOnly, + "extended" to extended, + "offset" to offset, + "count" to count, + "skip_own" to skipOwn, + "fields" to fields + ), base(LikesListResponse.serializer()) + ) + } //https://vk.com/dev/likes.delete - @FormUrlEncoded - @POST("likes.delete") fun delete( - @Field("type") type: String?, - @Field("owner_id") ownerId: Int?, - @Field("item_id") itemId: Int, - @Field("access_key") accessKey: String? - ): Single> + type: String?, + ownerId: Int?, + itemId: Int, + accessKey: String? + ): Single> { + return rest.request( + "likes.delete", form( + "type" to type, + "owner_id" to ownerId, + "item_id" to itemId, + "access_key" to accessKey + ), base(LikeResponse.serializer()) + ) + } //https://vk.com/dev/likes.add - @FormUrlEncoded - @POST("likes.add") fun add( - @Field("type") type: String?, - @Field("owner_id") ownerId: Int?, - @Field("item_id") itemId: Int, - @Field("access_key") accessKey: String? - ): Single> + type: String?, + ownerId: Int?, + itemId: Int, + accessKey: String? + ): Single> { + return rest.request( + "likes.add", form( + "type" to type, + "owner_id" to ownerId, + "item_id" to itemId, + "access_key" to accessKey + ), base(LikeResponse.serializer()) + ) + } //https://vk.com/dev/likes.isLiked - @FormUrlEncoded - @POST("likes.isLiked") fun isLiked( - @Field("type") type: String?, - @Field("owner_id") ownerId: Int?, - @Field("item_id") itemId: Int - ): Single> + type: String?, + ownerId: Int?, + itemId: Int + ): Single> { + return rest.request( + "likes.isLiked", form( + "type" to type, + "owner_id" to ownerId, + "item_id" to itemId + ), base(isLikeResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("execute") fun checkAndAddLike( - @Field("code") code: String?, - @Field("type") type: String?, - @Field("owner_id") ownerId: Int?, - @Field("item_id") itemId: Int, - @Field("access_key") accessKey: String? - ): Single> + code: String?, + type: String?, + ownerId: Int?, + itemId: Int, + accessKey: String? + ): Single> { + return rest.request( + "execute", form( + "code" to code, + "type" to type, + "owner_id" to ownerId, + "item_id" to itemId, + "access_key" to accessKey + ), baseInt + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILocalServerService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILocalServerService.kt index ba817c015..8209d0422 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILocalServerService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILocalServerService.kt @@ -5,117 +5,151 @@ import dev.ragnarok.fenrir.api.model.VKApiAudio import dev.ragnarok.fenrir.api.model.VKApiPhoto import dev.ragnarok.fenrir.api.model.VKApiVideo import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import dev.ragnarok.fenrir.model.FileRemote import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface ILocalServerService { - @FormUrlEncoded - @POST("audio.get") +class ILocalServerService : IServiceRest() { fun getAudios( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "audio.get", + form("offset" to offset, "count" to count, "reverse" to reverse), + items(VKApiAudio.serializer()) + ) + } - @FormUrlEncoded - @POST("discography.get") fun getDiscography( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "discography.get", + form("offset" to offset, "count" to count, "reverse" to reverse), + items(VKApiAudio.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.get") fun getPhotos( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "photos.get", + form("offset" to offset, "count" to count, "reverse" to reverse), + items(VKApiPhoto.serializer()) + ) + } - @FormUrlEncoded - @POST("video.get") fun getVideos( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "video.get", + form("offset" to offset, "count" to count, "reverse" to reverse), + items(VKApiVideo.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.search") fun searchAudios( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> - - @FormUrlEncoded - @POST("discography.search") + query: String?, + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "audio.search", + form("q" to query, "offset" to offset, "count" to count, "reverse" to reverse), + items(VKApiAudio.serializer()) + ) + } + fun searchDiscography( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> - - @FormUrlEncoded - @POST("video.search") + query: String?, + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "discography.search", + form("q" to query, "offset" to offset, "count" to count, "reverse" to reverse), + items(VKApiAudio.serializer()) + ) + } + fun searchVideos( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> - - @FormUrlEncoded - @POST("photos.search") + query: String?, + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "video.search", + form("q" to query, "offset" to offset, "count" to count, "reverse" to reverse), + items(VKApiVideo.serializer()) + ) + } + fun searchPhotos( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> - - @FormUrlEncoded - @POST("update_time") - fun update_time(@Field("hash") hash: String?): Single> - - @FormUrlEncoded - @POST("delete_media") - fun delete_media(@Field("hash") hash: String?): Single> - - @FormUrlEncoded - @POST("get_file_name") - fun get_file_name(@Field("hash") hash: String?): Single> - - @FormUrlEncoded - @POST("update_file_name") + query: String?, + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "photos.search", + form("q" to query, "offset" to offset, "count" to count, "reverse" to reverse), + items(VKApiPhoto.serializer()) + ) + } + + fun update_time(hash: String?): Single> { + return rest.request("update_time", form("hash" to hash), baseInt) + } + + fun delete_media(hash: String?): Single> { + return rest.request("delete_media", form("hash" to hash), baseInt) + } + + fun get_file_name(hash: String?): Single> { + return rest.request("get_file_name", form("hash" to hash), baseString) + } + fun update_file_name( - @Field("hash") hash: String?, - @Field("name") name: String? - ): Single> + hash: String?, + name: String? + ): Single> { + return rest.request("update_file_name", form("hash" to hash, "name" to name), baseInt) + } - @FormUrlEncoded - @POST("fs.get") fun fsGet( - @Field("dir") dir: String? - ): Single>> + dir: String? + ): Single>> { + return rest.request("fs.get", form("dir" to dir), items(FileRemote.serializer())) + } - @FormUrlEncoded - @POST("rebootPC") fun rebootPC( - @Field("type") type: String? - ): Single> + type: String? + ): Single> { + return rest.request("rebootPC", form("type" to type), baseInt) + } - @FormUrlEncoded - @POST("audio.upload") fun uploadAudio( - @Field("hash") hash: String?, - @Field("access_token") access_token: String?, - @Field("user_agent") user_agent: String? - ): Single> + hash: String?, + access_token: String?, + user_agent: String? + ): Single> { + return rest.request( + "audio.upload", + form("hash" to hash, "access_token" to access_token, "user_agent" to user_agent), + baseInt + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILongpollUpdatesService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILongpollUpdatesService.kt index deffdf537..3527af3af 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILongpollUpdatesService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/ILongpollUpdatesService.kt @@ -2,29 +2,44 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.longpoll.VkApiGroupLongpollUpdates import dev.ragnarok.fenrir.api.model.longpoll.VkApiLongpollUpdates +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.GET -import retrofit2.http.Query -import retrofit2.http.Url -interface ILongpollUpdatesService { - @GET +class ILongpollUpdatesService : IServiceRest() { fun getUpdates( - @Url server: String?, - @Query("act") act: String?, - @Query("key") key: String?, - @Query("ts") ts: Long, - @Query("wait") wait: Int, - @Query("mode") mode: Int, - @Query("version") version: Int - ): Single + server: String, + act: String?, + key: String?, + ts: Long, + wait: Int, + mode: Int, + version: Int + ): Single { + return rest.requestFullUrl( + server, + form( + "act" to act, + "key" to key, + "ts" to ts, + "wait" to wait, + "mode" to mode, + "version" to version + ), + VkApiLongpollUpdates.serializer() + ) + } - @GET fun getGroupUpdates( - @Url server: String?, - @Query("act") act: String?, - @Query("key") key: String?, - @Query("ts") ts: String?, - @Query("wait") wait: Int - ): Single + server: String, + act: String?, + key: String?, + ts: String?, + wait: Int + ): Single { + return rest.requestFullUrl( + server, + form("act" to act, "key" to key, "ts" to ts, "wait" to wait), + VkApiGroupLongpollUpdates.serializer() + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IMessageService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IMessageService.kt index af6b5af8a..516d79e0c 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IMessageService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IMessageService.kt @@ -2,45 +2,59 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.* import dev.ragnarok.fenrir.api.model.response.* +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer -interface IMessageService { - @FormUrlEncoded - @POST("messages.edit") +class IMessageService : IServiceRest() { fun editMessage( - @Field("peer_id") peedId: Int, - @Field("message_id") messageId: Int, - @Field("message") message: String?, - @Field("attachment") attachment: String?, - @Field("keep_forward_messages") keepForwardMessages: Int?, - @Field("keep_snippets") keepSnippets: Int? - ): Single> - - @FormUrlEncoded - @POST("messages.pin") - fun pin( - @Field("peer_id") peerId: Int, - @Field("message_id") messageId: Int - ): Single> - - @FormUrlEncoded - @POST("messages.pinConversation") - fun pinConversation(@Field("peer_id") peerId: Int): Single> - - @FormUrlEncoded - @POST("messages.unpinConversation") - fun unpinConversation(@Field("peer_id") peerId: Int): Single> + peedId: Int, + messageId: Int, + message: String?, + attachment: String?, + keepForwardMessages: Int?, + keepSnippets: Int? + ): Single> { + return rest.request( + "messages.edit", form( + "peer_id" to peedId, + "message_id" to messageId, + "message" to message, + "attachment" to attachment, + "keep_forward_messages" to keepForwardMessages, + "keep_snippets" to keepSnippets + ), baseInt + ) + } - @FormUrlEncoded - @POST("messages.markAsListened") - fun markAsListened(@Field("message_id") message_id: Int): Single> - - @FormUrlEncoded - @POST("messages.unpin") - fun unpin(@Field("peer_id") peerId: Int): Single> + fun pin( + peerId: Int, + messageId: Int + ): Single> { + return rest.request( + "messages.pin", form( + "peer_id" to peerId, + "message_id" to messageId + ), base(VKApiMessage.serializer()) + ) + } + + fun pinConversation(peerId: Int): Single> { + return rest.request("messages.pinConversation", form("peer_id" to peerId), baseInt) + } + + fun unpinConversation(peerId: Int): Single> { + return rest.request("messages.unpinConversation", form("peer_id" to peerId), baseInt) + } + + fun markAsListened(message_id: Int): Single> { + return rest.request("messages.markAsListened", form("message_id" to message_id), baseInt) + } + + fun unpin(peerId: Int): Single> { + return rest.request("messages.unpin", form("peer_id" to peerId), baseInt) + } /** * Allows the current user to leave a chat or, if the current user started the chat, @@ -50,16 +64,25 @@ interface IMessageService { * @param memberId ID of the member to be removed from the chat * @return 1 */ - @FormUrlEncoded - @POST("messages.removeChatUser") fun removeChatUser( - @Field("chat_id") chatId: Int, - @Field("member_id") memberId: Int - ): Single> - - @FormUrlEncoded - @POST("messages.deleteChatPhoto") - fun deleteChatPhoto(@Field("chat_id") chatId: Int): Single> + chatId: Int, + memberId: Int + ): Single> { + return rest.request( + "messages.removeChatUser", form( + "chat_id" to chatId, + "member_id" to memberId + ), baseInt + ) + } + + fun deleteChatPhoto(chatId: Int): Single> { + return rest.request( + "messages.deleteChatPhoto", + form("chat_id" to chatId), + base(UploadChatPhotoResponse.serializer()) + ) + } /** * Adds a new user to a chat. @@ -68,12 +91,17 @@ interface IMessageService { * @param userId ID of the user to be added to the chat. * @return 1 */ - @FormUrlEncoded - @POST("messages.addChatUser") fun addChatUser( - @Field("chat_id") chatId: Int, - @Field("user_id") userId: Int - ): Single> + chatId: Int, + userId: Int + ): Single> { + return rest.request( + "messages.addChatUser", form( + "chat_id" to chatId, + "user_id" to userId + ), baseInt + ) + } /** * Returns information about a chat. @@ -90,21 +118,35 @@ interface IMessageService { * abl — prepositional * @return Returns a list of chat objects. */ - @FormUrlEncoded - @POST("messages.getChat") fun getChat( - @Field("chat_id") chatId: Int?, - @Field("chat_ids") chatIds: String?, - @Field("fields") fields: String?, - @Field("name_case") nameCase: String? - ): Single> - - @FormUrlEncoded - @POST("messages.getConversationMembers") + chatId: Int?, + chatIds: String?, + fields: String?, + nameCase: String? + ): Single> { + return rest.request( + "messages.getChat", form( + "chat_id" to chatId, + "chat_ids" to chatIds, + "fields" to fields, + "name_case" to nameCase + ), base(ChatsInfoResponse.serializer()) + ) + } + fun getConversationMembers( - @Field("peer_id") peer_id: Int?, - @Field("fields") fields: String? - ): Single> + peer_id: Int?, + fields: String? + ): Single> { + return rest.request( + "messages.getConversationMembers", + form( + "peer_id" to peer_id, + "fields" to fields + ), + base(ConversationMembersResponse.serializer()) + ) + } /** * Edits the title of a chat. @@ -113,12 +155,17 @@ interface IMessageService { * @param title New title of the chat. * @return 1 */ - @FormUrlEncoded - @POST("messages.editChat") fun editChat( - @Field("chat_id") chatId: Int, - @Field("title") title: String? - ): Single> + chatId: Int, + title: String? + ): Single> { + return rest.request( + "messages.editChat", form( + "chat_id" to chatId, + "title" to title + ), baseInt + ) + } /** * Creates a chat with several participants. @@ -127,12 +174,17 @@ interface IMessageService { * @param title Chat title * @return the ID of the created chat (chat_id). */ - @FormUrlEncoded - @POST("messages.createChat") fun createChat( - @Field("user_ids") userIds: String?, - @Field("title") title: String? - ): Single> + userIds: String?, + title: String? + ): Single> { + return rest.request( + "messages.createChat", form( + "user_ids" to userIds, + "title" to title + ), baseInt + ) + } /** * Deletes all private messages in a conversation. @@ -140,9 +192,13 @@ interface IMessageService { * @param peerId Destination ID. * @return 1 */ - @FormUrlEncoded - @POST("messages.deleteConversation") - fun deleteDialog(@Field("peer_id") peerId: Int): Single> + fun deleteDialog(peerId: Int): Single> { + return rest.request( + "messages.deleteConversation", + form("peer_id" to peerId), + base(ConversationDeleteResult.serializer()) + ) + } /** * Restores a deleted message. @@ -150,9 +206,9 @@ interface IMessageService { * @param messageId ID of a previously-deleted message to restore * @return 1 */ - @FormUrlEncoded - @POST("messages.restore") - fun restore(@Field("message_id") messageId: Int): Single> + fun restore(messageId: Int): Single> { + return rest.request("messages.restore", form("message_id" to messageId), baseInt) + } /** * Deletes one or more messages. @@ -161,13 +217,21 @@ interface IMessageService { * @param spam 1 — to mark message as spam. * @return 1 */ - @FormUrlEncoded - @POST("messages.delete") fun delete( - @Field("message_ids") messageIds: String?, - @Field("delete_for_all") deleteForAll: Int?, - @Field("spam") spam: Int? - ): Single>> + messageIds: String?, + deleteForAll: Int?, + spam: Int? + ): Single>> { + return rest.request( + "messages.delete", + form( + "message_ids" to messageIds, + "delete_for_all" to deleteForAll, + "spam" to spam + ), + base(MapSerializer(String.serializer(), Int.serializer())) + ) + } /** * Marks messages as read. @@ -176,19 +240,29 @@ interface IMessageService { * @param startMessageId Message ID to start from * @return 1 */ - @FormUrlEncoded - @POST("messages.markAsRead") fun markAsRead( - @Field("peer_id") peerId: Int?, - @Field("start_message_id") startMessageId: Int? - ): Single> + peerId: Int?, + startMessageId: Int? + ): Single> { + return rest.request( + "messages.markAsRead", form( + "peer_id" to peerId, + "start_message_id" to startMessageId + ), baseInt + ) + } - @FormUrlEncoded - @POST("messages.markAsImportant") fun markAsImportant( - @Field("message_ids") messageIds: String?, - @Field("important") important: Int? - ): Single>> + messageIds: String?, + important: Int? + ): Single>> { + return rest.request( + "messages.markAsImportant", form( + "message_ids" to messageIds, + "important" to important + ), baseList(Int.serializer()) + ) + } /** * Changes the status of a user as typing in a conversation. @@ -197,12 +271,17 @@ interface IMessageService { * @param type typing — user has started to type. * @return 1 */ - @FormUrlEncoded - @POST("messages.setActivity") fun setActivity( - @Field("peer_id") peerId: Int, - @Field("type") type: String? - ): Single> + peerId: Int, + type: String? + ): Single> { + return rest.request( + "messages.setActivity", form( + "peer_id" to peerId, + "type" to type + ), baseInt + ) + } /** * Returns a list of the current user's private messages that match search criteria. @@ -217,16 +296,25 @@ interface IMessageService { * @param count Number of messages to return * @return list of the current user's private messages */ - @FormUrlEncoded - @POST("messages.search") fun search( - @Field("q") query: String?, - @Field("peer_id") peerId: Int?, - @Field("date") date: Long?, - @Field("preview_length") previewLength: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + query: String?, + peerId: Int?, + date: Long?, + previewLength: Int?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "messages.search", form( + "q" to query, + "peer_id" to peerId, + "date" to date, + "preview_length" to previewLength, + "offset" to offset, + "count" to count + ), items(VKApiMessage.serializer()) + ) + } /** * Returns updates in user's private messages. @@ -254,18 +342,31 @@ interface IMessageService { * (addition of a new message) from the history field. Each object of message contains a set * of fields described here. The first array element is the total number of messages. */ - @FormUrlEncoded - @POST("messages.getLongPollHistory") fun getLongPollHistory( - @Field("ts") ts: Long?, - @Field("pts") pts: Long?, - @Field("preview_length") previewLength: Int?, - @Field("onlines") onlines: Int?, - @Field("fields") fields: String?, - @Field("events_limit") eventsLimit: Int?, - @Field("msgs_limit") msgsLimit: Int?, - @Field("max_msg_id") maxMsgId: Int? - ): Single> + ts: Long?, + pts: Long?, + previewLength: Int?, + onlines: Int?, + fields: String?, + eventsLimit: Int?, + msgsLimit: Int?, + maxMsgId: Int? + ): Single> { + return rest.request( + "messages.getLongPollHistory", + form( + "ts" to ts, + "pts" to pts, + "preview_length" to previewLength, + "onlines" to onlines, + "fields" to fields, + "events_limit" to eventsLimit, + "msgs_limit" to msgsLimit, + "max_msg_id" to maxMsgId + ), + base(LongpollHistoryResponse.serializer()) + ) + } /** * Returns media files from the dialog or group chat. @@ -279,16 +380,27 @@ interface IMessageService { * @return a list of photo, video, audio or doc objects depending on media_type parameter value * and additional next_from field containing new offset value. */ - @FormUrlEncoded - @POST("messages.getHistoryAttachments") fun getHistoryAttachments( - @Field("peer_id") peerId: Int, - @Field("media_type") mediaType: String?, - @Field("start_from") startFrom: String?, - @Field("count") count: Int?, - @Field("photo_sizes") photoSizes: Int?, - @Field("fields") fields: String? - ): Single> + peerId: Int, + mediaType: String?, + startFrom: String?, + count: Int?, + photoSizes: Int?, + fields: String? + ): Single> { + return rest.request( + "messages.getHistoryAttachments", + form( + "peer_id" to peerId, + "media_type" to mediaType, + "start_from" to startFrom, + "count" to count, + "photo_sizes" to photoSizes, + "fields" to fields + ), + base(AttachmentsHistoryResponse.serializer()) + ) + } /** * Sends a message. @@ -306,21 +418,35 @@ interface IMessageService { * @param stickerId Sticker id * @return sent message ID. */ - @FormUrlEncoded - @POST("messages.send") fun send( - @Field("random_id") randomId: Long?, - @Field("peer_id") peerId: Int?, - @Field("domain") domain: String?, - @Field("message") message: String?, - @Field("lat") latitude: Double?, - @Field("long") longitude: Double?, - @Field("attachment") attachment: String?, - @Field("forward_messages") forwardMessages: String?, - @Field("sticker_id") stickerId: Int?, - @Field("payload") payload: String?, - @Field("reply_to") reply_to: Int? - ): Single> + randomId: Long?, + peerId: Int?, + domain: String?, + message: String?, + latitude: Double?, + longitude: Double?, + attachment: String?, + forwardMessages: String?, + stickerId: Int?, + payload: String?, + reply_to: Int? + ): Single> { + return rest.request( + "messages.send", form( + "random_id" to randomId, + "peer_id" to peerId, + "domain" to domain, + "message" to message, + "lat" to latitude, + "long" to longitude, + "attachment" to attachment, + "forward_messages" to forwardMessages, + "sticker_id" to stickerId, + "payload" to payload, + "reply_to" to reply_to + ), baseInt + ) + } /** * Returns messages by their IDs. @@ -331,39 +457,67 @@ interface IMessageService { * NOTE: Messages are not truncated by default. Messages are truncated by words. * @return a list of message objects. */ - @FormUrlEncoded - @POST("messages.getById") fun getById( - @Field("message_ids") messageIds: String?, - @Field("preview_length") previewLength: Int? - ): Single>> + messageIds: String?, + previewLength: Int? + ): Single>> { + return rest.request( + "messages.getById", form( + "message_ids" to messageIds, + "preview_length" to previewLength + ), items(VKApiMessage.serializer()) + ) + } //https://vk.com/dev/messages.getConversations - @FormUrlEncoded - @POST("messages.getConversations") fun getDialogs( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("start_message_id") startMessageId: Int?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + offset: Int?, + count: Int?, + startMessageId: Int?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "messages.getConversations", form( + "offset" to offset, + "count" to count, + "start_message_id" to startMessageId, + "extended" to extended, + "fields" to fields + ), base(DialogsResponse.serializer()) + ) + } //https://vk.com/dev/messages.getConversationsById - @FormUrlEncoded - @POST("messages.getConversationsById") fun getConversationsById( - @Field("peer_ids") peerIds: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single>> + peerIds: String?, + extended: Int?, + fields: String? + ): Single>> { + return rest.request( + "messages.getConversationsById", + form( + "peer_ids" to peerIds, + "extended" to extended, + "fields" to fields + ), + base(ItemsProfilesGroupsResponse.serializer(VKApiConversation.serializer())) + ) + } - @FormUrlEncoded - @POST("messages.getLongPollServer") fun getLongpollServer( - @Field("need_pts") needPts: Int, - @Field("lp_version") lpVersion: Int - ): Single> + needPts: Int, + lpVersion: Int + ): Single> { + return rest.request( + "messages.getLongPollServer", + form( + "need_pts" to needPts, + "lp_version" to lpVersion + ), + base(VKApiLongpollServer.serializer()) + ) + } /** * Returns message history for the specified user or group chat. @@ -377,58 +531,106 @@ interface IMessageService { * 0 — return messages in reverse chronological order. * @return Returns a list of message objects. */ - @FormUrlEncoded - @POST("messages.getHistory") fun getHistory( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("peer_id") peerId: Int, - @Field("start_message_id") startMessageId: Int?, - @Field("rev") rev: Int?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> - - @FormUrlEncoded - @POST("messages.getHistory") + offset: Int?, + count: Int?, + peerId: Int, + startMessageId: Int?, + rev: Int?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "messages.getHistory", + form( + "offset" to offset, + "count" to count, + "peer_id" to peerId, + "start_message_id" to startMessageId, + "rev" to rev, + "extended" to extended, + "fields" to fields + ), + base(MessageHistoryResponse.serializer()) + ) + } + fun getJsonHistory( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("peer_id") peerId: Int - ): Single>> + offset: Int?, + count: Int?, + peerId: Int + ): Single>> { + return rest.request( + "messages.getHistory", form( + "offset" to offset, + "count" to count, + "peer_id" to peerId + ), items(VKApiJsonString.serializer()) + ) + } - @FormUrlEncoded - @POST("messages.getImportantMessages") fun getImportantMessages( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("start_message_id") startMessageId: Int?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + offset: Int?, + count: Int?, + startMessageId: Int?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "messages.getImportantMessages", + form( + "offset" to offset, + "count" to count, + "start_message_id" to startMessageId, + "extended" to extended, + "fields" to fields + ), + base(MessageImportantResponse.serializer()) + ) + } //https://vk.com/dev/messages.searchDialogs - @FormUrlEncoded - @POST("messages.searchConversations") fun searchConversations( - @Field("q") q: String?, - @Field("count") count: Int?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> - - @FormUrlEncoded - @POST("messages.recogniseAudioMessage") + q: String?, + count: Int?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "messages.searchConversations", + form( + "q" to q, + "count" to count, + "extended" to extended, + "fields" to fields + ), + base(ConversationsResponse.serializer()) + ) + } + fun recogniseAudioMessage( - @Field("message_id") message_id: Int?, - @Field("audio_message_id") audio_message_id: String? - ): Single> + message_id: Int?, + audio_message_id: String? + ): Single> { + return rest.request( + "messages.recogniseAudioMessage", form( + "message_id" to message_id, + "audio_message_id" to audio_message_id + ), baseInt + ) + } - @FormUrlEncoded - @POST("messages.setMemberRole") fun setMemberRole( - @Field("peer_id") peer_id: Int?, - @Field("member_id") member_id: Int?, - @Field("role") role: String? - ): Single> + peer_id: Int?, + member_id: Int?, + role: String? + ): Single> { + return rest.request( + "messages.setMemberRole", form( + "peer_id" to peer_id, + "member_id" to member_id, + "role" to role + ), baseInt + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/INewsfeedService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/INewsfeedService.kt index 8d113202e..41d633ca4 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/INewsfeedService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/INewsfeedService.kt @@ -3,94 +3,143 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.Items import dev.ragnarok.fenrir.api.model.VKApiFeedList import dev.ragnarok.fenrir.api.model.response.* +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface INewsfeedService { +class INewsfeedService : IServiceRest() { /** * filters post, photo, video, topic, market, note */ //https://vk.com/dev/newsfeed.getComments - @FormUrlEncoded - @POST("newsfeed.getComments") fun getComments( - @Field("count") count: Int?, - @Field("filters") filters: String?, - @Field("reposts") reposts: String?, - @Field("start_time") startTime: Long?, - @Field("end_time") endTime: Long?, - @Field("last_comments_count") lastCommentsCount: Int?, - @Field("start_from") startFrom: String?, - @Field("fields") fields: String?, - @Field("photo_sizes") photoSizes: Int? - ): Single> + count: Int?, + filters: String?, + reposts: String?, + startTime: Long?, + endTime: Long?, + lastCommentsCount: Int?, + startFrom: String?, + fields: String?, + photoSizes: Int? + ): Single> { + return rest.request( + "newsfeed.getComments", form( + "count" to count, + "filters" to filters, + "reposts" to reposts, + "start_time" to startTime, + "end_time" to endTime, + "last_comments_count" to lastCommentsCount, + "start_from" to startFrom, + "fields" to fields, + "photo_sizes" to photoSizes + ), base(NewsfeedCommentsResponse.serializer()) + ) + } //https://vk.com/dev/newsfeed.getMentions - @FormUrlEncoded - @POST("newsfeed.getMentions") fun getMentions( - @Field("owner_id") owner_id: Int?, - @Field("count") count: Int?, - @Field("offset") offset: Int?, - @Field("start_time") startTime: Long?, - @Field("end_time") endTime: Long? - ): Single> + owner_id: Int?, + count: Int?, + offset: Int?, + startTime: Long?, + endTime: Long? + ): Single> { + return rest.request( + "newsfeed.getMentions", form( + "owner_id" to owner_id, + "count" to count, + "offset" to offset, + "start_time" to startTime, + "end_time" to endTime + ), base(NewsfeedCommentsResponse.serializer()) + ) + } //https://vk.com/dev/newsfeed.getLists - @FormUrlEncoded - @POST("newsfeed.getLists") fun getLists( - @Field("list_ids") listIds: String?, - @Field("extended") extended: Int? - ): Single>> + listIds: String?, + extended: Int? + ): Single>> { + return rest.request( + "newsfeed.getLists", form( + "list_ids" to listIds, + "extended" to extended + ), items(VKApiFeedList.serializer()) + ) + } //https://vk.com/dev/newsfeed.saveList - @FormUrlEncoded - @POST("newsfeed.saveList") fun saveList( - @Field("title") title: String?, - @Field("source_ids") source_ids: String? - ): Single> + title: String?, + source_ids: String? + ): Single> { + return rest.request( + "newsfeed.saveList", form( + "title" to title, + "source_ids" to source_ids + ), baseInt + ) + } //https://vk.com/dev/newsfeed.getBanned - @FormUrlEncoded - @POST("newsfeed.getBanned") fun getBanned( - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "newsfeed.getBanned", form( + "extended" to extended, + "fields" to fields + ), base(NewsfeedBanResponse.serializer()) + ) + } //https://vk.com/dev/newsfeed.deleteBan - @FormUrlEncoded - @POST("newsfeed.deleteBan") fun deleteBan( - @Field("user_ids") user_ids: String?, - @Field("group_ids") group_ids: String? - ): Single> + user_ids: String?, + group_ids: String? + ): Single> { + return rest.request( + "newsfeed.deleteBan", form( + "user_ids" to user_ids, + "group_ids" to group_ids + ), baseInt + ) + } //https://vk.com/dev/newsfeed.addBan - @FormUrlEncoded - @POST("newsfeed.addBan") fun addBan( - @Field("user_ids") user_ids: String?, - @Field("group_ids") group_ids: String? - ): Single> + user_ids: String?, + group_ids: String? + ): Single> { + return rest.request( + "newsfeed.addBan", form( + "user_ids" to user_ids, + "group_ids" to group_ids + ), baseInt + ) + } //https://vk.com/dev/newsfeed.ignoreItem - @FormUrlEncoded - @POST("newsfeed.ignoreItem") fun ignoreItem( - @Field("type") type: String?, - @Field("owner_id") owner_id: Int?, - @Field("item_id") item_id: Int? - ): Single> + type: String?, + owner_id: Int?, + item_id: Int? + ): Single> { + return rest.request( + "newsfeed.ignoreItem", form( + "type" to type, + "owner_id" to owner_id, + "item_id" to item_id + ), baseInt + ) + } //https://vk.com/dev/newsfeed.deleteList - @FormUrlEncoded - @POST("newsfeed.deleteList") - fun deleteList(@Field("list_id") list_id: Int?): Single> + fun deleteList(list_id: Int?): Single> { + return rest.request("newsfeed.deleteList", form("list_id" to list_id), baseInt) + } /** * Returns search results by statuses. @@ -107,19 +156,31 @@ interface INewsfeedService { * @param fields Additional fields of profiles and communities to return. * @return Returns the total number of posts and an array of wall objects */ - @FormUrlEncoded - @POST("newsfeed.search") fun search( - @Field("q") query: String?, - @Field("extended") extended: Int?, - @Field("count") count: Int?, - @Field("latitude") latitude: Double?, - @Field("longitude") longitude: Double?, - @Field("start_time") startTime: Long?, - @Field("end_time") endTime: Long?, - @Field("start_from") startFrom: String?, - @Field("fields") fields: String? - ): Single> + query: String?, + extended: Int?, + count: Int?, + latitude: Double?, + longitude: Double?, + startTime: Long?, + endTime: Long?, + startFrom: String?, + fields: String? + ): Single> { + return rest.request( + "newsfeed.search", form( + "q" to query, + "extended" to extended, + "count" to count, + "latitude" to latitude, + "longitude" to longitude, + "start_time" to startTime, + "end_time" to endTime, + "start_from" to startFrom, + "fields" to fields + ), base(NewsfeedSearchResponse.serializer()) + ) + } /** * Returns data required to show newsfeed for the current user. @@ -156,52 +217,93 @@ interface INewsfeedService { * new_offset — Contains an offset parameter that is passed to get the next array of news. * next_from — Contains a from parameter that is passed to get the next array of news. */ - @FormUrlEncoded - @POST("newsfeed.get") operator fun get( - @Field("filters") filters: String?, - @Field("return_banned") returnBanned: Int?, - @Field("start_time") startTime: Long?, - @Field("end_time") endTime: Long?, - @Field("max_photos") maxPhotoCount: Int?, - @Field("source_ids") sourceIds: String?, - @Field("start_from") startFrom: String?, - @Field("count") count: Int?, - @Field("fields") fields: String? - ): Single> + filters: String?, + returnBanned: Int?, + startTime: Long?, + endTime: Long?, + maxPhotoCount: Int?, + sourceIds: String?, + startFrom: String?, + count: Int?, + fields: String? + ): Single> { + return rest.request( + "newsfeed.get", form( + "filters" to filters, + "return_banned" to returnBanned, + "start_time" to startTime, + "end_time" to endTime, + "max_photos" to maxPhotoCount, + "source_ids" to sourceIds, + "start_from" to startFrom, + "count" to count, + "fields" to fields + ), base(NewsfeedResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("newsfeed.getByType") fun getByType( - @Field("feed_type") feed_type: String, - @Field("filters") filters: String?, - @Field("return_banned") returnBanned: Int?, - @Field("start_time") startTime: Long?, - @Field("end_time") endTime: Long?, - @Field("max_photos") maxPhotoCount: Int?, - @Field("source_ids") sourceIds: String?, - @Field("start_from") startFrom: String?, - @Field("count") count: Int?, - @Field("fields") fields: String? - ): Single> + feed_type: String, + filters: String?, + returnBanned: Int?, + startTime: Long?, + endTime: Long?, + maxPhotoCount: Int?, + sourceIds: String?, + startFrom: String?, + count: Int?, + fields: String? + ): Single> { + return rest.request( + "newsfeed.getByType", form( + "feed_type" to feed_type, + "filters" to filters, + "return_banned" to returnBanned, + "start_time" to startTime, + "end_time" to endTime, + "max_photos" to maxPhotoCount, + "source_ids" to sourceIds, + "start_from" to startFrom, + "count" to count, + "fields" to fields + ), base(NewsfeedResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("newsfeed.getRecommended") fun getRecommended( - @Field("start_time") startTime: Long?, - @Field("end_time") endTime: Long?, - @Field("max_photos") maxPhotoCount: Int?, - @Field("start_from") startFrom: String?, - @Field("count") count: Int?, - @Field("fields") fields: String? - ): Single> + startTime: Long?, + endTime: Long?, + maxPhotoCount: Int?, + startFrom: String?, + count: Int?, + fields: String? + ): Single> { + return rest.request( + "newsfeed.getRecommended", form( + "start_time" to startTime, + "end_time" to endTime, + "max_photos" to maxPhotoCount, + "start_from" to startFrom, + "count" to count, + "fields" to fields + ), base(NewsfeedResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("execute.getFeedLikes") fun getFeedLikes( - @Field("max_photos") maxPhotoCount: Int?, - @Field("start_from") startFrom: String?, - @Field("count") count: Int?, - @Field("fields") fields: String? - ): Single> + maxPhotoCount: Int?, + startFrom: String?, + count: Int?, + fields: String? + ): Single> { + return rest.request( + "execute.getFeedLikes", form( + "max_photos" to maxPhotoCount, + "start_from" to startFrom, + "count" to count, + "fields" to fields + ), base(NewsfeedResponse.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/INotificationsService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/INotificationsService.kt index f9247ab0d..d11f80013 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/INotificationsService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/INotificationsService.kt @@ -2,38 +2,53 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.NotificationsResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import dev.ragnarok.fenrir.model.FeedbackVKOfficialList import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface INotificationsService { - @POST("notifications.markAsViewed") - fun markAsViewed(): Single> +class INotificationsService : IServiceRest() { + val markAsViewed: Single> + get() = rest.request("notifications.markAsViewed", null, baseInt) - @FormUrlEncoded - @POST("notifications.get") operator fun get( - @Field("count") count: Int?, - @Field("start_from") startFrom: String?, - @Field("filters") filters: String?, - @Field("start_time") startTime: Long?, - @Field("end_time") endTime: Long? - ): Single> + count: Int?, + startFrom: String?, + filters: String?, + startTime: Long?, + endTime: Long? + ): Single> { + return rest.request( + "notifications.get", form( + "count" to count, + "start_from" to startFrom, + "filters" to filters, + "start_time" to startTime, + "end_time" to endTime + ), base(NotificationsResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("notifications.get") fun getOfficial( - @Field("count") count: Int?, - @Field("start_from") startFrom: Int?, - @Field("filters") filters: String?, - @Field("start_time") startTime: Long?, - @Field("end_time") endTime: Long?, - @Field("fields") fields: String? - ): Single> + count: Int?, + startFrom: Int?, + filters: String?, + startTime: Long?, + endTime: Long?, + fields: String? + ): Single> { + return rest.request( + "notifications.get", form( + "count" to count, + "start_from" to startFrom, + "filters" to filters, + "start_time" to startTime, + "end_time" to endTime, + "fields" to fields + ), base(FeedbackVKOfficialList.serializer()) + ) + } - @FormUrlEncoded - @POST("notifications.hide") - fun hide(@Field("query") query: String?): Single> + fun hide(query: String?): Single> { + return rest.request("notifications.hide", form("query" to query), baseInt) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPagesService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPagesService.kt index 351ee19ec..2d8f034bf 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPagesService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPagesService.kt @@ -2,22 +2,30 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.VKApiWikiPage import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IPagesService { +class IPagesService : IServiceRest() { //https://vk.com/dev/pages.get - @FormUrlEncoded - @POST("pages.get") operator fun get( - @Field("owner_id") ownerId: Int, - @Field("page_id") pageId: Int, - @Field("global") global: Int?, - @Field("site_preview") sitePreview: Int?, - @Field("title") title: String?, - @Field("need_source") needSource: Int?, - @Field("need_html") needHtml: Int? - ): Single> + ownerId: Int, + pageId: Int, + global: Int?, + sitePreview: Int?, + title: String?, + needSource: Int?, + needHtml: Int? + ): Single> { + return rest.request( + "pages.get", form( + "owner_id" to ownerId, + "page_id" to pageId, + "global" to global, + "site_preview" to sitePreview, + "title" to title, + "need_source" to needSource, + "need_html" to needHtml + ), base(VKApiWikiPage.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPhotosService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPhotosService.kt index 3180a48dc..9bc29cd00 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPhotosService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPhotosService.kt @@ -6,68 +6,105 @@ import dev.ragnarok.fenrir.api.model.response.DefaultCommentsResponse import dev.ragnarok.fenrir.api.model.response.UploadChatPhotoResponse import dev.ragnarok.fenrir.api.model.response.UploadOwnerPhotoResponse import dev.ragnarok.fenrir.api.model.server.* +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.GET -import retrofit2.http.POST -interface IPhotosService { +class IPhotosService : IServiceRest() { //https://vk.com/dev/photos.deleteAlbum - @FormUrlEncoded - @POST("photos.deleteAlbum") fun deleteAlbum( - @Field("album_id") albumId: Int, - @Field("group_id") groupId: Int? - ): Single> + albumId: Int, + groupId: Int? + ): Single> { + return rest.request( + "photos.deleteAlbum", form( + "album_id" to albumId, + "group_id" to groupId + ), baseInt + ) + } //https://vk.com/dev/photos.restore - @FormUrlEncoded - @POST("photos.restore") fun restore( - @Field("owner_id") ownerId: Int?, - @Field("photo_id") photoId: Int - ): Single> + ownerId: Int?, + photoId: Int + ): Single> { + return rest.request( + "photos.restore", form( + "owner_id" to ownerId, + "photo_id" to photoId + ), baseInt + ) + } //https://vk.com/dev/photos.delete - @FormUrlEncoded - @POST("photos.delete") fun delete( - @Field("owner_id") ownerId: Int?, - @Field("photo_id") photoId: Int - ): Single> + ownerId: Int?, + photoId: Int + ): Single> { + return rest.request( + "photos.delete", form( + "owner_id" to ownerId, + "photo_id" to photoId + ), baseInt + ) + } //https://vk.com/dev/photos.deleteComment - @FormUrlEncoded - @POST("photos.deleteComment") fun deleteComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int - ): Single> + ownerId: Int?, + commentId: Int + ): Single> { + return rest.request( + "photos.deleteComment", form( + "owner_id" to ownerId, + "comment_id" to commentId + ), baseInt + ) + } //https://vk.com/dev/photos.restoreComment - @FormUrlEncoded - @POST("photos.restoreComment") fun restoreComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int - ): Single> + ownerId: Int?, + commentId: Int + ): Single> { + return rest.request( + "photos.restoreComment", form( + "owner_id" to ownerId, + "comment_id" to commentId + ), baseInt + ) + } //https://vk.com/dev/photos.getComments - @FormUrlEncoded - @POST("photos.getComments") fun getComments( - @Field("owner_id") ownerId: Int?, - @Field("photo_id") photoId: Int, - @Field("need_likes") needLikes: Int?, - @Field("start_comment_id") startCommentId: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("sort") sort: String?, - @Field("access_key") accessKey: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + ownerId: Int?, + photoId: Int, + needLikes: Int?, + startCommentId: Int?, + offset: Int?, + count: Int?, + sort: String?, + accessKey: String?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "photos.getComments", + form( + "owner_id" to ownerId, + "photo_id" to photoId, + "need_likes" to needLikes, + "start_comment_id" to startCommentId, + "offset" to offset, + "count" to count, + "sort" to sort, + "access_key" to accessKey, + "extended" to extended, + "fields" to fields + ), + base(DefaultCommentsResponse.serializer()) + ) + } /** * Edits a comment on a photo. @@ -89,14 +126,21 @@ interface IPhotosService { * List of comma-separated words * @return 1 */ - @FormUrlEncoded - @POST("photos.editComment") fun editComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int, - @Field("message") message: String?, - @Field("attachments") attachments: String? - ): Single> + ownerId: Int?, + commentId: Int, + message: String?, + attachments: String? + ): Single> { + return rest.request( + "photos.editComment", form( + "owner_id" to ownerId, + "comment_id" to commentId, + "message" to message, + "attachments" to attachments + ), baseInt + ) + } /** * Creates an empty photo album. @@ -114,17 +158,27 @@ interface IPhotosService { * 1 - commenting is disabled. * @return Returns an instance of photo album */ - @FormUrlEncoded - @POST("photos.createAlbum") fun createAlbum( - @Field("title") title: String?, - @Field("group_id") groupId: Int?, - @Field("description") description: String?, - @Field("privacy_view") privacyView: String?, - @Field("privacy_comment") privacyComment: String?, - @Field("upload_by_admins_only") uploadByAdminsOnly: Int?, - @Field("comments_disabled") commentsDisabled: Int? - ): Single> + title: String?, + groupId: Int?, + description: String?, + privacyView: String?, + privacyComment: String?, + uploadByAdminsOnly: Int?, + commentsDisabled: Int? + ): Single> { + return rest.request( + "photos.createAlbum", form( + "title" to title, + "group_id" to groupId, + "description" to description, + "privacy_view" to privacyView, + "privacy_comment" to privacyComment, + "upload_by_admins_only" to uploadByAdminsOnly, + "comments_disabled" to commentsDisabled + ), base(VKApiPhotoAlbum.serializer()) + ) + } /** * Edits information about a photo album. @@ -143,18 +197,29 @@ interface IPhotosService { * 1 - commenting is disabled. * @return 1 */ - @FormUrlEncoded - @POST("photos.editAlbum") fun editAlbum( - @Field("album_id") albumId: Int, - @Field("title") title: String?, - @Field("description") description: String?, - @Field("owner_id") ownerId: Int?, - @Field("privacy_view") privacyView: String?, - @Field("privacy_comment") privacyComment: String?, - @Field("upload_by_admins_only") uploadByAdminsOnly: Int?, - @Field("comments_disabled") commentsDisabled: Int? - ): Single> + albumId: Int, + title: String?, + description: String?, + ownerId: Int?, + privacyView: String?, + privacyComment: String?, + uploadByAdminsOnly: Int?, + commentsDisabled: Int? + ): Single> { + return rest.request( + "photos.editAlbum", form( + "album_id" to albumId, + "title" to title, + "description" to description, + "owner_id" to ownerId, + "privacy_view" to privacyView, + "privacy_comment" to privacyComment, + "upload_by_admins_only" to uploadByAdminsOnly, + "comments_disabled" to commentsDisabled + ), baseInt + ) + } /** * Allows to copy a photo to the "Saved photos" album @@ -164,181 +229,332 @@ interface IPhotosService { * @param accessKey special access key for private photos * @return Returns the created photo ID. */ - @FormUrlEncoded - @POST("photos.copy") fun copy( - @Field("owner_id") ownerId: Int, - @Field("photo_id") photoId: Int, - @Field("access_key") accessKey: String? - ): Single> + ownerId: Int, + photoId: Int, + accessKey: String? + ): Single> { + return rest.request( + "photos.copy", form( + "owner_id" to ownerId, + "photo_id" to photoId, + "access_key" to accessKey + ), baseInt + ) + } - @FormUrlEncoded - @POST("photos.createComment") fun createComment( - @Field("owner_id") ownerId: Int?, - @Field("photo_id") photoId: Int, - @Field("from_group") fromGroup: Int?, - @Field("message") message: String?, - @Field("reply_to_comment") replyToComment: Int?, - @Field("attachments") attachments: String?, - @Field("sticker_id") stickerId: Int?, - @Field("access_key") accessKey: String?, - @Field("guid") generatedUniqueId: Int? - ): Single> + ownerId: Int?, + photoId: Int, + fromGroup: Int?, + message: String?, + replyToComment: Int?, + attachments: String?, + stickerId: Int?, + accessKey: String?, + generatedUniqueId: Int? + ): Single> { + return rest.request( + "photos.createComment", form( + "owner_id" to ownerId, + "photo_id" to photoId, + "from_group" to fromGroup, + "message" to message, + "reply_to_comment" to replyToComment, + "attachments" to attachments, + "sticker_id" to stickerId, + "access_key" to accessKey, + "guid" to generatedUniqueId + ), baseInt + ) + } - @FormUrlEncoded - @POST("photos.getById") fun getById( - @Field("photos") photos: String?, - @Field("extended") extended: Int?, - @Field("photo_sizes") photo_sizes: Int? - ): Single>> + photos: String?, + extended: Int?, + photo_sizes: Int? + ): Single>> { + return rest.request( + "photos.getById", form( + "photos" to photos, + "extended" to extended, + "photo_sizes" to photo_sizes + ), baseList(VKApiPhoto.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getUploadServer") fun getUploadServer( - @Field("album_id") albumId: Int, - @Field("group_id") groupId: Int? - ): Single> + albumId: Int, + groupId: Int? + ): Single> { + return rest.request( + "photos.getUploadServer", form( + "album_id" to albumId, + "group_id" to groupId + ), base(VKApiUploadServer.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.saveOwnerPhoto") fun saveOwnerPhoto( - @Field("server") server: String?, - @Field("hash") hash: String?, - @Field("photo") photo: String? - ): Single> + server: String?, + hash: String?, + photo: String? + ): Single> { + return rest.request( + "photos.saveOwnerPhoto", + form( + "server" to server, + "hash" to hash, + "photo" to photo + ), + base(UploadOwnerPhotoResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("messages.setChatPhoto") - fun setChatPhoto(@Field("file") file: String?): Single> + fun setChatPhoto(file: String?): Single> { + return rest.request( + "messages.setChatPhoto", + form("file" to file), + base(UploadChatPhotoResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getOwnerPhotoUploadServer") - fun getOwnerPhotoUploadServer(@Field("owner_id") ownerId: Int?): Single> + fun getOwnerPhotoUploadServer(ownerId: Int?): Single> { + return rest.request( + "photos.getOwnerPhotoUploadServer", + form("owner_id" to ownerId), + base(VKApiOwnerPhotoUploadServer.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getChatUploadServer") - fun getChatUploadServer(@Field("chat_id") chat_id: Int?): Single> + fun getChatUploadServer(chat_id: Int?): Single> { + return rest.request( + "photos.getChatUploadServer", + form("chat_id" to chat_id), + base(VKApiChatPhotoUploadServer.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.saveWallPhoto") fun saveWallPhoto( - @Field("user_id") userId: Int?, - @Field("group_id") groupId: Int?, - @Field("photo") photo: String?, - @Field("server") server: Int, - @Field("hash") hash: String?, - @Field("latitude") latitude: Double?, - @Field("longitude") longitude: Double?, - @Field("caption") caption: String? - ): Single>> + userId: Int?, + groupId: Int?, + photo: String?, + server: Int, + hash: String?, + latitude: Double?, + longitude: Double?, + caption: String? + ): Single>> { + return rest.request( + "photos.saveWallPhoto", form( + "user_id" to userId, + "group_id" to groupId, + "photo" to photo, + "server" to server, + "hash" to hash, + "latitude" to latitude, + "longitude" to longitude, + "caption" to caption + ), baseList(VKApiPhoto.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getWallUploadServer") - fun getWallUploadServer(@Field("group_id") groupId: Int?): Single> + fun getWallUploadServer(groupId: Int?): Single> { + return rest.request( + "photos.getWallUploadServer", + form("group_id" to groupId), + base(VKApiWallUploadServer.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.save") fun save( - @Field("album_id") albumId: Int, - @Field("group_id") groupId: Int?, - @Field("server") server: Int, - @Field("photos_list") photosList: String?, - @Field("hash") hash: String?, - @Field("latitude") latitude: Double?, - @Field("longitude") longitude: Double?, - @Field("caption") caption: String? - ): Single>> + albumId: Int, + groupId: Int?, + server: Int, + photosList: String?, + hash: String?, + latitude: Double?, + longitude: Double?, + caption: String? + ): Single>> { + return rest.request( + "photos.save", form( + "album_id" to albumId, + "group_id" to groupId, + "server" to server, + "photos_list" to photosList, + "hash" to hash, + "latitude" to latitude, + "longitude" to longitude, + "caption" to caption + ), baseList(VKApiPhoto.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.get") operator fun get( - @Field("owner_id") ownerId: Int?, - @Field("album_id") albumId: String?, - @Field("photo_ids") photoIds: String?, - @Field("rev") rev: Int?, - @Field("extended") extended: Int?, - @Field("photo_sizes") photoSizes: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + ownerId: Int?, + albumId: String?, + photoIds: String?, + rev: Int?, + extended: Int?, + photoSizes: Int?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "photos.get", form( + "owner_id" to ownerId, + "album_id" to albumId, + "photo_ids" to photoIds, + "rev" to rev, + "extended" to extended, + "photo_sizes" to photoSizes, + "offset" to offset, + "count" to count + ), items(VKApiPhoto.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getUserPhotos") fun getUserPhotos( - @Field("user_id") ownerId: Int?, - @Field("extended") extended: Int?, - @Field("sort") sort: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + ownerId: Int?, + extended: Int?, + sort: Int?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "photos.getUserPhotos", form( + "user_id" to ownerId, + "extended" to extended, + "sort" to sort, + "offset" to offset, + "count" to count + ), items(VKApiPhoto.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getAll") fun getAll( - @Field("owner_id") ownerId: Int?, - @Field("extended") extended: Int?, - @Field("photo_sizes") photo_sizes: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("no_service_albums") no_service_albums: Int?, - @Field("need_hidden") need_hidden: Int?, - @Field("skip_hidden") skip_hidden: Int? - ): Single>> + ownerId: Int?, + extended: Int?, + photo_sizes: Int?, + offset: Int?, + count: Int?, + no_service_albums: Int?, + need_hidden: Int?, + skip_hidden: Int? + ): Single>> { + return rest.request( + "photos.getAll", form( + "owner_id" to ownerId, + "extended" to extended, + "photo_sizes" to photo_sizes, + "offset" to offset, + "count" to count, + "no_service_albums" to no_service_albums, + "need_hidden" to need_hidden, + "skip_hidden" to skip_hidden + ), items(VKApiPhoto.serializer()) + ) + } - @get:GET("photos.getMessagesUploadServer") val messagesUploadServer: Single> + get() = rest.request( + "photos.getMessagesUploadServer", + null, + base(VKApiPhotoMessageServer.serializer()) + ) - @FormUrlEncoded - @POST("photos.saveMessagesPhoto") fun saveMessagesPhoto( - @Field("server") server: Int?, - @Field("photo") photo: String?, - @Field("hash") hash: String? - ): Single>> + server: Int?, + photo: String?, + hash: String? + ): Single>> { + return rest.request( + "photos.saveMessagesPhoto", form( + "server" to server, + "photo" to photo, + "hash" to hash + ), baseList(VKApiPhoto.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getAlbums") fun getAlbums( - @Field("owner_id") ownerId: Int?, - @Field("album_ids") albumIds: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("need_system") needSystem: Int?, - @Field("need_covers") needCovers: Int?, - @Field("photo_sizes") photoSizes: Int? - ): Single>> + ownerId: Int?, + albumIds: String?, + offset: Int?, + count: Int?, + needSystem: Int?, + needCovers: Int?, + photoSizes: Int? + ): Single>> { + return rest.request( + "photos.getAlbums", form( + "owner_id" to ownerId, + "album_ids" to albumIds, + "offset" to offset, + "count" to count, + "need_system" to needSystem, + "need_covers" to needCovers, + "photo_sizes" to photoSizes + ), items(VKApiPhotoAlbum.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getTags") fun getTags( - @Field("owner_id") ownerId: Int?, - @Field("photo_id") photo_id: Int?, - @Field("access_key") access_key: String? - ): Single>> + ownerId: Int?, + photo_id: Int?, + access_key: String? + ): Single>> { + return rest.request( + "photos.getTags", form( + "owner_id" to ownerId, + "photo_id" to photo_id, + "access_key" to access_key + ), baseList(VKApiPhotoTags.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.getAllComments") fun getAllComments( - @Field("owner_id") ownerId: Int?, - @Field("album_id") album_id: Int?, - @Field("need_likes") need_likes: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + ownerId: Int?, + album_id: Int?, + need_likes: Int?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "photos.getAllComments", form( + "owner_id" to ownerId, + "album_id" to album_id, + "need_likes" to need_likes, + "offset" to offset, + "count" to count + ), items(VKApiComment.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.search") fun search( - @Field("q") q: String?, - @Field("lat") lat_gps: Double?, - @Field("long") long_gps: Double?, - @Field("sort") sort: Int?, - @Field("radius") radius: Int?, - @Field("start_time") start_time: Long?, - @Field("end_time") end_time: Long?, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + q: String?, + lat_gps: Double?, + long_gps: Double?, + sort: Int?, + radius: Int?, + start_time: Long?, + end_time: Long?, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "photos.search", form( + "q" to q, + "lat" to lat_gps, + "long" to long_gps, + "sort" to sort, + "radius" to radius, + "start_time" to start_time, + "end_time" to end_time, + "offset" to offset, + "count" to count + ), items(VKApiPhoto.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPollsService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPollsService.kt index ce3083664..1551bc819 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPollsService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IPollsService.kt @@ -3,60 +3,97 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.VKApiPoll import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.PollUsersResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IPollsService { - @FormUrlEncoded - @POST("polls.create") +class IPollsService : IServiceRest() { fun create( - @Field("question") question: String?, - @Field("is_anonymous") isAnonymous: Int?, - @Field("is_multiple") isMultiple: Int?, - @Field("owner_id") ownerId: Int?, - @Field("add_answers") addAnswers: String? - ): Single> + question: String?, + isAnonymous: Int?, + isMultiple: Int?, + ownerId: Int?, + addAnswers: String? + ): Single> { + return rest.request( + "polls.create", form( + "question" to question, + "is_anonymous" to isAnonymous, + "is_multiple" to isMultiple, + "owner_id" to ownerId, + "add_answers" to addAnswers + ), base(VKApiPoll.serializer()) + ) + } //https://vk.com/dev/polls.deleteVote - @FormUrlEncoded - @POST("polls.deleteVote") fun deleteVote( - @Field("owner_id") ownerId: Int?, - @Field("poll_id") pollId: Int, - @Field("answer_id") answerId: Long, - @Field("is_board") isBoard: Int? - ): Single> + ownerId: Int?, + pollId: Int, + answerId: Long, + isBoard: Int? + ): Single> { + return rest.request( + "polls.deleteVote", form( + "owner_id" to ownerId, + "poll_id" to pollId, + "answer_id" to answerId, + "is_board" to isBoard + ), baseInt + ) + } //https://vk.com/dev/polls.addVote - @FormUrlEncoded - @POST("polls.addVote") fun addVote( - @Field("owner_id") ownerId: Int?, - @Field("poll_id") pollId: Int, - @Field("answer_ids") answerIds: String?, - @Field("is_board") isBoard: Int? - ): Single> + ownerId: Int?, + pollId: Int, + answerIds: String?, + isBoard: Int? + ): Single> { + return rest.request( + "polls.addVote", form( + "owner_id" to ownerId, + "poll_id" to pollId, + "answer_ids" to answerIds, + "is_board" to isBoard + ), baseInt + ) + } - @FormUrlEncoded - @POST("polls.getById") fun getById( - @Field("owner_id") ownerId: Int?, - @Field("is_board") isBoard: Int?, - @Field("poll_id") pollId: Int? - ): Single> + ownerId: Int?, + isBoard: Int?, + pollId: Int? + ): Single> { + return rest.request( + "polls.getById", form( + "owner_id" to ownerId, + "is_board" to isBoard, + "poll_id" to pollId + ), base(VKApiPoll.serializer()) + ) + } - @FormUrlEncoded - @POST("polls.getVoters") fun getVoters( - @Field("owner_id") ownerId: Int, - @Field("poll_id") pollId: Int, - @Field("is_board") isBoard: Int?, - @Field("answer_ids") answer_ids: String, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("fields") fields: String?, - @Field("name_case") nameCase: String? - ): Single>> + ownerId: Int, + pollId: Int, + isBoard: Int?, + answer_ids: String, + offset: Int?, + count: Int?, + fields: String?, + nameCase: String? + ): Single>> { + return rest.request( + "polls.getVoters", form( + "owner_id" to ownerId, + "poll_id" to pollId, + "is_board" to isBoard, + "answer_ids" to answer_ids, + "offset" to offset, + "count" to count, + "fields" to fields, + "name_case" to nameCase + ), baseList(PollUsersResponse.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStatusService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStatusService.kt index 61a297e64..1cec90062 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStatusService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStatusService.kt @@ -1,12 +1,10 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IStatusService { +class IStatusService : IServiceRest() { /** * Sets a new status for the current user. * @@ -14,10 +12,15 @@ interface IStatusService { * @param groupId Identifier of a community to set a status in. If left blank the status is set to current user. * @return 1 */ - @FormUrlEncoded - @POST("status.set") operator fun set( - @Field("text") text: String?, - @Field("group_id") groupId: Int? - ): Single> + text: String?, + groupId: Int? + ): Single> { + return rest.request( + "status.set", form( + "text" to text, + "group_id" to groupId + ), baseInt + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStoreService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStoreService.kt index 0e4fbf537..4ec92e9ec 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStoreService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStoreService.kt @@ -3,17 +3,23 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.VKApiStickerSetsData import dev.ragnarok.fenrir.api.model.VKApiStickersKeywords import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IStoreService { - @FormUrlEncoded - @POST("execute") - fun getStickers(@Field("code") code: String?): Single> +class IStoreService : IServiceRest() { + fun getStickers(code: String?): Single> { + return rest.request( + "execute", + form("code" to code), + base(VKApiStickerSetsData.serializer()) + ) + } - @FormUrlEncoded - @POST("execute") - fun getStickersKeywords(@Field("code") code: String?): Single> + fun getStickersKeywords(code: String?): Single> { + return rest.request( + "execute", + form("code" to code), + base(VKApiStickersKeywords.serializer()) + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUploadService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUploadService.kt index 42c531b4a..f48428fdf 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUploadService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUploadService.kt @@ -2,72 +2,69 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.upload.* +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single import okhttp3.MultipartBody -import retrofit2.http.Multipart -import retrofit2.http.POST -import retrofit2.http.Part -import retrofit2.http.Url -interface IUploadService { - @Multipart - @POST - fun uploadDocumentRx(@Url server: String?, @Part file: MultipartBody.Part): Single +class IUploadService : IServiceRest() { + fun uploadDocumentRx(server: String, file: MultipartBody.Part): Single { + return rest.doMultipartFormFullUrl(server, file, UploadDocDto.serializer()) + } - @Multipart - @POST - fun uploadAudioRx(@Url server: String?, @Part file: MultipartBody.Part): Single + fun uploadAudioRx(server: String, file: MultipartBody.Part): Single { + return rest.doMultipartFormFullUrl(server, file, UploadAudioDto.serializer()) + } - @Multipart - @POST fun remotePlayAudioRx( - @Url server: String?, - @Part file: MultipartBody.Part - ): Single> + server: String, + file: MultipartBody.Part + ): Single> { + return rest.doMultipartFormFullUrl(server, file, baseInt) + } - @Multipart - @POST fun uploadStoryRx( - @Url server: String?, - @Part file: MultipartBody.Part - ): Single> + server: String, + file: MultipartBody.Part + ): Single> { + return rest.doMultipartFormFullUrl(server, file, base(UploadStoryDto.serializer())) + } - @Multipart - @POST - fun uploadVideoRx(@Url server: String?, @Part file: MultipartBody.Part): Single + fun uploadVideoRx(server: String, file: MultipartBody.Part): Single { + return rest.doMultipartFormFullUrl(server, file, UploadVideoDto.serializer()) + } - @Multipart - @POST fun uploadOwnerPhotoRx( - @Url server: String?, - @Part photo: MultipartBody.Part - ): Single + server: String, + photo: MultipartBody.Part + ): Single { + return rest.doMultipartFormFullUrl(server, photo, UploadOwnerPhotoDto.serializer()) + } - @Multipart - @POST fun uploadChatPhotoRx( - @Url server: String?, - @Part photo: MultipartBody.Part - ): Single + server: String, + photo: MultipartBody.Part + ): Single { + return rest.doMultipartFormFullUrl(server, photo, UploadChatPhotoDto.serializer()) + } - @Multipart - @POST fun uploadPhotoToWallRx( - @Url server: String?, - @Part photo: MultipartBody.Part - ): Single + server: String, + photo: MultipartBody.Part + ): Single { + return rest.doMultipartFormFullUrl(server, photo, UploadPhotoToWallDto.serializer()) + } - @Multipart - @POST fun uploadPhotoToMessageRx( - @Url server: String?, - @Part photo: MultipartBody.Part - ): Single + server: String, + photo: MultipartBody.Part + ): Single { + return rest.doMultipartFormFullUrl(server, photo, UploadPhotoToMessageDto.serializer()) + } - @Multipart - @POST fun uploadPhotoToAlbumRx( - @Url server: String?, - @Part file1: MultipartBody.Part - ): Single + server: String, + file1: MultipartBody.Part + ): Single { + return rest.doMultipartFormFullUrl(server, file1, UploadPhotoToAlbumDto.serializer()) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUsersService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUsersService.kt index 17cd06f57..b92c7d15e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUsersService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUsersService.kt @@ -6,80 +6,137 @@ import dev.ragnarok.fenrir.api.model.response.StoryGetResponse import dev.ragnarok.fenrir.api.model.response.StoryResponse import dev.ragnarok.fenrir.api.model.response.UserWallInfoResponse import dev.ragnarok.fenrir.api.model.server.VKApiStoryUploadServer +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IUsersService { - @FormUrlEncoded - @POST("execute") +class IUsersService : IServiceRest() { fun getUserWallInfo( - @Field("code") code: String?, - @Field("user_id") userId: Int, - @Field("fields") fields: String?, - @Field("name_case") nameCase: String? - ): Single> + code: String?, + userId: Int, + fields: String?, + nameCase: String? + ): Single> { + return rest.request( + "execute", form( + "code" to code, + "user_id" to userId, + "fields" to fields, + "name_case" to nameCase + ), base(UserWallInfoResponse.serializer()) + ) + } //https://vk.com/dev/users.getFollowers - @FormUrlEncoded - @POST("users.getFollowers") fun getFollowers( - @Field("user_id") userId: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("fields") fields: String?, - @Field("name_case") nameCase: String? - ): Single>> - - @FormUrlEncoded - @POST("friends.getRequests") + userId: Int?, + offset: Int?, + count: Int?, + fields: String?, + nameCase: String? + ): Single>> { + return rest.request( + "users.getFollowers", form( + "user_id" to userId, + "offset" to offset, + "count" to count, + "fields" to fields, + "name_case" to nameCase + ), items(VKApiUser.serializer()) + ) + } + fun getRequests( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("extended") extended: Int?, - @Field("out") out: Int?, - @Field("fields") fields: String? - ): Single>> + offset: Int?, + count: Int?, + extended: Int?, + out: Int?, + fields: String? + ): Single>> { + return rest.request( + "friends.getRequests", form( + "offset" to offset, + "count" to count, + "extended" to extended, + "out" to out, + "fields" to fields + ), items(VKApiUser.serializer()) + ) + } //https://vk.com/dev/users.search - @FormUrlEncoded - @POST("users.search") fun search( - @Field("q") query: String?, - @Field("sort") sort: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("fields") fields: String?, - @Field("city") city: Int?, - @Field("country") country: Int?, - @Field("hometown") hometown: String?, - @Field("university_country") universityCountry: Int?, - @Field("university") university: Int?, - @Field("university_year") universityYear: Int?, - @Field("university_faculty") universityFaculty: Int?, - @Field("university_chair") universityChair: Int?, - @Field("sex") sex: Int?, - @Field("status") status: Int?, - @Field("age_from") ageFrom: Int?, - @Field("age_to") ageTo: Int?, - @Field("birth_day") birthDay: Int?, - @Field("birth_month") birthMonth: Int?, - @Field("birth_year") birthYear: Int?, - @Field("online") online: Int?, - @Field("has_photo") hasPhoto: Int?, - @Field("school_country") schoolCountry: Int?, - @Field("school_city") schoolCity: Int?, - @Field("school_class") schoolClass: Int?, - @Field("school") school: Int?, - @Field("school_year") schoolYear: Int?, - @Field("religion") religion: String?, - @Field("interests") interests: String?, - @Field("company") company: String?, - @Field("position") position: String?, - @Field("group_id") groupId: Int?, - @Field("from_list") fromList: String? - ): Single>> + query: String?, + sort: Int?, + offset: Int?, + count: Int?, + fields: String?, + city: Int?, + country: Int?, + hometown: String?, + universityCountry: Int?, + university: Int?, + universityYear: Int?, + universityFaculty: Int?, + universityChair: Int?, + sex: Int?, + status: Int?, + ageFrom: Int?, + ageTo: Int?, + birthDay: Int?, + birthMonth: Int?, + birthYear: Int?, + online: Int?, + hasPhoto: Int?, + schoolCountry: Int?, + schoolCity: Int?, + schoolClass: Int?, + school: Int?, + schoolYear: Int?, + religion: String?, + interests: String?, + company: String?, + position: String?, + groupId: Int?, + fromList: String? + ): Single>> { + return rest.request( + "users.search", form( + "q" to query, + "sort" to sort, + "offset" to offset, + "count" to count, + "fields" to fields, + "city" to city, + "country" to country, + "hometown" to hometown, + "university_country" to universityCountry, + "university" to university, + "university_year" to universityYear, + "university_faculty" to universityFaculty, + "university_chair" to universityChair, + "sex" to sex, + "status" to status, + "age_from" to ageFrom, + "age_to" to ageTo, + "birth_day" to birthDay, + "birth_month" to birthMonth, + "birth_year" to birthYear, + "online" to online, + "has_photo" to hasPhoto, + "school_country" to schoolCountry, + "school_city" to schoolCity, + "school_class" to schoolClass, + "school" to school, + "school_year" to schoolYear, + "religion" to religion, + "interests" to interests, + "company" to company, + "position" to position, + "group_id" to groupId, + "from_list" to fromList + ), items(VKApiUser.serializer()) + ) + } /** * Returns detailed information on users. @@ -97,80 +154,141 @@ interface IUsersService { * @return Returns a list of user objects. * A deactivated field may be returned with the value deleted or banned if a user has been suspended. */ - @FormUrlEncoded - @POST("users.get") operator fun get( - @Field("user_ids") userIds: String?, - @Field("fields") fields: String?, - @Field("name_case") nameCase: String? - ): Single>> + userIds: String?, + fields: String?, + nameCase: String? + ): Single>> { + return rest.request( + "users.get", form( + "user_ids" to userIds, + "fields" to fields, + "name_case" to nameCase + ), baseList(VKApiUser.serializer()) + ) + } - @FormUrlEncoded - @POST("stories.getPhotoUploadServer") - fun stories_getPhotoUploadServer(@Field("add_to_news") add_to_news: Int?): Single> + fun stories_getPhotoUploadServer(add_to_news: Int?): Single> { + return rest.request( + "stories.getPhotoUploadServer", + form("add_to_news" to add_to_news), + base(VKApiStoryUploadServer.serializer()) + ) + } - @FormUrlEncoded - @POST("stories.getVideoUploadServer") - fun stories_getVideoUploadServer(@Field("add_to_news") add_to_news: Int?): Single> + fun stories_getVideoUploadServer(add_to_news: Int?): Single> { + return rest.request( + "stories.getVideoUploadServer", + form("add_to_news" to add_to_news), + base(VKApiStoryUploadServer.serializer()) + ) + } - @FormUrlEncoded - @POST("stories.save") - fun stories_save(@Field("upload_results") upload_results: String?): Single>> + fun stories_save(upload_results: String?): Single>> { + return rest.request( + "stories.save", + form("upload_results" to upload_results), + items(VKApiStory.serializer()) + ) + } - @POST("users.report") - @FormUrlEncoded fun report( - @Field("user_id") userId: Int?, - @Field("type") type: String?, - @Field("comment") comment: String? - ): Single> + userId: Int?, + type: String?, + comment: String? + ): Single> { + return rest.request( + "users.report", form( + "user_id" to userId, + "type" to type, + "comment" to comment + ), baseInt + ) + } - @POST("stories.get") - @FormUrlEncoded fun getStory( - @Field("owner_id") owner_id: Int?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + owner_id: Int?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "stories.get", form( + "owner_id" to owner_id, + "extended" to extended, + "fields" to fields + ), base(StoryResponse.serializer()) + ) + } - @POST("narratives.getFromOwner") - @FormUrlEncoded fun getNarratives( - @Field("owner_id") owner_id: Int, - @Field("offset") offset: Int?, - @Field("count") count: Int? - ): Single>> + owner_id: Int, + offset: Int?, + count: Int? + ): Single>> { + return rest.request( + "narratives.getFromOwner", form( + "owner_id" to owner_id, + "offset" to offset, + "count" to count + ), items(VKApiNarratives.serializer()) + ) + } - @POST("stories.getById") - @FormUrlEncoded fun getStoryById( - @Field("stories") stories: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + stories: String?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "stories.getById", form( + "stories" to stories, + "extended" to extended, + "fields" to fields + ), base(StoryGetResponse.serializer()) + ) + } - @POST("gifts.get") - @FormUrlEncoded fun getGifts( - @Field("user_id") user_id: Int?, - @Field("count") count: Int?, - @Field("offset") offset: Int? - ): Single>> + user_id: Int?, + count: Int?, + offset: Int? + ): Single>> { + return rest.request( + "gifts.get", form( + "user_id" to user_id, + "count" to count, + "offset" to offset + ), items(VKApiGift.serializer()) + ) + } - @POST("stories.search") - @FormUrlEncoded fun searchStory( - @Field("q") q: String?, - @Field("mentioned_id") mentioned_id: Int?, - @Field("count") count: Int?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> - - @FormUrlEncoded - @POST("execute") + q: String?, + mentioned_id: Int?, + count: Int?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "stories.search", form( + "q" to q, + "mentioned_id" to mentioned_id, + "count" to count, + "extended" to extended, + "fields" to fields + ), base(StoryResponse.serializer()) + ) + } + fun checkAndAddFriend( - @Field("code") code: String?, - @Field("user_id") user_id: Int? - ): Single> + code: String?, + user_id: Int? + ): Single> { + return rest.request( + "execute", form( + "code" to code, + "user_id" to user_id + ), baseInt + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUtilsService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUtilsService.kt index 32eb5bc3d..0f9badb9d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUtilsService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IUtilsService.kt @@ -7,55 +7,74 @@ import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.ResolveDomailResponse import dev.ragnarok.fenrir.api.model.response.VKApiChatResponse import dev.ragnarok.fenrir.api.model.response.VKApiLinkResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.GET -import retrofit2.http.POST - -interface IUtilsService { - @FormUrlEncoded - @POST("utils.resolveScreenName") - fun resolveScreenName(@Field("screen_name") screenName: String?): Single> - - @FormUrlEncoded - @POST("utils.getShortLink") - fun getShortLink( - @Field("url") url: String?, - @Field("private") t_private: Int? - ): Single> - - @FormUrlEncoded - @POST("utils.getLastShortenedLinks") + +class IUtilsService : IServiceRest() { + fun resolveScreenName(screenName: String?): Single> { + return rest.request( + "utils.resolveScreenName", + form("screen_name" to screenName), + base(ResolveDomailResponse.serializer()) + ) + } + + fun getShortLink(url: String?, t_private: Int?): Single> { + return rest.request( + "utils.getShortLink", + form("url" to url, "private" to t_private), + base(VKApiShortLink.serializer()) + ) + } + fun getLastShortenedLinks( - @Field("count") count: Int?, - @Field("offset") offset: Int? - ): Single>> + count: Int?, + offset: Int? + ): Single>> { + return rest.request( + "utils.getLastShortenedLinks", form("count" to count, "offset" to offset), + items(VKApiShortLink.serializer()) + ) + } - @FormUrlEncoded - @POST("utils.deleteFromLastShortened") - fun deleteFromLastShortened(@Field("key") key: String?): Single> + fun deleteFromLastShortened(key: String?): Single> { + return rest.request( + "utils.deleteFromLastShortened", form("key" to key), + baseInt + ) + } - @FormUrlEncoded - @POST("utils.checkLink") - fun checkLink(@Field("url") url: String?): Single> + fun checkLink(url: String?): Single> { + return rest.request( + "utils.checkLink", form("url" to url), + base(VKApiCheckedLink.serializer()) + ) + } - @FormUrlEncoded - @POST("messages.joinChatByInviteLink") - fun joinChatByInviteLink(@Field("link") link: String?): Single> + fun joinChatByInviteLink(link: String?): Single> { + return rest.request( + "messages.joinChatByInviteLink", + form("link" to link), + base(VKApiChatResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("messages.getInviteLink") fun getInviteLink( - @Field("peer_id") peer_id: Int?, - @Field("reset") reset: Int? - ): Single> + peer_id: Int?, + reset: Int? + ): Single> { + return rest.request( + "messages.getInviteLink", + form("peer_id" to peer_id, "reset" to reset), + base(VKApiLinkResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("execute") - fun customScript(@Field("code") code: String?): Single> + fun customScript(code: String?): Single> { + return rest.request("execute", form("code" to code), baseInt) + } - @FormUrlEncoded - @GET("utils.getServerTime") - fun getServerTime(): Single> + fun getServerTime(): Single> { + return rest.request("utils.getServerTime", null, baseLong) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IVideoService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IVideoService.kt index e061bae13..6f4cde8be 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IVideoService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IVideoService.kt @@ -6,130 +6,225 @@ import dev.ragnarok.fenrir.api.model.VKApiVideoAlbum import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.DefaultCommentsResponse import dev.ragnarok.fenrir.api.model.response.SearchVideoResponse +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IVideoService { - @FormUrlEncoded - @POST("video.getComments") +class IVideoService : IServiceRest() { fun getComments( - @Field("owner_id") ownerId: Int?, - @Field("video_id") videoId: Int, - @Field("need_likes") needLikes: Int?, - @Field("start_comment_id") startCommentId: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("sort") sort: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + ownerId: Int?, + videoId: Int, + needLikes: Int?, + startCommentId: Int?, + offset: Int?, + count: Int?, + sort: String?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "video.getComments", form( + "owner_id" to ownerId, + "video_id" to videoId, + "need_likes" to needLikes, + "start_comment_id" to startCommentId, + "offset" to offset, + "count" to count, + "sort" to sort, + "extended" to extended, + "fields" to fields + ), base(DefaultCommentsResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("video.add") fun addVideo( - @Field("target_id") targetId: Int?, - @Field("video_id") videoId: Int?, - @Field("owner_id") ownerId: Int? - ): Single> + targetId: Int?, + videoId: Int?, + ownerId: Int? + ): Single> { + return rest.request( + "video.add", form( + "target_id" to targetId, + "video_id" to videoId, + "owner_id" to ownerId + ), baseInt + ) + } - @FormUrlEncoded - @POST("video.delete") fun deleteVideo( - @Field("video_id") videoId: Int?, - @Field("owner_id") ownerId: Int?, - @Field("target_id") targetId: Int? - ): Single> + videoId: Int?, + ownerId: Int?, + targetId: Int? + ): Single> { + return rest.request( + "video.delete", form( + "video_id" to videoId, + "owner_id" to ownerId, + "target_id" to targetId + ), baseInt + ) + } - @FormUrlEncoded - @POST("video.getAlbums") fun getAlbums( - @Field("owner_id") ownerId: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("extended") extended: Int?, - @Field("need_system") needSystem: Int? - ): Single>> + ownerId: Int?, + offset: Int?, + count: Int?, + extended: Int?, + needSystem: Int? + ): Single>> { + return rest.request( + "video.getAlbums", form( + "owner_id" to ownerId, + "offset" to offset, + "count" to count, + "extended" to extended, + "need_system" to needSystem + ), items(VKApiVideoAlbum.serializer()) + ) + } - @FormUrlEncoded - @POST("video.getAlbumsByVideo") fun getAlbumsByVideo( - @Field("target_id") target_id: Int?, - @Field("owner_id") owner_id: Int?, - @Field("video_id") video_id: Int?, - @Field("extended") extended: Int? - ): Single>> + target_id: Int?, + owner_id: Int?, + video_id: Int?, + extended: Int? + ): Single>> { + return rest.request( + "video.getAlbumsByVideo", form( + "target_id" to target_id, + "owner_id" to owner_id, + "video_id" to video_id, + "extended" to extended + ), items(VKApiVideoAlbum.serializer()) + ) + } - @FormUrlEncoded - @POST("video.search") fun search( - @Field("q") query: String?, - @Field("sort") sort: Int?, - @Field("hd") hd: Int?, - @Field("adult") adult: Int?, - @Field("filters") filters: String?, - @Field("search_own") searchOwn: Int?, - @Field("offset") offset: Int?, - @Field("longer") longer: Int?, - @Field("shorter") shorter: Int?, - @Field("count") count: Int?, - @Field("extended") extended: Int? - ): Single> + query: String?, + sort: Int?, + hd: Int?, + adult: Int?, + filters: String?, + searchOwn: Int?, + offset: Int?, + longer: Int?, + shorter: Int?, + count: Int?, + extended: Int? + ): Single> { + return rest.request( + "video.search", form( + "q" to query, + "sort" to sort, + "hd" to hd, + "adult" to adult, + "filters" to filters, + "search_own" to searchOwn, + "offset" to offset, + "longer" to longer, + "shorter" to shorter, + "count" to count, + "extended" to extended + ), base(SearchVideoResponse.serializer()) + ) + } - @FormUrlEncoded - @POST("video.restoreComment") fun restoreComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int - ): Single> + ownerId: Int?, + commentId: Int + ): Single> { + return rest.request( + "video.restoreComment", form( + "owner_id" to ownerId, + "comment_id" to commentId + ), baseInt + ) + } - @FormUrlEncoded - @POST("video.deleteComment") fun deleteComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int - ): Single> + ownerId: Int?, + commentId: Int + ): Single> { + return rest.request( + "video.deleteComment", form( + "owner_id" to ownerId, + "comment_id" to commentId + ), baseInt + ) + } - @FormUrlEncoded - @POST("video.get") operator fun get( - @Field("owner_id") ownerId: Int?, - @Field("videos") videos: String?, - @Field("album_id") albumId: Int?, - @Field("count") count: Int?, - @Field("offset") offset: Int?, - @Field("extended") extended: Int? - ): Single>> + ownerId: Int?, + videos: String?, + albumId: Int?, + count: Int?, + offset: Int?, + extended: Int? + ): Single>> { + return rest.request( + "video.get", form( + "owner_id" to ownerId, + "videos" to videos, + "album_id" to albumId, + "count" to count, + "offset" to offset, + "extended" to extended + ), items(VKApiVideo.serializer()) + ) + } - @FormUrlEncoded - @POST("video.createComment") fun createComment( - @Field("owner_id") ownerId: Int?, - @Field("video_id") videoId: Int, - @Field("message") message: String?, - @Field("attachments") attachments: String?, - @Field("from_group") fromGroup: Int?, - @Field("reply_to_comment") replyToComment: Int?, - @Field("sticker_id") stickerId: Int?, - @Field("guid") uniqueGeneratedId: Int? - ): Single> + ownerId: Int?, + videoId: Int, + message: String?, + attachments: String?, + fromGroup: Int?, + replyToComment: Int?, + stickerId: Int?, + uniqueGeneratedId: Int? + ): Single> { + return rest.request( + "video.createComment", form( + "owner_id" to ownerId, + "video_id" to videoId, + "message" to message, + "attachments" to attachments, + "from_group" to fromGroup, + "reply_to_comment" to replyToComment, + "sticker_id" to stickerId, + "guid" to uniqueGeneratedId + ), baseInt + ) + } - @FormUrlEncoded - @POST("video.editComment") fun editComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int, - @Field("message") message: String?, - @Field("attachments") attachments: String? - ): Single> + ownerId: Int?, + commentId: Int, + message: String?, + attachments: String? + ): Single> { + return rest.request( + "video.editComment", form( + "owner_id" to ownerId, + "comment_id" to commentId, + "message" to message, + "attachments" to attachments + ), baseInt + ) + } - @FormUrlEncoded - @POST("video.edit") fun edit( - @Field("owner_id") ownerId: Int?, - @Field("video_id") video_id: Int, - @Field("name") name: String?, - @Field("desc") desc: String? - ): Single> + ownerId: Int?, + video_id: Int, + name: String?, + desc: String? + ): Single> { + return rest.request( + "video.edit", form( + "owner_id" to ownerId, + "video_id" to video_id, + "name" to name, + "desc" to desc + ), baseInt + ) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IWallService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IWallService.kt index edb474a2a..6447c830e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IWallService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IWallService.kt @@ -1,90 +1,149 @@ package dev.ragnarok.fenrir.api.services import dev.ragnarok.fenrir.api.model.response.* +import dev.ragnarok.fenrir.api.rest.IServiceRest import io.reactivex.rxjava3.core.Single -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded -import retrofit2.http.POST -interface IWallService { +class IWallService : IServiceRest() { //https://vk.com/dev/wall.search - @FormUrlEncoded - @POST("wall.search") fun search( - @Field("owner_id") ownerId: Int?, - @Field("domain") domain: String?, - @Field("query") query: String?, - @Field("owners_only") ownersOnly: Int?, - @Field("count") count: Int?, - @Field("offset") offset: Int?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> + ownerId: Int?, + domain: String?, + query: String?, + ownersOnly: Int?, + count: Int?, + offset: Int?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "wall.search", form( + "owner_id" to ownerId, + "domain" to domain, + "query" to query, + "owners_only" to ownersOnly, + "count" to count, + "offset" to offset, + "extended" to extended, + "fields" to fields + ), base(WallSearchResponse.serializer()) + ) + } //https://vk.com/dev/wall.edit - @FormUrlEncoded - @POST("wall.edit") fun edit( - @Field("owner_id") ownerId: Int?, - @Field("post_id") postId: Int?, - @Field("friends_only") friendsOnly: Int?, - @Field("message") message: String?, - @Field("attachments") attachments: String?, - @Field("services") services: String?, - @Field("signed") signed: Int?, - @Field("publish_date") publishDate: Long?, - @Field("lat") latitude: Double?, - @Field("long") longitude: Double?, - @Field("place_id") placeId: Int?, - @Field("mark_as_ads") markAsAds: Int? - ): Single> + ownerId: Int?, + postId: Int?, + friendsOnly: Int?, + message: String?, + attachments: String?, + services: String?, + signed: Int?, + publishDate: Long?, + latitude: Double?, + longitude: Double?, + placeId: Int?, + markAsAds: Int? + ): Single> { + return rest.request( + "wall.edit", form( + "owner_id" to ownerId, + "post_id" to postId, + "friends_only" to friendsOnly, + "message" to message, + "attachments" to attachments, + "services" to services, + "signed" to signed, + "publish_date" to publishDate, + "lat" to latitude, + "long" to longitude, + "place_id" to placeId, + "mark_as_ads" to markAsAds + ), base(WallEditResponse.serializer()) + ) + } //https://vk.com/dev/wall.pin - @FormUrlEncoded - @POST("wall.pin") fun pin( - @Field("owner_id") ownerId: Int?, - @Field("post_id") postId: Int - ): Single> + ownerId: Int?, + postId: Int + ): Single> { + return rest.request( + "wall.pin", form( + "owner_id" to ownerId, + "post_id" to postId + ), baseInt + ) + } //https://vk.com/dev/wall.unpin - @FormUrlEncoded - @POST("wall.unpin") fun unpin( - @Field("owner_id") ownerId: Int?, - @Field("post_id") postId: Int - ): Single> + ownerId: Int?, + postId: Int + ): Single> { + return rest.request( + "wall.unpin", form( + "owner_id" to ownerId, + "post_id" to postId + ), baseInt + ) + } //https://vk.com/dev/wall.repost - @FormUrlEncoded - @POST("wall.repost") fun repost( - @Field("object") `object`: String?, - @Field("message") message: String?, - @Field("group_id") groupId: Int?, - @Field("mark_as_ads") markAsAds: Int? - ): Single> + pobject: String?, + message: String?, + groupId: Int?, + markAsAds: Int? + ): Single> { + return rest.request( + "wall.repost", form( + "object" to pobject, + "message" to message, + "group_id" to groupId, + "mark_as_ads" to markAsAds + ), base(RepostReponse.serializer()) + ) + } //https://vk.com/dev/wall.post - @FormUrlEncoded - @POST("wall.post") fun post( - @Field("owner_id") ownerId: Int?, - @Field("friends_only") friendsOnly: Int?, - @Field("from_group") fromGroup: Int?, - @Field("message") message: String?, - @Field("attachments") attachments: String?, - @Field("services") services: String?, - @Field("signed") signed: Int?, - @Field("publish_date") publishDate: Long?, - @Field("lat") latitude: Double?, - @Field("long") longitude: Double?, - @Field("place_id") placeId: Int?, - @Field("post_id") postId: Int?, - @Field("guid") guid: Int?, - @Field("mark_as_ads") markAsAds: Int?, - @Field("ads_promoted_stealth") adsPromotedStealth: Int? - ): Single> + ownerId: Int?, + friendsOnly: Int?, + fromGroup: Int?, + message: String?, + attachments: String?, + services: String?, + signed: Int?, + publishDate: Long?, + latitude: Double?, + longitude: Double?, + placeId: Int?, + postId: Int?, + guid: Int?, + markAsAds: Int?, + adsPromotedStealth: Int? + ): Single> { + return rest.request( + "wall.post", form( + "owner_id" to ownerId, + "friends_only" to friendsOnly, + "from_group" to fromGroup, + "message" to message, + "attachments" to attachments, + "services" to services, + "signed" to signed, + "publish_date" to publishDate, + "lat" to latitude, + "long" to longitude, + "place_id" to placeId, + "post_id" to postId, + "guid" to guid, + "mark_as_ads" to markAsAds, + "ads_promoted_stealth" to adsPromotedStealth + ), base(PostCreateResponse.serializer()) + ) + } /** * Deletes a post from a user wall or community wall. @@ -94,12 +153,17 @@ interface IWallService { * @param postId ID of the post to be deleted * @return 1 */ - @FormUrlEncoded - @POST("wall.delete") fun delete( - @Field("owner_id") ownerId: Int?, - @Field("post_id") postId: Int - ): Single> + ownerId: Int?, + postId: Int + ): Single> { + return rest.request( + "wall.delete", form( + "owner_id" to ownerId, + "post_id" to postId + ), baseInt + ) + } /** * Restores a comment deleted from a user wall or community wall. @@ -109,12 +173,17 @@ interface IWallService { * @param commentId Comment ID. * @return 1 */ - @FormUrlEncoded - @POST("wall.restoreComment") fun restoreComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int - ): Single> + ownerId: Int?, + commentId: Int + ): Single> { + return rest.request( + "wall.restoreComment", form( + "owner_id" to ownerId, + "comment_id" to commentId + ), baseInt + ) + } /** * Deletes a comment on a post on a user wall or community wall. @@ -124,12 +193,17 @@ interface IWallService { * @param commentId Comment ID. * @return 1 */ - @FormUrlEncoded - @POST("wall.deleteComment") fun deleteComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int - ): Single> + ownerId: Int?, + commentId: Int + ): Single> { + return rest.request( + "wall.deleteComment", form( + "owner_id" to ownerId, + "comment_id" to commentId + ), baseInt + ) + } /** * Restores a post deleted from a user wall or community wall. @@ -140,12 +214,17 @@ interface IWallService { * @param postId ID of the post to be restored. * @return 1 */ - @FormUrlEncoded - @POST("wall.restore") fun restore( - @Field("owner_id") ownerId: Int?, - @Field("post_id") postId: Int - ): Single> + ownerId: Int?, + postId: Int + ): Single> { + return rest.request( + "wall.restore", form( + "owner_id" to ownerId, + "post_id" to postId + ), baseInt + ) + } /** * Edits a comment on a user wall or community wall. @@ -168,86 +247,146 @@ interface IWallService { * List of comma-separated words * @return 1 */ - @FormUrlEncoded - @POST("wall.editComment") fun editComment( - @Field("owner_id") ownerId: Int?, - @Field("comment_id") commentId: Int, - @Field("message") message: String?, - @Field("attachments") attachments: String? - ): Single> - - @FormUrlEncoded - @POST("wall.createComment") + ownerId: Int?, + commentId: Int, + message: String?, + attachments: String? + ): Single> { + return rest.request( + "wall.editComment", form( + "owner_id" to ownerId, + "comment_id" to commentId, + "message" to message, + "attachments" to attachments + ), baseInt + ) + } + fun createComment( - @Field("owner_id") ownerId: Int?, - @Field("post_id") postId: Int, - @Field("from_group") fromGroup: Int?, - @Field("message") message: String?, - @Field("reply_to_comment") replyToComment: Int?, - @Field("attachments") attachments: String?, - @Field("sticker_id") stickerId: Int?, - @Field("guid") generatedUniqueId: Int? - ): Single> + ownerId: Int?, + postId: Int, + fromGroup: Int?, + message: String?, + replyToComment: Int?, + attachments: String?, + stickerId: Int?, + generatedUniqueId: Int? + ): Single> { + return rest.request( + "wall.createComment", form( + "owner_id" to ownerId, + "post_id" to postId, + "from_group" to fromGroup, + "message" to message, + "reply_to_comment" to replyToComment, + "attachments" to attachments, + "sticker_id" to stickerId, + "guid" to generatedUniqueId + ), base(CommentCreateResponse.serializer()) + ) + } //https://vk.com/dev/wall.getComments - @FormUrlEncoded - @POST("wall.getComments") fun getComments( - @Field("owner_id") ownerId: Int?, - @Field("post_id") postId: Int, - @Field("need_likes") needLikes: Int?, - @Field("start_comment_id") startCommentId: Int?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("sort") sort: String?, - @Field("extended") extended: Int?, - @Field("thread_items_count") thread_items_count: Int?, - @Field("fields") fields: String? - ): Single> - - @FormUrlEncoded - @POST("wall.get") + ownerId: Int?, + postId: Int, + needLikes: Int?, + startCommentId: Int?, + offset: Int?, + count: Int?, + sort: String?, + extended: Int?, + thread_items_count: Int?, + fields: String? + ): Single> { + return rest.request( + "wall.getComments", form( + "owner_id" to ownerId, + "post_id" to postId, + "need_likes" to needLikes, + "start_comment_id" to startCommentId, + "offset" to offset, + "count" to count, + "sort" to sort, + "extended" to extended, + "thread_items_count" to thread_items_count, + "fields" to fields + ), base(DefaultCommentsResponse.serializer()) + ) + } + operator fun get( - @Field("owner_id") ownerId: Int?, - @Field("domain") domain: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("filter") filter: String?, - @Field("extended") extended: Int?, - @Field("fields") fields: String? - ): Single> - - @FormUrlEncoded - @POST("wall.getById") + ownerId: Int?, + domain: String?, + offset: Int?, + count: Int?, + filter: String?, + extended: Int?, + fields: String? + ): Single> { + return rest.request( + "wall.get", form( + "owner_id" to ownerId, + "domain" to domain, + "offset" to offset, + "count" to count, + "filter" to filter, + "extended" to extended, + "fields" to fields + ), base(WallResponse.serializer()) + ) + } + fun getById( - @Field("posts") ids: String?, - @Field("extended") extended: Int?, - @Field("copy_history_depth") copyHistoryDepth: Int?, - @Field("fields") fields: String? - ): Single> - - @POST("wall.reportComment") - @FormUrlEncoded + ids: String?, + extended: Int?, + copyHistoryDepth: Int?, + fields: String? + ): Single> { + return rest.request( + "wall.getById", form( + "posts" to ids, + "extended" to extended, + "copy_history_depth" to copyHistoryDepth, + "fields" to fields + ), base(PostsResponse.serializer()) + ) + } + fun reportComment( - @Field("owner_id") owner_id: Int?, - @Field("comment_id") comment_id: Int?, - @Field("reason") reason: Int? - ): Single> + owner_id: Int?, + comment_id: Int?, + reason: Int? + ): Single> { + return rest.request( + "wall.reportComment", form( + "owner_id" to owner_id, + "comment_id" to comment_id, + "reason" to reason + ), baseInt + ) + } - @POST("wall.reportPost") - @FormUrlEncoded fun reportPost( - @Field("owner_id") owner_id: Int?, - @Field("post_id") post_id: Int?, - @Field("reason") reason: Int? - ): Single> - - @POST("wall.subscribe") - @FormUrlEncoded - fun subscribe(@Field("owner_id") owner_id: Int?): Single> - - @POST("wall.unsubscribe") - @FormUrlEncoded - fun unsubscribe(@Field("owner_id") owner_id: Int?): Single> + owner_id: Int?, + post_id: Int?, + reason: Int? + ): Single> { + return rest.request( + "wall.reportPost", form( + "owner_id" to owner_id, + "post_id" to post_id, + "reason" to reason + ), baseInt + ) + } + + fun subscribe(owner_id: Int?): Single> { + return rest.request("wall.subscribe", form("owner_id" to owner_id), baseInt) + } + + fun unsubscribe(owner_id: Int?): Single> { + return rest.request("wall.unsubscribe", form("owner_id" to owner_id), baseInt) + } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/AttachmentsStorage.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/AttachmentsStorage.kt index 32022f24b..c330f534e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/AttachmentsStorage.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/AttachmentsStorage.kt @@ -128,7 +128,7 @@ internal class AttachmentsStorage(base: AppStorages) : AbsStorage(base), IAttach if (count > 0) { e.onComplete() } else { - e.onError(NotFoundException()) + e.tryOnError(NotFoundException()) } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/CommentsStorage.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/CommentsStorage.kt index 02ebe976c..4fccc94e2 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/CommentsStorage.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/CommentsStorage.kt @@ -226,7 +226,7 @@ internal class CommentsStorage(base: AppStorages) : AbsStorage(base), ICommentsS if (id == null) { val uri = contentResolver.insert(commentsWithAccountUri, contentValues) if (uri == null) { - e.onError(DatabaseException("Result URI is null")) + e.tryOnError(DatabaseException("Result URI is null")) return@create } id = uri.pathSegments[1].toInt() diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/ContactsUtils.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/ContactsUtils.kt index 10fc84ccd..ac780a8ec 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/ContactsUtils.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/ContactsUtils.kt @@ -122,7 +122,7 @@ object ContactsUtils { null ) if (cursor == null) { - it.onError(Throwable("Can't collect contact list!")) + it.tryOnError(Throwable("Can't collect contact list!")) return@create } if (cursor.count > 0) { @@ -137,7 +137,7 @@ object ContactsUtils { cursor.close() } if (contacts.isEmpty()) { - it.onError(Throwable("Can't collect contact list!")) + it.tryOnError(Throwable("Can't collect contact list!")) } it.onSuccess( kJson.encodeToString(ListSerializer(ContactData.serializer()), contacts) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/KeysPersistStorage.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/KeysPersistStorage.kt index d319dd6f6..e9a76bdc6 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/KeysPersistStorage.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/KeysPersistStorage.kt @@ -34,7 +34,7 @@ internal class KeysPersistStorage(context: AppStorages) : AbsStorage(context), I val alreaadyExist = findKeyPairFor(pair.accountId, pair.sessionId) .blockingGet() if (alreaadyExist != null) { - e.onError(DatabaseException("Key pair with the session ID is already in the database")) + e.tryOnError(DatabaseException("Key pair with the session ID is already in the database")) return@create } val cv = ContentValues() diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/MessagesStorage.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/MessagesStorage.kt index b727d6ae1..b4af949c5 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/MessagesStorage.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/db/impl/MessagesStorage.kt @@ -504,7 +504,7 @@ internal class MessagesStorage(base: AppStorages) : AbsStorage(base), IMessagesS if (count > 0) { e.onComplete() } else { - e.onError(NotFoundException()) + e.tryOnError(NotFoundException()) } } } @@ -550,7 +550,7 @@ internal class MessagesStorage(base: AppStorages) : AbsStorage(base), IMessagesS if (count > 0) { e.onComplete() } else { - e.onError(NotFoundException()) + e.tryOnError(NotFoundException()) } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/dialog/audioduplicate/AudioDuplicatePresenter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/dialog/audioduplicate/AudioDuplicatePresenter.kt index ed5267a09..9c4356a8e 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/dialog/audioduplicate/AudioDuplicatePresenter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/dialog/audioduplicate/AudioDuplicatePresenter.kt @@ -60,10 +60,10 @@ class AudioDuplicatePresenter( if (bitrate != null) { v.onSuccess((bitrate.toLong() / 1000).toInt()) } else { - v.onError(Throwable("Can't receipt bitrate ")) + v.tryOnError(Throwable("Can't receipt bitrate ")) } } catch (e: RuntimeException) { - v.onError(e) + v.tryOnError(e) } } } @@ -122,13 +122,13 @@ class AudioDuplicatePresenter( if (bitrate != null) { v.onSuccess((bitrate.toLong() / 1000).toInt()) } else { - v.onError(Throwable("Can't receipt bitrate ")) + v.tryOnError(Throwable("Can't receipt bitrate ")) } } else { - v.onError(Throwable("Can't receipt bitrate ")) + v.tryOnError(Throwable("Can't receipt bitrate ")) } } catch (e: RuntimeException) { - v.onError(e) + v.tryOnError(e) } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/MessagesRepository.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/MessagesRepository.kt index 42f751af5..2916e2f55 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/MessagesRepository.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/MessagesRepository.kt @@ -534,7 +534,7 @@ class MessagesRepository( val resp = b?.let { kJson.decodeFromStream(ChatJsonResponse.serializer(), it) } b?.close() if (resp == null || resp.page_title.isNullOrEmpty()) { - its.onError(Throwable("parsing error")) + its.tryOnError(Throwable("parsing error")) return@create } val ids = VKOwnIds().append(resp.messages) @@ -1577,7 +1577,12 @@ class MessagesRepository( try { `is`[0] = FileInputStream(file) return@flatMap networker.uploads() - .uploadDocumentRx(server.url, file.name, `is`[0]!!, null) + .uploadDocumentRx( + server.url ?: throw NotFoundException("upload url empty"), + file.name, + `is`[0]!!, + null + ) .doFinally(safelyCloseAction(`is`[0])) .flatMap { uploadDto -> docsApi diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/accounts/AccountsFragment.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/accounts/AccountsFragment.kt index eff4674a4..398d7160c 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/accounts/AccountsFragment.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/accounts/AccountsFragment.kt @@ -40,6 +40,7 @@ import dev.ragnarok.fenrir.api.adapters.AbsAdapter.Companion.asPrimitiveSafe import dev.ragnarok.fenrir.api.adapters.AbsAdapter.Companion.has import dev.ragnarok.fenrir.api.model.VKApiUser import dev.ragnarok.fenrir.api.model.response.BaseResponse +import dev.ragnarok.fenrir.api.rest.HttpException import dev.ragnarok.fenrir.db.DBHelper import dev.ragnarok.fenrir.dialog.directauth.DirectAuthDialog import dev.ragnarok.fenrir.dialog.directauth.DirectAuthDialog.Companion.newInstance @@ -70,7 +71,6 @@ import dev.ragnarok.fenrir.util.ViewUtils.setupSwipeRefreshLayoutWithCurrentThem import dev.ragnarok.fenrir.util.rxutils.RxUtils import dev.ragnarok.fenrir.util.serializeble.json.* import dev.ragnarok.fenrir.util.serializeble.msgpack.MsgPack -import dev.ragnarok.fenrir.util.serializeble.retrofit.HttpCodeException import dev.ragnarok.fenrir.util.toast.CustomSnackbars import dev.ragnarok.fenrir.util.toast.CustomToast.Companion.createCustomToast import io.reactivex.rxjava3.core.Single @@ -737,7 +737,7 @@ class AccountsFragment : BaseFragment(), View.OnClickListener, AccountAdapter.Ca .add( "device_id", Utils.getDeviceId(provideApplicationContext()) ) - return Includes.networkInterfaces.getVkRetrofitProvider().provideRawHttpClient(type) + return Includes.networkInterfaces.getVkRestProvider().provideRawHttpClient(type) .flatMap { client -> Single.create { emitter: SingleEmitter -> val request: Request = Request.Builder() @@ -747,18 +747,18 @@ class AccountsFragment : BaseFragment(), View.OnClickListener, AccountAdapter.Ca ) .post(bodyBuilder.build()) .build() - val call = client.newCall(request) + val call = client.build().newCall(request) emitter.setCancellable { call.cancel() } try { val response = call.execute() if (!response.isSuccessful) { - emitter.onError(HttpCodeException(response.code)) + emitter.tryOnError(HttpException(response.code)) } else { emitter.onSuccess(response) } response.close() } catch (e: Exception) { - emitter.onError(e) + emitter.tryOnError(e) } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/audio/local/audioslocal/AudioLocalRecyclerAdapter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/audio/local/audioslocal/AudioLocalRecyclerAdapter.kt index 43efe4da5..3c0c48fe6 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/audio/local/audioslocal/AudioLocalRecyclerAdapter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/audio/local/audioslocal/AudioLocalRecyclerAdapter.kt @@ -89,13 +89,13 @@ class AudioLocalRecyclerAdapter(private val mContext: Context, private var data: if (bitrate != null && fl != null) { v.onSuccess(Pair((bitrate.toLong() / 1000).toInt(), File(fl).length())) } else { - v.onError(Throwable("Can't receipt bitrate ")) + v.tryOnError(Throwable("Can't receipt bitrate ")) } } else { - v.onError(Throwable("Can't receipt bitrate ")) + v.tryOnError(Throwable("Can't receipt bitrate ")) } } catch (e: RuntimeException) { - v.onError(e) + v.tryOnError(e) } } } @@ -120,16 +120,16 @@ class AudioLocalRecyclerAdapter(private val mContext: Context, private var data: it.onComplete() return@create } else { - it.onError(Throwable("Can't strip metadata")) + it.tryOnError(Throwable("Can't strip metadata")) } } else { - it.onError(Throwable("Can't find file")) + it.tryOnError(Throwable("Can't find file")) } } else { - it.onError(Throwable("Can't find file")) + it.tryOnError(Throwable("Can't find file")) } } catch (e: RuntimeException) { - it.onError(e) + it.tryOnError(e) } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/localserver/audioslocalserver/AudioLocalServerRecyclerAdapter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/localserver/audioslocalserver/AudioLocalServerRecyclerAdapter.kt index 2f37eefd3..1ae93cbb4 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/localserver/audioslocalserver/AudioLocalServerRecyclerAdapter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/localserver/audioslocalserver/AudioLocalServerRecyclerAdapter.kt @@ -79,10 +79,10 @@ class AudioLocalServerRecyclerAdapter( if (bitrate != null) { v.onSuccess((bitrate.toLong() / 1000).toInt()) } else { - v.onError(Throwable("Can't receipt bitrate ")) + v.tryOnError(Throwable("Can't receipt bitrate ")) } } catch (e: RuntimeException) { - v.onError(e) + v.tryOnError(e) } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/messages/chat/ChatPresenter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/messages/chat/ChatPresenter.kt index a797fe4d0..009b9b845 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/messages/chat/ChatPresenter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/messages/chat/ChatPresenter.kt @@ -2195,7 +2195,7 @@ class ChatPresenter( sInput[0] = FileInputStream(file) return@flatMap Includes.networkInterfaces.uploads() .uploadDocumentRx( - server.url, + server.url ?: throw NotFoundException("upload url empty"), if (filePath.isAnimated) filePath.animationName else file.name, sInput[0]!!, null diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/userwall/UserWallPresenter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/userwall/UserWallPresenter.kt index 4b0879736..1c7c2a508 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/userwall/UserWallPresenter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/userwall/UserWallPresenter.kt @@ -13,6 +13,7 @@ import dev.ragnarok.fenrir.api.HttpLoggerAndParser.toRequestBuilder import dev.ragnarok.fenrir.api.HttpLoggerAndParser.vkHeader import dev.ragnarok.fenrir.api.ProxyUtil import dev.ragnarok.fenrir.api.model.VKApiUser +import dev.ragnarok.fenrir.api.rest.HttpException import dev.ragnarok.fenrir.domain.* import dev.ragnarok.fenrir.domain.Repository.owners import dev.ragnarok.fenrir.domain.Repository.walls @@ -30,7 +31,6 @@ import dev.ragnarok.fenrir.util.Utils import dev.ragnarok.fenrir.util.Utils.getCauseIfRuntime import dev.ragnarok.fenrir.util.Utils.singletonArrayList import dev.ragnarok.fenrir.util.rxutils.RxUtils.ignore -import dev.ragnarok.fenrir.util.serializeble.retrofit.HttpCodeException import io.reactivex.rxjava3.core.Single import okhttp3.* import java.io.File @@ -715,7 +715,7 @@ class UserWallPresenter( try { val response = call.execute() if (!response.isSuccessful) { - emitter.onError(HttpCodeException(response.code)) + emitter.tryOnError(HttpException(response.code)) } else { val resp = response.body.string() val locale = Utils.appLocale @@ -772,12 +772,12 @@ class UserWallPresenter( ) ) } catch (e: ParseException) { - emitter.onError(e) + emitter.tryOnError(e) } } response.close() } catch (e: Exception) { - emitter.onError(e) + emitter.tryOnError(e) } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/longpoll/GroupLongpoll.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/longpoll/GroupLongpoll.kt index 6f15a768d..a95c49d69 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/longpoll/GroupLongpoll.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/longpoll/GroupLongpoll.kt @@ -64,7 +64,7 @@ internal class GroupLongpoll( if (validServer) { compositeDisposable.add( networker.longpoll() - .getGroupUpdates(server, key, ts, 25) + .getGroupUpdates(server ?: return, key, ts, 25) .fromIOToMain() .subscribe({ updates -> onUpdates(updates) }) { throwable -> onUpdatesGetError( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/service/ErrorLocalizer.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/service/ErrorLocalizer.kt index 880caac18..116d5eee3 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/service/ErrorLocalizer.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/service/ErrorLocalizer.kt @@ -3,10 +3,9 @@ package dev.ragnarok.fenrir.service import android.content.Context import dev.ragnarok.fenrir.R import dev.ragnarok.fenrir.api.ApiException +import dev.ragnarok.fenrir.api.rest.HttpException import dev.ragnarok.fenrir.exception.NotFoundException import dev.ragnarok.fenrir.nonNullNoEmpty -import dev.ragnarok.fenrir.util.serializeble.retrofit.HttpCodeException -import retrofit2.HttpException import java.net.SocketTimeoutException import java.net.UnknownHostException @@ -30,10 +29,11 @@ object ErrorLocalizer { context.getString(R.string.error_not_found_message) } is HttpException -> { - context.getString(R.string.vk_servers_error, throwable.code()) - } - is HttpCodeException -> { - context.getString(R.string.vk_servers_error, throwable.code) + if (throwable.code < 0) { + context.getString(R.string.client_rest_shutdown) + } else { + context.getString(R.string.vk_servers_error, throwable.code) + } } else -> throwable.message.nonNullNoEmpty({ it }, { throwable.toString() }) } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/AudioToMessageUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/AudioToMessageUploadable.kt index d12c68a59..b9c72d9d6 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/AudioToMessageUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/AudioToMessageUploadable.kt @@ -71,7 +71,12 @@ class AudioToMessageUploadable( val finalArtist = Artist val finalTrackName = TrackName return@flatMap networker.uploads() - .uploadAudioRx(server.url, filename, `is`, listener) + .uploadAudioRx( + server.url ?: throw NotFoundException("upload url empty"), + filename, + `is`, + listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> networker diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/AudioUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/AudioUploadable.kt index 4dba617e6..5eb802633 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/AudioUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/AudioUploadable.kt @@ -62,7 +62,12 @@ class AudioUploadable(private val context: Context, private val networker: INetw val finalArtist = Artist val finalTrackName = TrackName return@flatMap networker.uploads() - .uploadAudioRx(server.url, filename, `is`, listener) + .uploadAudioRx( + server.url ?: throw NotFoundException("upload url empty"), + filename, + `is`, + listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> networker diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/ChatPhotoUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/ChatPhotoUploadable.kt index 89e7ae2dd..83afc1043 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/ChatPhotoUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/ChatPhotoUploadable.kt @@ -38,7 +38,11 @@ class ChatPhotoUploadable(private val context: Context, private val networker: I try { `is` = UploadUtils.openStream(context, upload.fileUri, upload.size) networker.uploads() - .uploadChatPhotoRx(server.url, `is`!!, listener) + .uploadChatPhotoRx( + server.url ?: throw NotFoundException("upload url empty"), + `is`!!, + listener + ) .doFinally { safelyClose(`is`) } .flatMap { dto -> networker.vkDefault(accountId) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/DocumentUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/DocumentUploadable.kt index de20eb45d..ee5f9dbc4 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/DocumentUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/DocumentUploadable.kt @@ -64,7 +64,12 @@ class DocumentUploadable( } val filename = UploadUtils.findFileName(context, uri) networker.uploads() - .uploadDocumentRx(server.url, filename, `is`, listener) + .uploadDocumentRx( + server.url ?: throw NotFoundException("upload url empty"), + filename, + `is`, + listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> networker diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/OwnerPhotoUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/OwnerPhotoUploadable.kt index 1c70c30c4..ddd937998 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/OwnerPhotoUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/OwnerPhotoUploadable.kt @@ -42,7 +42,11 @@ class OwnerPhotoUploadable( try { `is` = UploadUtils.openStream(context, upload.fileUri, upload.size) networker.uploads() - .uploadOwnerPhotoRx(server.url, `is`!!, listener) + .uploadOwnerPhotoRx( + server.url ?: throw NotFoundException("upload url empty"), + `is`!!, + listener + ) .doFinally { safelyClose(`is`) } .flatMap { dto -> networker.vkDefault(accountId) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2AlbumUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2AlbumUploadable.kt index 7dd3fa34b..ba51b3803 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2AlbumUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2AlbumUploadable.kt @@ -52,7 +52,11 @@ class Photo2AlbumUploadable( try { `is` = UploadUtils.openStream(context, upload.fileUri, upload.size) networker.uploads() - .uploadPhotoToAlbumRx(server.url, `is`!!, listener) + .uploadPhotoToAlbumRx( + server.url ?: throw NotFoundException("upload url empty"), + `is`!!, + listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> var latitude: Double? = null diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2MessageUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2MessageUploadable.kt index dfdc0ed76..ee98c445d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2MessageUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2MessageUploadable.kt @@ -47,7 +47,9 @@ class Photo2MessageUploadable( try { `is` = UploadUtils.openStream(context, upload.fileUri, upload.size) networker.uploads() - .uploadPhotoToMessageRx(server.url, `is`!!, listener) + .uploadPhotoToMessageRx( + server.url ?: throw NotFoundException("upload url empty"), `is`!!, listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> networker.vkDefault(accountId) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2WallUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2WallUploadable.kt index 51153ff89..bbdcf0db0 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2WallUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Photo2WallUploadable.kt @@ -46,7 +46,11 @@ class Photo2WallUploadable( try { `is` = UploadUtils.openStream(context, upload.fileUri, upload.size) networker.uploads() - .uploadPhotoToWallRx(server.url, `is`!!, listener) + .uploadPhotoToWallRx( + server.url ?: throw NotFoundException("upload url empty"), + `is`!!, + listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> networker.vkDefault(accountId) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/StoryUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/StoryUploadable.kt index 9d33b587a..e1fafc9df 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/StoryUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/StoryUploadable.kt @@ -56,7 +56,7 @@ class StoryUploadable(private val context: Context, private val networker: INetw val filename = UploadUtils.findFileName(context, uri) return@flatMap networker.uploads() .uploadStoryRx( - server.url, + server.url ?: throw NotFoundException("upload url empty"), filename, `is`, listener, diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Video2WallUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Video2WallUploadable.kt index a04b2d90c..e3603f135 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Video2WallUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/Video2WallUploadable.kt @@ -55,7 +55,12 @@ class Video2WallUploadable( } val filename = UploadUtils.findFileName(context, uri) networker.uploads() - .uploadVideoRx(server.url, filename, `is`, listener) + .uploadVideoRx( + server.url ?: throw NotFoundException("upload url empty"), + filename, + `is`, + listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> val video = Video().setId(dto.video_id).setOwnerId(dto.owner_id).setTitle( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/VideoToMessageUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/VideoToMessageUploadable.kt index 656c373a1..eed0a0bea 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/VideoToMessageUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/VideoToMessageUploadable.kt @@ -60,7 +60,12 @@ class VideoToMessageUploadable( context, uri ) networker.uploads() - .uploadVideoRx(server.url, filename, `is`, listener) + .uploadVideoRx( + server.url ?: throw NotFoundException("upload url empty"), + filename, + `is`, + listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> val video = Video().setId(dto.video_id).setOwnerId(dto.owner_id).setTitle( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/VideoUploadable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/VideoUploadable.kt index 6c03bd9ec..ac1bc9b11 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/VideoUploadable.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/upload/impl/VideoUploadable.kt @@ -56,7 +56,12 @@ class VideoUploadable(private val context: Context, private val networker: INetw } val filename = UploadUtils.findFileName(context, uri) return@flatMap networker.uploads() - .uploadVideoRx(server.url, filename, `is`, listener) + .uploadVideoRx( + server.url ?: throw NotFoundException("upload url empty"), + filename, + `is`, + listener + ) .doFinally(safelyCloseAction(`is`)) .flatMap { dto -> val result = UploadResult( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/Mp3InfoHelper.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/Mp3InfoHelper.kt index c0a369ef2..01d09d3a6 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/Mp3InfoHelper.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/Mp3InfoHelper.kt @@ -14,7 +14,7 @@ object Mp3InfoHelper { .build() val response = builder.build().newCall(request).execute() if (!response.isSuccessful) { - it.onError( + it.tryOnError( Exception( "Server return " + response.code + " " + response.message @@ -25,7 +25,7 @@ object Mp3InfoHelper { response.body.close() response.close() if (length.isNullOrEmpty()) { - it.onError(Exception("Empty content length!")) + it.tryOnError(Exception("Empty content length!")) } length?.let { o -> it.onSuccess(o.toLong()) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/DeserializationStrategyConverter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/DeserializationStrategyConverter.kt deleted file mode 100644 index 408896f93..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/DeserializationStrategyConverter.kt +++ /dev/null @@ -1,24 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization - -import dev.ragnarok.fenrir.api.model.response.VkResponse -import kotlinx.serialization.DeserializationStrategy -import okhttp3.ResponseBody -import retrofit2.Converter -import java.lang.reflect.Type - -internal class DeserializationStrategyConverter( - private val loader: DeserializationStrategy, - private val serializer: Serializer, - private val type: Type -) : Converter { - override fun convert(value: ResponseBody): T { - val result = serializer.fromResponseBody(loader, value) - if (result is VkResponse) { - result.error?.let { - it.type = type - it.serializer = serializer - } - } - return result - } -} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/Factory.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/Factory.kt deleted file mode 100644 index 03ecde2ac..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/Factory.kt +++ /dev/null @@ -1,100 +0,0 @@ -@file:JvmName("KotlinSerializationConverterFactory") - -package dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization - -import dev.ragnarok.fenrir.util.serializeble.json.Json -import dev.ragnarok.fenrir.util.serializeble.msgpack.MsgPack -import dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization.Serializer.FromBytes -import dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization.Serializer.FromString -import kotlinx.serialization.BinaryFormat -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.StringFormat -import okhttp3.MediaType -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.RequestBody -import okhttp3.ResponseBody -import retrofit2.Converter -import retrofit2.Retrofit -import java.lang.reflect.Type - -@ExperimentalSerializationApi -internal class Factory( - private val contentType: MediaType, - private val serializer: Serializer -) : Converter.Factory() { - @Suppress("RedundantNullableReturnType") // Retaining interface contract. - override fun responseBodyConverter( - type: Type, - annotations: Array, - retrofit: Retrofit - ): Converter? { - val loader = serializer.serializer(type) - return DeserializationStrategyConverter(loader, serializer, type) - } - - @Suppress("RedundantNullableReturnType") // Retaining interface contract. - override fun requestBodyConverter( - type: Type, - parameterAnnotations: Array, - methodAnnotations: Array, - retrofit: Retrofit - ): Converter<*, RequestBody>? { - val saver = serializer.serializer(type) - return SerializationStrategyConverter(contentType, saver, serializer) - } -} - -/** - * Return a [Converter.Factory] which uses Kotlin serialization for string-based payloads. - * - * Because Kotlin serialization is so flexible in the types it supports, this converter assumes - * that it can handle all types. If you are mixing this with something else, you must add this - * instance last to allow the other converters a chance to see their types. - */ -@ExperimentalSerializationApi -@JvmName("create") -fun StringFormat.asConverterFactory(contentType: MediaType): Converter.Factory { - return Factory(contentType, FromString(this)) -} - -@ExperimentalSerializationApi -@JvmName("create") -fun StringFormat.asConverterFactory(): Converter.Factory { - return Factory("application/json; charset=UTF-8".toMediaType(), FromString(this)) -} - -@ExperimentalSerializationApi -@JvmName("create") -fun Json.asConverterFactory(): Converter.Factory { - return Factory("application/json; charset=UTF-8".toMediaType(), Serializer.FromJson(this)) -} - -@ExperimentalSerializationApi -fun jsonMsgPackConverterFactory(json: Json, msgPack: MsgPack): Converter.Factory { - return Factory( - "application/json; charset=UTF-8".toMediaType(), - Serializer.FromJsonMsgPack(json, msgPack) - ) -} - -@ExperimentalSerializationApi -@JvmName("create") -fun MsgPack.asConverterFactory(): Converter.Factory { - return Factory( - "application/x-msgpack; charset=utf-8".toMediaType(), - Serializer.FromMsgPack(this) - ) -} - -/** - * Return a [Converter.Factory] which uses Kotlin serialization for byte-based payloads. - * - * Because Kotlin serialization is so flexible in the types it supports, this converter assumes - * that it can handle all types. If you are mixing this with something else, you must add this - * instance last to allow the other converters a chance to see their types. - */ -@ExperimentalSerializationApi -@JvmName("create") -fun BinaryFormat.asConverterFactory(contentType: MediaType): Converter.Factory { - return Factory(contentType, FromBytes(this)) -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/SerializationStrategyConverter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/SerializationStrategyConverter.kt deleted file mode 100644 index 6247c8944..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/SerializationStrategyConverter.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization - -import kotlinx.serialization.SerializationStrategy -import okhttp3.MediaType -import okhttp3.RequestBody -import retrofit2.Converter - -internal class SerializationStrategyConverter( - private val contentType: MediaType, - private val saver: SerializationStrategy, - private val serializer: Serializer -) : Converter { - override fun convert(value: T) = serializer.toRequestBody(contentType, saver, value) -} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/Serializer.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/Serializer.kt deleted file mode 100644 index 838431ef2..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/kotlinx/serialization/Serializer.kt +++ /dev/null @@ -1,122 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.kotlinx.serialization - -import dev.ragnarok.fenrir.isMsgPack -import dev.ragnarok.fenrir.util.serializeble.json.Json -import dev.ragnarok.fenrir.util.serializeble.json.internal.JavaStreamSerialReader -import dev.ragnarok.fenrir.util.serializeble.json.internal.decodeByReader -import dev.ragnarok.fenrir.util.serializeble.msgpack.MsgPack -import kotlinx.serialization.* -import okhttp3.MediaType -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.ResponseBody -import java.lang.reflect.Type - -sealed class Serializer { - abstract fun fromResponseBody(loader: DeserializationStrategy, body: ResponseBody): T - abstract fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody - - protected abstract val format: SerialFormat - - @ExperimentalSerializationApi // serializer(Type) is not stable. - fun serializer(type: Type): KSerializer = format.serializersModule.serializer(type) - - class FromString(override val format: StringFormat) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - val string = body.string() - return format.decodeFromString(loader, string) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val string = format.encodeToString(saver, value) - return string.toRequestBody(contentType) - } - } - - class FromJsonMsgPack(override val format: Json, private val msgPack: MsgPack) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - if (body.isMsgPack()) { - return msgPack.decodeFromOkioStream(loader, body.source()) - } - return format.decodeByReader(loader, JavaStreamSerialReader(body.byteStream())) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val string = format.encodeToString(saver, value) - return string.toRequestBody(contentType) - } - } - - class FromJson(override val format: Json) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - return format.decodeByReader(loader, JavaStreamSerialReader(body.byteStream())) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val string = format.encodeToString(saver, value) - return string.toRequestBody(contentType) - } - } - - class FromMsgPack(override val format: MsgPack) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - return format.decodeFromOkioStream(loader, body.source()) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val bytes = format.encodeToByteArray(saver, value) - return bytes.toRequestBody(contentType, 0, bytes.size) - } - } - - class FromBytes(override val format: BinaryFormat) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - val bytes = body.bytes() - return format.decodeFromByteArray(loader, bytes) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val bytes = format.encodeToByteArray(saver, value) - return bytes.toRequestBody(contentType, 0, bytes.size) - } - } -} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/BodyObservable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/BodyObservable.kt deleted file mode 100644 index 41c24e443..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/BodyObservable.kt +++ /dev/null @@ -1,98 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3 - -import dev.ragnarok.fenrir.api.HttpLoggerAndParser -import dev.ragnarok.fenrir.api.model.Params -import dev.ragnarok.fenrir.api.model.response.VkResponse -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Observer -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.exceptions.CompositeException -import io.reactivex.rxjava3.exceptions.Exceptions -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import okhttp3.FormBody -import retrofit2.HttpException -import retrofit2.Response - -internal class BodyObservable(private val upstream: Observable>) : - Observable() { - override fun subscribeActual(observer: Observer) { - upstream.subscribe(BodyObserver(observer)) - } - - private class BodyObserver(observer: Observer) : - Observer> { - private val observer: Observer - private var terminated = false - override fun onSubscribe(disposable: Disposable) { - observer.onSubscribe(disposable) - } - - override fun onNext(response: Response) { - val body = response.body() - if (response.isSuccessful && body != null) { - if (body is VkResponse) { - body.error?.let { - val o: ArrayList = when (val stmp = response.raw().request.body) { - is FormBody -> { - val f = ArrayList(stmp.size) - for (i in 0 until stmp.size) { - val tmp = Params() - tmp.key = stmp.name(i) - tmp.value = stmp.value(i) - f.add(tmp) - } - f - } - is HttpLoggerAndParser.GzipFormBody -> { - val f = ArrayList(stmp.original.size) - f.addAll(stmp.original) - f - } - else -> { - ArrayList() - } - } - val tmp = Params() - tmp.key = "post_url" - tmp.value = response.raw().request.url.toString() - o.add(tmp) - it.requestParams = o - } - } - observer.onNext(body) - } else { - terminated = true - val t: Throwable = HttpException(response) - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - } - } - - override fun onComplete() { - if (!terminated) { - observer.onComplete() - } - } - - override fun onError(throwable: Throwable) { - if (!terminated) { - observer.onError(throwable) - } else { - // This should never happen! onNext handles and forwards errors automatically. - val broken: Throwable = AssertionError( - "This should never happen! Report as a bug with the full stacktrace." - ) - broken.initCause(throwable) - RxJavaPlugins.onError(broken) - } - } - - init { - this.observer = observer - } - } -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/CallEnqueueObservable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/CallEnqueueObservable.kt deleted file mode 100644 index 8fc3b2b9f..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/CallEnqueueObservable.kt +++ /dev/null @@ -1,75 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Observer -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.exceptions.CompositeException -import io.reactivex.rxjava3.exceptions.Exceptions -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response - -internal class CallEnqueueObservable(private val originalCall: Call) : - Observable>() { - override fun subscribeActual(observer: Observer>) { - // Since Call is a one-shot type, clone it for each new observer. - val call = originalCall.clone() - val callback = CallCallback(call, observer) - observer.onSubscribe(callback) - if (!callback.isDisposed) { - call.enqueue(callback) - } - } - - private class CallCallback( - private val call: Call<*>, - private val observer: Observer> - ) : Disposable, Callback { - var terminated = false - - @Volatile - private var disposed = false - override fun onResponse(call: Call, response: Response) { - if (disposed) return - try { - observer.onNext(response) - if (!disposed) { - terminated = true - observer.onComplete() - } - } catch (t: Throwable) { - Exceptions.throwIfFatal(t) - if (terminated) { - RxJavaPlugins.onError(t) - } else if (!disposed) { - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - } - } - } - - override fun onFailure(call: Call, t: Throwable) { - if (call.isCanceled) return - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - } - - override fun dispose() { - disposed = true - call.cancel() - } - - override fun isDisposed(): Boolean { - return disposed - } - } -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/CallExecuteObservable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/CallExecuteObservable.kt deleted file mode 100644 index 51884d9e9..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/CallExecuteObservable.kt +++ /dev/null @@ -1,59 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Observer -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.exceptions.CompositeException -import io.reactivex.rxjava3.exceptions.Exceptions -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.Call -import retrofit2.Response - -internal class CallExecuteObservable(private val originalCall: Call) : - Observable>() { - override fun subscribeActual(observer: Observer>) { - // Since Call is a one-shot type, clone it for each new observer. - val call = originalCall.clone() - val disposable = CallDisposable(call) - observer.onSubscribe(disposable) - if (disposable.isDisposed) { - return - } - var terminated = false - try { - val response = call.execute() - if (!disposable.isDisposed) { - observer.onNext(response) - } - if (!disposable.isDisposed) { - terminated = true - observer.onComplete() - } - } catch (t: Throwable) { - Exceptions.throwIfFatal(t) - if (terminated) { - RxJavaPlugins.onError(t) - } else if (!disposable.isDisposed) { - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - } - } - } - - private class CallDisposable(private val call: Call<*>) : Disposable { - @Volatile - private var disposed = false - override fun dispose() { - disposed = true - call.cancel() - } - - override fun isDisposed(): Boolean { - return disposed - } - } -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/Result.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/Result.kt deleted file mode 100644 index ff670cc90..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/Result.kt +++ /dev/null @@ -1,52 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3 - -import retrofit2.Response - -/** - * The result of executing an HTTP request. - */ -class Result private constructor( - private val response: Response?, - private val error: Throwable? -) { - /** - * The response received from executing an HTTP request. Only present when [.isError] is - * false, null otherwise. - */ - fun response(): Response? { - return response - } - - /** - * The error experienced while attempting to execute an HTTP request. Only present when [ ][.isError] is true, null otherwise. - * - * - * If the error is an [Throwable] then there was a problem with the transport to the - * remote server. Any other exception type indicates an unexpected failure and should be - * considered fatal (configuration error, programming error, etc.). - */ - fun error(): Throwable? { - return error - } - - /** - * `true` if the request resulted in an error. See [.error] for the cause. - */ - fun isError(): Boolean { - return error != null - } - - companion object { - // Guarding public API nullability. - fun error(error: Throwable?): Result { - if (error == null) throw NullPointerException("error == null") - return Result(null, error) - } - - // Guarding public API nullability. - fun response(response: Response?): Result { - if (response == null) throw NullPointerException("response == null") - return Result(response, null) - } - } -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/ResultObservable.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/ResultObservable.kt deleted file mode 100644 index 7c161a4e0..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/ResultObservable.kt +++ /dev/null @@ -1,46 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Observer -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.exceptions.CompositeException -import io.reactivex.rxjava3.exceptions.Exceptions -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.Response - -internal class ResultObservable(private val upstream: Observable>) : - Observable>() { - override fun subscribeActual(observer: Observer>) { - upstream.subscribe(ResultObserver(observer)) - } - - private class ResultObserver(private val observer: Observer>) : - Observer> { - override fun onSubscribe(disposable: Disposable) { - observer.onSubscribe(disposable) - } - - override fun onNext(response: Response) { - observer.onNext(Result.response(response)) - } - - override fun onError(throwable: Throwable) { - try { - observer.onNext(Result.error(throwable)) - } catch (t: Throwable) { - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - return - } - observer.onComplete() - } - - override fun onComplete() { - observer.onComplete() - } - } -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/RxJava3CallAdapter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/RxJava3CallAdapter.kt deleted file mode 100644 index 5cb3125a8..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/RxJava3CallAdapter.kt +++ /dev/null @@ -1,55 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.BackpressureStrategy -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Scheduler -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.Call -import retrofit2.CallAdapter -import java.lang.reflect.Type - -internal class RxJava3CallAdapter( - private val responseType: Type, - private val scheduler: Scheduler?, - private val isAsync: Boolean, - private val isResult: Boolean, - private val isBody: Boolean, - private val isFlowable: Boolean, - private val isSingle: Boolean, - private val isMaybe: Boolean, - private val isCompletable: Boolean -) : CallAdapter { - override fun responseType(): Type { - return responseType - } - - override fun adapt(call: Call): Any { - val responseObservable = - if (isAsync) CallEnqueueObservable(call) else CallExecuteObservable(call) - var observable: Observable<*> - observable = if (isResult) { - ResultObservable(responseObservable) - } else if (isBody) { - BodyObservable(responseObservable) - } else { - responseObservable - } - if (scheduler != null) { - observable = observable.subscribeOn(scheduler) - } - if (isFlowable) { - // We only ever deliver a single value, and the RS spec states that you MUST request at least - // one element which means we never need to honor backpressure. - return observable.toFlowable(BackpressureStrategy.MISSING) - } - if (isSingle) { - return observable.singleOrError() - } - if (isMaybe) { - return observable.singleElement() - } - return if (isCompletable) { - observable.ignoreElements() - } else RxJavaPlugins.onAssembly(observable) - } -} \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/RxJava3CallAdapterFactory.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/RxJava3CallAdapterFactory.kt deleted file mode 100644 index eba747bdf..000000000 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/util/serializeble/retrofit/rxjava3/RxJava3CallAdapterFactory.kt +++ /dev/null @@ -1,103 +0,0 @@ -package dev.ragnarok.fenrir.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.* -import retrofit2.CallAdapter -import retrofit2.Response -import retrofit2.Retrofit -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type - -class RxJava3CallAdapterFactory private constructor( - private val scheduler: Scheduler?, - private val isAsync: Boolean -) : CallAdapter.Factory() { - override fun get( - returnType: Type, annotations: Array, retrofit: Retrofit - ): CallAdapter<*, *>? { - val rawType = getRawType(returnType) - if (rawType == Completable::class.java) { - // Completable is not parameterized (which is what the rest of this method deals with) so it - // can only be created with a single configuration. - return RxJava3CallAdapter( - Void::class.java, scheduler, isAsync, - isResult = false, - isBody = true, - isFlowable = false, - isSingle = false, - isMaybe = false, - isCompletable = true - ) - } - val isFlowable = rawType == Flowable::class.java - val isSingle = rawType == Single::class.java - val isMaybe = rawType == Maybe::class.java - if (rawType != Observable::class.java && !isFlowable && !isSingle && !isMaybe) { - return null - } - var isResult = false - var isBody = false - val responseType: Type - if (returnType !is ParameterizedType) { - val name = - if (isFlowable) "Flowable" else if (isSingle) "Single" else if (isMaybe) "Maybe" else "Observable" - throw IllegalStateException( - name - + " return type must be parameterized" - + " as " - + name - + " or " - + name - + "" - ) - } - val observableType = getParameterUpperBound(0, returnType) - when (getRawType(observableType)) { - Response::class.java -> { - check(observableType is ParameterizedType) { "Response must be parameterized" + " as Response or Response" } - responseType = getParameterUpperBound(0, observableType) - } - Result::class.java -> { - check(observableType is ParameterizedType) { "Result must be parameterized" + " as Result or Result" } - responseType = getParameterUpperBound(0, observableType) - isResult = true - } - else -> { - responseType = observableType - isBody = true - } - } - return RxJava3CallAdapter( - responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false - ) - } - - companion object { - /** - * Returns an instance which creates asynchronous observables that run on a background thread by - * default. Applying `subscribeOn(..)` has no effect on instances created by the returned - * factory. - */ - fun create(): RxJava3CallAdapterFactory { - return RxJava3CallAdapterFactory(null, true) - } - - /** - * Returns an instance which creates synchronous observables that do not operate on any scheduler - * by default. Applying `subscribeOn(..)` will change the scheduler on which the HTTP calls - * are made. - */ - fun createSynchronous(): RxJava3CallAdapterFactory { - return RxJava3CallAdapterFactory(null, false) - } - - /** - * Returns an instance which creates synchronous observables that `subscribeOn(..)` the - * supplied `scheduler` by default. - */ - // Guarding public API nullability. - fun createWithScheduler(scheduler: Scheduler?): RxJava3CallAdapterFactory { - if (scheduler == null) throw NullPointerException("scheduler == null") - return RxJava3CallAdapterFactory(scheduler, false) - } - } -} \ No newline at end of file diff --git a/app_fenrir/src/main/res/values-be/strings.xml b/app_fenrir/src/main/res/values-be/strings.xml index efc1c1cb3..d06b6752c 100644 --- a/app_fenrir/src/main/res/values-be/strings.xml +++ b/app_fenrir/src/main/res/values-be/strings.xml @@ -1217,6 +1217,7 @@ Адправіць код праз SMS Аб\'ект не знойдзены Сервера ВК упалі (HTTP: %1$d) + Запыт скасаваны Хуткі пошук Краіны Паведамленні diff --git a/app_fenrir/src/main/res/values-ru/strings.xml b/app_fenrir/src/main/res/values-ru/strings.xml index e6e399ba6..7c41b75b5 100644 --- a/app_fenrir/src/main/res/values-ru/strings.xml +++ b/app_fenrir/src/main/res/values-ru/strings.xml @@ -1219,6 +1219,7 @@ Отправить код через SMS Объект не найден Сервера ВК упали (HTTP: %1$d) + Запрос отменён Быстрый поиск Страны Сообщения diff --git a/app_fenrir/src/main/res/values/strings.xml b/app_fenrir/src/main/res/values/strings.xml index 932e786b1..b4fc70a3f 100644 --- a/app_fenrir/src/main/res/values/strings.xml +++ b/app_fenrir/src/main/res/values/strings.xml @@ -1453,6 +1453,7 @@ Send code via SMS Object not found VK servers down (HTTP: %1$d) + Request canceled Quick search Countries Messages diff --git a/app_filegallery/build.gradle b/app_filegallery/build.gradle index 7d9068abc..86e4394a4 100644 --- a/app_filegallery/build.gradle +++ b/app_filegallery/build.gradle @@ -108,7 +108,6 @@ dependencies { implementation project(path: ":viewpager2") implementation project(path: ":material") implementation project(path: ":preference") - implementation project(path: ":retrofit") implementation project(path: ":camera2") implementation("com.squareup.okhttp3:okhttp-android:$okhttpLibraryVersion") //implementation("com.squareup.okhttp3:logging-interceptor:$okhttpLibraryVersion") diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/App.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/App.kt index ec66011b7..12648debb 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/App.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/App.kt @@ -1,6 +1,7 @@ package dev.ragnarok.filegallery import android.app.Application +import android.os.Handler import androidx.appcompat.app.AppCompatDelegate import androidx.camera.core.ImageProcessingUtil import dev.ragnarok.fenrir.module.FenrirNative @@ -8,9 +9,12 @@ import dev.ragnarok.filegallery.activity.crash.CrashUtils import dev.ragnarok.filegallery.media.music.MusicPlaybackController import dev.ragnarok.filegallery.picasso.PicassoInstance import dev.ragnarok.filegallery.settings.Settings +import dev.ragnarok.filegallery.util.ErrorLocalizer import dev.ragnarok.filegallery.util.Utils import dev.ragnarok.filegallery.util.existfile.FileExistJVM import dev.ragnarok.filegallery.util.existfile.FileExistNative +import dev.ragnarok.filegallery.util.toast.CustomToast.Companion.createCustomToast +import io.reactivex.rxjava3.plugins.RxJavaPlugins class App : Application() { override fun onCreate() { @@ -44,6 +48,19 @@ class App : Application() { Utils.isCompressIncomingTraffic = Settings.get().main().isCompress_incoming_traffic Utils.currentParser = Settings.get().main().currentParser PicassoInstance.init(this) + RxJavaPlugins.setErrorHandler { + it.printStackTrace() + Handler(mainLooper).post { + if (Settings.get().main().isDeveloper_mode()) { + createCustomToast(this, null)?.showToastError( + ErrorLocalizer.localizeThrowable( + this, + it + ) + ) + } + } + } } companion object { diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/IOtherRestProvider.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/IOtherRestProvider.kt new file mode 100644 index 000000000..93c3b62b8 --- /dev/null +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/IOtherRestProvider.kt @@ -0,0 +1,8 @@ +package dev.ragnarok.filegallery.api + +import dev.ragnarok.filegallery.api.rest.SimplePostHttp +import io.reactivex.rxjava3.core.Single + +interface IOtherRestProvider { + fun provideLocalServerRest(): Single +} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/IOtherRetrofitProvider.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/IOtherRetrofitProvider.kt deleted file mode 100644 index cfadd5b21..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/IOtherRetrofitProvider.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.ragnarok.filegallery.api - -import io.reactivex.rxjava3.core.Single - -interface IOtherRetrofitProvider { - fun provideLocalServerRetrofit(): Single -} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/OtherRetrofitProvider.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/OtherRestProvider.kt similarity index 61% rename from app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/OtherRetrofitProvider.kt rename to app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/OtherRestProvider.kt index 43da4ceb6..165ab70bd 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/OtherRetrofitProvider.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/OtherRestProvider.kt @@ -3,34 +3,30 @@ package dev.ragnarok.filegallery.api import android.annotation.SuppressLint import dev.ragnarok.filegallery.Constants import dev.ragnarok.filegallery.api.HttpLoggerAndParser.serverHeader -import dev.ragnarok.filegallery.kJson +import dev.ragnarok.filegallery.api.rest.SimplePostHttp import dev.ragnarok.filegallery.nonNullNoEmpty import dev.ragnarok.filegallery.settings.ISettings.IMainSettings import dev.ragnarok.filegallery.util.UncompressDefaultInterceptor import dev.ragnarok.filegallery.util.Utils.firstNonEmptyString -import dev.ragnarok.filegallery.util.serializeble.msgpack.MsgPack -import dev.ragnarok.filegallery.util.serializeble.retrofit.kotlinx.serialization.jsonMsgPackConverterFactory -import dev.ragnarok.filegallery.util.serializeble.retrofit.rxjava3.RxJava3CallAdapterFactory import io.reactivex.rxjava3.core.Single import okhttp3.FormBody import okhttp3.Interceptor import okhttp3.MultipartBody import okhttp3.OkHttpClient -import retrofit2.Retrofit import java.util.concurrent.TimeUnit -class OtherRetrofitProvider @SuppressLint("CheckResult") constructor(private val mainSettings: IMainSettings) : - IOtherRetrofitProvider { - private val localServerRetrofitLock = Any() - private var localServerRetrofitInstance: RetrofitWrapper? = null +class OtherRestProvider @SuppressLint("CheckResult") constructor(private val mainSettings: IMainSettings) : + IOtherRestProvider { + private val localServerRestLock = Any() + private var localServerRestInstance: SimplePostHttp? = null private fun onLocalServerSettingsChanged() { - synchronized(localServerRetrofitLock) { - localServerRetrofitInstance?.cleanup() - localServerRetrofitInstance = null + synchronized(localServerRestLock) { + localServerRestInstance?.stop() + localServerRestInstance = null } } - private fun createLocalServerRetrofit(): Retrofit { + private fun createLocalServerRest(): SimplePostHttp { val localSettings = mainSettings.getLocalServer() val builder = OkHttpClient.Builder() .readTimeout(15, TimeUnit.SECONDS) @@ -67,35 +63,22 @@ class OtherRetrofitProvider @SuppressLint("CheckResult") constructor(private val HttpLoggerAndParser.adjust(builder) HttpLoggerAndParser.configureToIgnoreCertificates(builder) val url = firstNonEmptyString(localSettings.url, "https://debug.dev")!! - return Retrofit.Builder() - .baseUrl("$url/method/") - .addConverterFactory( - KCONVERTER_FACTORY - ) - .addCallAdapterFactory(RX_ADAPTER_FACTORY) - .client(builder.build()) - .build() + return SimplePostHttp("$url/method", builder) } - override fun provideLocalServerRetrofit(): Single { + override fun provideLocalServerRest(): Single { return Single.fromCallable { - if (localServerRetrofitInstance == null) { - synchronized(localServerRetrofitLock) { - if (localServerRetrofitInstance == null) { - localServerRetrofitInstance = - RetrofitWrapper.wrap(createLocalServerRetrofit()) + if (localServerRestInstance == null) { + synchronized(localServerRestLock) { + if (localServerRestInstance == null) { + localServerRestInstance = createLocalServerRest() } } } - localServerRetrofitInstance!! + localServerRestInstance!! } } - companion object { - private val KCONVERTER_FACTORY = jsonMsgPackConverterFactory(kJson, MsgPack()) - private val RX_ADAPTER_FACTORY = RxJava3CallAdapterFactory.create() - } - init { mainSettings.observeLocalServer() .subscribe { onLocalServerSettingsChanged() } diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/RetrofitWrapper.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/RetrofitWrapper.kt deleted file mode 100644 index ab254d570..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/RetrofitWrapper.kt +++ /dev/null @@ -1,40 +0,0 @@ -package dev.ragnarok.filegallery.api - -import retrofit2.Retrofit -import java.util.* - -class RetrofitWrapper private constructor( - private val retrofit: Retrofit, - private val withCaching: Boolean -) { - private val servicesCache: MutableMap, Any?>? = - if (withCaching) Collections.synchronizedMap(HashMap(4)) else null - - @Suppress("UNCHECKED_CAST") - fun create(serviceClass: Class): T { - if (!withCaching || servicesCache == null) { - return retrofit.create(serviceClass) - } - if (servicesCache.containsKey(serviceClass)) { - return servicesCache[serviceClass] as T - } - val service = retrofit.create(serviceClass) - servicesCache[serviceClass] = service - return service - } - - fun cleanup() { - servicesCache?.clear() - } - - companion object { - fun wrap(retrofit: Retrofit): RetrofitWrapper { - return RetrofitWrapper(retrofit, true) - } - - fun wrap(retrofit: Retrofit, withCaching: Boolean): RetrofitWrapper { - return RetrofitWrapper(retrofit, withCaching) - } - } - -} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/impl/LocalServerApi.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/impl/LocalServerApi.kt index 6cf5dc7ec..ce8e959e6 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/impl/LocalServerApi.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/impl/LocalServerApi.kt @@ -222,7 +222,7 @@ internal class LocalServerApi(private val service: ILocalServerServiceProvider) } override fun remotePlayAudioRx( - server: String?, + server: String, filename: String?, `is`: InputStream, listener: PercentagePublisher? diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/impl/Networker.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/impl/Networker.kt index d0fa2e8b8..827c88cad 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/impl/Networker.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/impl/Networker.kt @@ -1,9 +1,8 @@ package dev.ragnarok.filegallery.api.impl import dev.ragnarok.filegallery.api.ILocalServerServiceProvider -import dev.ragnarok.filegallery.api.IOtherRetrofitProvider -import dev.ragnarok.filegallery.api.OtherRetrofitProvider -import dev.ragnarok.filegallery.api.RetrofitWrapper +import dev.ragnarok.filegallery.api.IOtherRestProvider +import dev.ragnarok.filegallery.api.OtherRestProvider import dev.ragnarok.filegallery.api.interfaces.ILocalServerApi import dev.ragnarok.filegallery.api.interfaces.INetworker import dev.ragnarok.filegallery.api.services.ILocalServerService @@ -11,21 +10,21 @@ import dev.ragnarok.filegallery.settings.ISettings.IMainSettings import io.reactivex.rxjava3.core.Single class Networker(settings: IMainSettings) : INetworker { - private val otherVkRetrofitProvider: IOtherRetrofitProvider + private val otherVkRestProvider: IOtherRestProvider override fun localServerApi(): ILocalServerApi { return LocalServerApi(object : ILocalServerServiceProvider { override fun provideLocalServerService(): Single { - return otherVkRetrofitProvider.provideLocalServerRetrofit() - .map { wrapper: RetrofitWrapper -> - wrapper.create( - ILocalServerService::class.java - ) + return otherVkRestProvider.provideLocalServerRest() + .map { + val ret = ILocalServerService() + ret.addon(it) + ret } } }) } init { - otherVkRetrofitProvider = OtherRetrofitProvider(settings) + otherVkRestProvider = OtherRestProvider(settings) } } \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/interfaces/ILocalServerApi.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/interfaces/ILocalServerApi.kt index 07c9cf6dd..8fa008a74 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/interfaces/ILocalServerApi.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/interfaces/ILocalServerApi.kt @@ -75,7 +75,7 @@ interface ILocalServerApi { @CheckResult fun remotePlayAudioRx( - server: String?, + server: String, filename: String?, `is`: InputStream, listener: PercentagePublisher? diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/HttpException.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/HttpException.kt new file mode 100644 index 000000000..4bc002ff9 --- /dev/null +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/HttpException.kt @@ -0,0 +1,13 @@ +package dev.ragnarok.filegallery.api.rest + +class HttpException(val code: Int) : RuntimeException( + getMessage( + code + ) +) { + companion object { + private fun getMessage(code: Int): String { + return "HTTP $code" + } + } +} diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/IServiceRest.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/IServiceRest.kt new file mode 100644 index 000000000..30d72b633 --- /dev/null +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/IServiceRest.kt @@ -0,0 +1,77 @@ +package dev.ragnarok.filegallery.api.rest + +import dev.ragnarok.filegallery.api.model.Items +import dev.ragnarok.filegallery.api.model.response.BaseResponse +import dev.ragnarok.filegallery.kJson +import kotlinx.serialization.KSerializer +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.serializer +import okhttp3.FormBody +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import java.lang.ref.WeakReference + +abstract class IServiceRest { + private var restClient: WeakReference? = null + + val rest: SimplePostHttp + get() = restClient?.get() ?: throw HttpException(-1) + + fun addon(client: SimplePostHttp?) { + restClient = WeakReference(client) + } + + companion object { + val baseInt: KSerializer> + get() = BaseResponse.serializer(Int.serializer()) + + val baseLong: KSerializer> + get() = BaseResponse.serializer(Long.serializer()) + + val baseString: KSerializer> + get() = BaseResponse.serializer(String.serializer()) + + inline fun base(serial: KSerializer): KSerializer> { + return BaseResponse.serializer(serial) + } + + inline fun baseList(serial: KSerializer): KSerializer>> { + return BaseResponse.serializer(ListSerializer(serial)) + } + + inline fun items(serial: KSerializer): KSerializer>> { + return BaseResponse.serializer(Items.serializer(serial)) + } + + private fun toSerialStr(obj: Any?): String? { + return when (obj) { + is String -> { + obj + } + is Byte, is Short, is Int, is Long, is Float, is Double -> { + obj.toString() + } + is Boolean -> { + if (obj) "1" else "0" + } + else -> null + } + } + + inline fun jsonForm(obj: T, serial: KSerializer): RequestBody { + return kJson.encodeToString(serial, obj) + .toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) + } + + fun form(vararg pairs: Pair): FormBody { + val formBuilder = FormBody.Builder() + for ((first, second) in pairs) { + toSerialStr(second)?.let { + formBuilder.add(first, it) + } + } + return formBuilder.build() + } + } +} diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/SimplePostHttp.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/SimplePostHttp.kt new file mode 100644 index 000000000..db03f7637 --- /dev/null +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/rest/SimplePostHttp.kt @@ -0,0 +1,107 @@ +package dev.ragnarok.filegallery.api.rest + +import dev.ragnarok.filegallery.ifNonNull +import dev.ragnarok.filegallery.isMsgPack +import dev.ragnarok.filegallery.kJson +import dev.ragnarok.filegallery.util.serializeble.json.decodeFromStream +import dev.ragnarok.filegallery.util.serializeble.msgpack.MsgPack +import io.reactivex.rxjava3.core.Single +import kotlinx.serialization.KSerializer +import okhttp3.MultipartBody +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody + +class SimplePostHttp( + private val baseUrl: String?, + okHttpClient: OkHttpClient.Builder +) { + private val client = okHttpClient.build() + + fun stop() { + client.dispatcher.cancelAll() + } + + fun requestFullUrl( + url: String, + body: RequestBody?, + serial: KSerializer, + onlySuccessful: Boolean = true + ): Single { + return requestInternal( + url, + body, + serial, onlySuccessful + ) + } + + fun request( + methodOrFullUrl: String, + body: RequestBody?, + serial: KSerializer, + onlySuccessful: Boolean = true + ): Single { + return requestInternal( + if (baseUrl.isNullOrEmpty()) methodOrFullUrl else "$baseUrl/$methodOrFullUrl", + body, + serial, onlySuccessful + ) + } + + private fun requestInternal( + url: String, + body: RequestBody?, + serial: KSerializer, + onlySuccessful: Boolean + ): Single { + return Single.create { emitter -> + val request = Request.Builder() + .url( + url + ) + body.ifNonNull( + { request.post(it) }, { request.get() } + ) + val call = client.newCall(request.build()) + emitter.setCancellable { call.cancel() } + try { + val response = call.execute() + if (!response.isSuccessful && onlySuccessful) { + emitter.tryOnError(HttpException(response.code)) + } else { + val ret = if (response.body.isMsgPack()) MsgPack().decodeFromOkioStream( + serial, response.body.source() + ) else kJson.decodeFromStream( + serial, response.body.byteStream() + ) + emitter.onSuccess( + ret + ) + } + response.close() + } catch (e: Exception) { + emitter.tryOnError(e) + } + } + } + + fun doMultipartForm( + methodOrFullUrl: String, + part: MultipartBody.Part, + serial: KSerializer, onlySuccessful: Boolean = true + ): Single { + val requestBodyMultipart: RequestBody = + MultipartBody.Builder().setType(MultipartBody.FORM).addPart(part).build() + return request(methodOrFullUrl, requestBodyMultipart, serial, onlySuccessful) + } + + fun doMultipartFormFullUrl( + url: String, + part: MultipartBody.Part, + serial: KSerializer, onlySuccessful: Boolean = true + ): Single { + val requestBodyMultipart: RequestBody = + MultipartBody.Builder().setType(MultipartBody.FORM).addPart(part).build() + return requestFullUrl(url, requestBodyMultipart, serial, onlySuccessful) + } +} diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/services/ILocalServerService.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/services/ILocalServerService.kt index dba0e977e..b1219320e 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/services/ILocalServerService.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/api/services/ILocalServerService.kt @@ -2,118 +2,175 @@ package dev.ragnarok.filegallery.api.services import dev.ragnarok.filegallery.api.model.Items import dev.ragnarok.filegallery.api.model.response.BaseResponse +import dev.ragnarok.filegallery.api.rest.IServiceRest import dev.ragnarok.filegallery.model.Audio import dev.ragnarok.filegallery.model.FileRemote import dev.ragnarok.filegallery.model.Photo import dev.ragnarok.filegallery.model.Video import io.reactivex.rxjava3.core.Single import okhttp3.MultipartBody -import retrofit2.http.* -interface ILocalServerService { - @FormUrlEncoded - @POST("audio.get") +class ILocalServerService : IServiceRest() { fun getAudios( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "audio.get", form( + "offset" to offset, + "count" to count, + "reverse" to reverse + ), items(Audio.serializer()) + ) + } - @FormUrlEncoded - @POST("discography.get") fun getDiscography( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "discography.get", form( + "offset" to offset, + "count" to count, + "reverse" to reverse + ), items(Audio.serializer()) + ) + } - @FormUrlEncoded - @POST("photos.get") fun getPhotos( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "photos.get", form( + "offset" to offset, + "count" to count, + "reverse" to reverse + ), items(Photo.serializer()) + ) + } - @FormUrlEncoded - @POST("video.get") fun getVideos( - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "video.get", form( + "offset" to offset, + "count" to count, + "reverse" to reverse + ), items(Video.serializer()) + ) + } - @FormUrlEncoded - @POST("audio.search") fun searchAudios( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> - - @FormUrlEncoded - @POST("discography.search") + query: String?, + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "audio.search", form( + "q" to query, + "offset" to offset, + "count" to count, + "reverse" to reverse + ), items(Audio.serializer()) + ) + } + fun searchDiscography( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> - - @FormUrlEncoded - @POST("video.search") + query: String?, + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "discography.search", form( + "q" to query, + "offset" to offset, + "count" to count, + "reverse" to reverse + ), items(Audio.serializer()) + ) + } + fun searchVideos( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> - - @FormUrlEncoded - @POST("photos.search") + query: String?, + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "video.search", form( + "q" to query, + "offset" to offset, + "count" to count, + "reverse" to reverse + ), items(Video.serializer()) + ) + } + fun searchPhotos( - @Field("q") query: String?, - @Field("offset") offset: Int?, - @Field("count") count: Int?, - @Field("reverse") reverse: Int? - ): Single>> - - @FormUrlEncoded - @POST("update_time") - fun update_time(@Field("hash") hash: String?): Single> - - @FormUrlEncoded - @POST("delete_media") - fun delete_media(@Field("hash") hash: String?): Single> - - @FormUrlEncoded - @POST("get_file_name") - fun get_file_name(@Field("hash") hash: String?): Single> - - @FormUrlEncoded - @POST("update_file_name") + query: String?, + offset: Int?, + count: Int?, + reverse: Int? + ): Single>> { + return rest.request( + "photos.search", form( + "q" to query, + "offset" to offset, + "count" to count, + "reverse" to reverse + ), items(Photo.serializer()) + ) + } + + fun update_time(hash: String?): Single> { + return rest.request("update_time", form("hash" to hash), baseInt) + } + + fun delete_media(hash: String?): Single> { + return rest.request("delete_media", form("hash" to hash), baseInt) + } + + fun get_file_name(hash: String?): Single> { + return rest.request("get_file_name", form("hash" to hash), baseString) + } + fun update_file_name( - @Field("hash") hash: String?, - @Field("name") name: String? - ): Single> + hash: String?, + name: String? + ): Single> { + return rest.request( + "update_file_name", form( + "hash" to hash, + "name" to name + ), baseInt + ) + } - @FormUrlEncoded - @POST("fs.get") fun fsGet( - @Field("dir") dir: String? - ): Single>> + dir: String? + ): Single>> { + return rest.request("fs.get", form("dir" to dir), items(FileRemote.serializer())) + } - @FormUrlEncoded - @POST("rebootPC") fun rebootPC( - @Field("type") type: String? - ): Single> + type: String? + ): Single> { + return rest.request("rebootPC", form("type" to type), baseInt) + } - @Multipart - @POST fun remotePlayAudioRx( - @Url server: String?, - @Part file: MultipartBody.Part - ): Single> + server: String, + file: MultipartBody.Part + ): Single> { + return rest.doMultipartFormFullUrl(server, file, baseInt) + } } \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/db/impl/SearchRequestHelperStorage.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/db/impl/SearchRequestHelperStorage.kt index 94019ee16..20c4dff97 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/db/impl/SearchRequestHelperStorage.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/db/impl/SearchRequestHelperStorage.kt @@ -215,7 +215,7 @@ class SearchRequestHelperStorage internal constructor(context: Context) : override fun insertTagOwner(name: String?): Single { return Single.create { emitter: SingleEmitter -> if (name.trimmedIsNullOrEmpty()) { - emitter.onError(Throwable("require name not null!!!")) + emitter.tryOnError(Throwable("require name not null!!!")) } else { val nameClean = name.trim { it <= ' ' } val db = helper.writableDatabase @@ -241,7 +241,7 @@ class SearchRequestHelperStorage internal constructor(context: Context) : db.setTransactionSuccessful() } db.endTransaction() - emitter.onError(Throwable("require name not equals!!!")) + emitter.tryOnError(Throwable("require name not equals!!!")) return@create } val cv = ContentValues() @@ -259,7 +259,7 @@ class SearchRequestHelperStorage internal constructor(context: Context) : override fun updateNameTagOwner(id: Int, name: String?): Completable { return Completable.create { iti -> if (name.trimmedIsNullOrEmpty()) { - iti.onError(Throwable("require name not null!!!")) + iti.tryOnError(Throwable("require name not null!!!")) } else { val nameClean = name.trim { it <= ' ' } val db = helper.writableDatabase diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/filemanager/FileManagerAdapter.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/filemanager/FileManagerAdapter.kt index d4fd290da..d19435899 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/filemanager/FileManagerAdapter.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/filemanager/FileManagerAdapter.kt @@ -189,10 +189,10 @@ class FileManagerAdapter(private var context: Context, private var data: List File(it1).delete() } == true) { it.onComplete() } else { - it.onError(Throwable("Can't Delete File")) + it.tryOnError(Throwable("Can't Delete File")) } } else { item.file_path?.let { it1 -> deleteRecursive(it1) } if (item.file_path?.let { it1 -> File(it1).delete() } == true) { it.onComplete() } else { - it.onError(Throwable("Can't Delete Folder")) + it.tryOnError(Throwable("Can't Delete Folder")) } } }.fromIOToMain() diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/localserver/audioslocalserver/AudioLocalServerRecyclerAdapter.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/localserver/audioslocalserver/AudioLocalServerRecyclerAdapter.kt index 23fa7f0ba..34b1e34ad 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/localserver/audioslocalserver/AudioLocalServerRecyclerAdapter.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/localserver/audioslocalserver/AudioLocalServerRecyclerAdapter.kt @@ -73,10 +73,10 @@ class AudioLocalServerRecyclerAdapter( if (bitrate != null) { v.onSuccess((bitrate.toLong() / 1000).toInt()) } else { - v.onError(Throwable("Can't receipt bitrate ")) + v.tryOnError(Throwable("Can't receipt bitrate ")) } } catch (e: RuntimeException) { - v.onError(e) + v.tryOnError(e) } } } diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/ErrorLocalizer.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/ErrorLocalizer.kt index 1b56b599d..f95ba997a 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/ErrorLocalizer.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/ErrorLocalizer.kt @@ -2,6 +2,7 @@ package dev.ragnarok.filegallery.util import android.content.Context import dev.ragnarok.filegallery.R +import dev.ragnarok.filegallery.api.rest.HttpException import dev.ragnarok.filegallery.nonNullNoEmpty import java.net.SocketTimeoutException import java.net.UnknownHostException @@ -9,12 +10,21 @@ import java.net.UnknownHostException object ErrorLocalizer { fun localizeThrowable(context: Context, throwable: Throwable?): String { throwable ?: return "null" - if (throwable is SocketTimeoutException) { - return context.getString(R.string.error_timeout_message) + return when (throwable) { + is SocketTimeoutException -> { + context.getString(R.string.error_timeout_message) + } + is UnknownHostException -> { + context.getString(R.string.error_unknown_host) + } + is HttpException -> { + if (throwable.code < 0) { + context.getString(R.string.client_rest_shutdown) + } else { + context.getString(R.string.internal_server_error, throwable.code) + } + } + else -> if (throwable.message.nonNullNoEmpty()) throwable.message!! else throwable.toString() } - if (throwable is UnknownHostException) { - return context.getString(R.string.error_unknown_host) - } - return if (throwable.message.nonNullNoEmpty()) throwable.message!! else throwable.toString() } } \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/DeserializationStrategyConverter.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/DeserializationStrategyConverter.kt deleted file mode 100644 index 994154f82..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/DeserializationStrategyConverter.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.kotlinx.serialization - -import kotlinx.serialization.DeserializationStrategy -import okhttp3.ResponseBody -import retrofit2.Converter - -internal class DeserializationStrategyConverter( - private val loader: DeserializationStrategy, - private val serializer: Serializer -) : Converter { - override fun convert(value: ResponseBody): T { - return serializer.fromResponseBody(loader, value) - } -} diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/Factory.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/Factory.kt deleted file mode 100644 index 50f79747e..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/Factory.kt +++ /dev/null @@ -1,100 +0,0 @@ -@file:JvmName("KotlinSerializationConverterFactory") - -package dev.ragnarok.filegallery.util.serializeble.retrofit.kotlinx.serialization - -import dev.ragnarok.filegallery.util.serializeble.json.Json -import dev.ragnarok.filegallery.util.serializeble.msgpack.MsgPack -import dev.ragnarok.filegallery.util.serializeble.retrofit.kotlinx.serialization.Serializer.FromBytes -import dev.ragnarok.filegallery.util.serializeble.retrofit.kotlinx.serialization.Serializer.FromString -import kotlinx.serialization.BinaryFormat -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.StringFormat -import okhttp3.MediaType -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.RequestBody -import okhttp3.ResponseBody -import retrofit2.Converter -import retrofit2.Retrofit -import java.lang.reflect.Type - -@ExperimentalSerializationApi -internal class Factory( - private val contentType: MediaType, - private val serializer: Serializer -) : Converter.Factory() { - @Suppress("RedundantNullableReturnType") // Retaining interface contract. - override fun responseBodyConverter( - type: Type, - annotations: Array, - retrofit: Retrofit - ): Converter? { - val loader = serializer.serializer(type) - return DeserializationStrategyConverter(loader, serializer) - } - - @Suppress("RedundantNullableReturnType") // Retaining interface contract. - override fun requestBodyConverter( - type: Type, - parameterAnnotations: Array, - methodAnnotations: Array, - retrofit: Retrofit - ): Converter<*, RequestBody>? { - val saver = serializer.serializer(type) - return SerializationStrategyConverter(contentType, saver, serializer) - } -} - -/** - * Return a [Converter.Factory] which uses Kotlin serialization for string-based payloads. - * - * Because Kotlin serialization is so flexible in the types it supports, this converter assumes - * that it can handle all types. If you are mixing this with something else, you must add this - * instance last to allow the other converters a chance to see their types. - */ -@ExperimentalSerializationApi -@JvmName("create") -fun StringFormat.asConverterFactory(contentType: MediaType): Converter.Factory { - return Factory(contentType, FromString(this)) -} - -@ExperimentalSerializationApi -@JvmName("create") -fun StringFormat.asConverterFactory(): Converter.Factory { - return Factory("application/json; charset=UTF-8".toMediaType(), FromString(this)) -} - -@ExperimentalSerializationApi -@JvmName("create") -fun Json.asConverterFactory(): Converter.Factory { - return Factory("application/json; charset=UTF-8".toMediaType(), Serializer.FromJson(this)) -} - -@ExperimentalSerializationApi -fun jsonMsgPackConverterFactory(json: Json, msgPack: MsgPack): Converter.Factory { - return Factory( - "application/json; charset=UTF-8".toMediaType(), - Serializer.FromJsonMsgPack(json, msgPack) - ) -} - -@ExperimentalSerializationApi -@JvmName("create") -fun MsgPack.asConverterFactory(): Converter.Factory { - return Factory( - "application/x-msgpack; charset=utf-8".toMediaType(), - Serializer.FromMsgPack(this) - ) -} - -/** - * Return a [Converter.Factory] which uses Kotlin serialization for byte-based payloads. - * - * Because Kotlin serialization is so flexible in the types it supports, this converter assumes - * that it can handle all types. If you are mixing this with something else, you must add this - * instance last to allow the other converters a chance to see their types. - */ -@ExperimentalSerializationApi -@JvmName("create") -fun BinaryFormat.asConverterFactory(contentType: MediaType): Converter.Factory { - return Factory(contentType, FromBytes(this)) -} diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/SerializationStrategyConverter.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/SerializationStrategyConverter.kt deleted file mode 100644 index 2d1a9191b..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/SerializationStrategyConverter.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.kotlinx.serialization - -import kotlinx.serialization.SerializationStrategy -import okhttp3.MediaType -import okhttp3.RequestBody -import retrofit2.Converter - -internal class SerializationStrategyConverter( - private val contentType: MediaType, - private val saver: SerializationStrategy, - private val serializer: Serializer -) : Converter { - override fun convert(value: T) = serializer.toRequestBody(contentType, saver, value) -} diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/Serializer.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/Serializer.kt deleted file mode 100644 index e6d3bc0a5..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/kotlinx/serialization/Serializer.kt +++ /dev/null @@ -1,122 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.kotlinx.serialization - -import dev.ragnarok.filegallery.isMsgPack -import dev.ragnarok.filegallery.util.serializeble.json.Json -import dev.ragnarok.filegallery.util.serializeble.json.internal.JavaStreamSerialReader -import dev.ragnarok.filegallery.util.serializeble.json.internal.decodeByReader -import dev.ragnarok.filegallery.util.serializeble.msgpack.MsgPack -import kotlinx.serialization.* -import okhttp3.MediaType -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.ResponseBody -import java.lang.reflect.Type - -sealed class Serializer { - abstract fun fromResponseBody(loader: DeserializationStrategy, body: ResponseBody): T - abstract fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody - - protected abstract val format: SerialFormat - - @ExperimentalSerializationApi // serializer(Type) is not stable. - fun serializer(type: Type): KSerializer = format.serializersModule.serializer(type) - - class FromString(override val format: StringFormat) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - val string = body.string() - return format.decodeFromString(loader, string) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val string = format.encodeToString(saver, value) - return string.toRequestBody(contentType) - } - } - - class FromJsonMsgPack(override val format: Json, private val msgPack: MsgPack) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - if (body.isMsgPack()) { - return msgPack.decodeFromOkioStream(loader, body.source()) - } - return format.decodeByReader(loader, JavaStreamSerialReader(body.byteStream())) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val string = format.encodeToString(saver, value) - return string.toRequestBody(contentType) - } - } - - class FromJson(override val format: Json) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - return format.decodeByReader(loader, JavaStreamSerialReader(body.byteStream())) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val string = format.encodeToString(saver, value) - return string.toRequestBody(contentType) - } - } - - class FromMsgPack(override val format: MsgPack) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - return format.decodeFromOkioStream(loader, body.source()) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val bytes = format.encodeToByteArray(saver, value) - return bytes.toRequestBody(contentType, 0, bytes.size) - } - } - - class FromBytes(override val format: BinaryFormat) : Serializer() { - override fun fromResponseBody( - loader: DeserializationStrategy, - body: ResponseBody - ): T { - val bytes = body.bytes() - return format.decodeFromByteArray(loader, bytes) - } - - override fun toRequestBody( - contentType: MediaType, - saver: SerializationStrategy, - value: T - ): RequestBody { - val bytes = format.encodeToByteArray(saver, value) - return bytes.toRequestBody(contentType, 0, bytes.size) - } - } -} diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/BodyObservable.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/BodyObservable.kt deleted file mode 100644 index c56f23d4c..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/BodyObservable.kt +++ /dev/null @@ -1,65 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Observer -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.exceptions.CompositeException -import io.reactivex.rxjava3.exceptions.Exceptions -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.HttpException -import retrofit2.Response - -internal class BodyObservable(private val upstream: Observable>) : - Observable() { - override fun subscribeActual(observer: Observer) { - upstream.subscribe(BodyObserver(observer)) - } - - private class BodyObserver(observer: Observer) : - Observer> { - private val observer: Observer - private var terminated = false - override fun onSubscribe(disposable: Disposable) { - observer.onSubscribe(disposable) - } - - override fun onNext(response: Response) { - val body = response.body() - if (response.isSuccessful && body != null) { - observer.onNext(body) - } else { - terminated = true - val t: Throwable = HttpException(response) - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - } - } - - override fun onComplete() { - if (!terminated) { - observer.onComplete() - } - } - - override fun onError(throwable: Throwable) { - if (!terminated) { - observer.onError(throwable) - } else { - // This should never happen! onNext handles and forwards errors automatically. - val broken: Throwable = AssertionError( - "This should never happen! Report as a bug with the full stacktrace." - ) - broken.initCause(throwable) - RxJavaPlugins.onError(broken) - } - } - - init { - this.observer = observer - } - } -} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/CallEnqueueObservable.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/CallEnqueueObservable.kt deleted file mode 100644 index c76ff48e1..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/CallEnqueueObservable.kt +++ /dev/null @@ -1,75 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Observer -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.exceptions.CompositeException -import io.reactivex.rxjava3.exceptions.Exceptions -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response - -internal class CallEnqueueObservable(private val originalCall: Call) : - Observable>() { - override fun subscribeActual(observer: Observer>) { - // Since Call is a one-shot type, clone it for each new observer. - val call = originalCall.clone() - val callback = CallCallback(call, observer) - observer.onSubscribe(callback) - if (!callback.isDisposed) { - call.enqueue(callback) - } - } - - private class CallCallback( - private val call: Call<*>, - private val observer: Observer> - ) : Disposable, Callback { - var terminated = false - - @Volatile - private var disposed = false - override fun onResponse(call: Call, response: Response) { - if (disposed) return - try { - observer.onNext(response) - if (!disposed) { - terminated = true - observer.onComplete() - } - } catch (t: Throwable) { - Exceptions.throwIfFatal(t) - if (terminated) { - RxJavaPlugins.onError(t) - } else if (!disposed) { - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - } - } - } - - override fun onFailure(call: Call, t: Throwable) { - if (call.isCanceled) return - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - } - - override fun dispose() { - disposed = true - call.cancel() - } - - override fun isDisposed(): Boolean { - return disposed - } - } -} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/CallExecuteObservable.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/CallExecuteObservable.kt deleted file mode 100644 index c16a6b5c9..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/CallExecuteObservable.kt +++ /dev/null @@ -1,59 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Observer -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.exceptions.CompositeException -import io.reactivex.rxjava3.exceptions.Exceptions -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.Call -import retrofit2.Response - -internal class CallExecuteObservable(private val originalCall: Call) : - Observable>() { - override fun subscribeActual(observer: Observer>) { - // Since Call is a one-shot type, clone it for each new observer. - val call = originalCall.clone() - val disposable = CallDisposable(call) - observer.onSubscribe(disposable) - if (disposable.isDisposed) { - return - } - var terminated = false - try { - val response = call.execute() - if (!disposable.isDisposed) { - observer.onNext(response) - } - if (!disposable.isDisposed) { - terminated = true - observer.onComplete() - } - } catch (t: Throwable) { - Exceptions.throwIfFatal(t) - if (terminated) { - RxJavaPlugins.onError(t) - } else if (!disposable.isDisposed) { - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - } - } - } - - private class CallDisposable(private val call: Call<*>) : Disposable { - @Volatile - private var disposed = false - override fun dispose() { - disposed = true - call.cancel() - } - - override fun isDisposed(): Boolean { - return disposed - } - } -} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/Result.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/Result.kt deleted file mode 100644 index c6b7331e2..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/Result.kt +++ /dev/null @@ -1,52 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.rxjava3 - -import retrofit2.Response - -/** - * The result of executing an HTTP request. - */ -class Result private constructor( - private val response: Response?, - private val error: Throwable? -) { - /** - * The response received from executing an HTTP request. Only present when [.isError] is - * false, null otherwise. - */ - fun response(): Response? { - return response - } - - /** - * The error experienced while attempting to execute an HTTP request. Only present when [ ][.isError] is true, null otherwise. - * - * - * If the error is an [Throwable] then there was a problem with the transport to the - * remote server. Any other exception type indicates an unexpected failure and should be - * considered fatal (configuration error, programming error, etc.). - */ - fun error(): Throwable? { - return error - } - - /** - * `true` if the request resulted in an error. See [.error] for the cause. - */ - fun isError(): Boolean { - return error != null - } - - companion object { - // Guarding public API nullability. - fun error(error: Throwable?): Result { - if (error == null) throw NullPointerException("error == null") - return Result(null, error) - } - - // Guarding public API nullability. - fun response(response: Response?): Result { - if (response == null) throw NullPointerException("response == null") - return Result(response, null) - } - } -} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/ResultObservable.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/ResultObservable.kt deleted file mode 100644 index 8cd531c96..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/ResultObservable.kt +++ /dev/null @@ -1,46 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Observer -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.exceptions.CompositeException -import io.reactivex.rxjava3.exceptions.Exceptions -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.Response - -internal class ResultObservable(private val upstream: Observable>) : - Observable>() { - override fun subscribeActual(observer: Observer>) { - upstream.subscribe(ResultObserver(observer)) - } - - private class ResultObserver(private val observer: Observer>) : - Observer> { - override fun onSubscribe(disposable: Disposable) { - observer.onSubscribe(disposable) - } - - override fun onNext(response: Response) { - observer.onNext(Result.response(response)) - } - - override fun onError(throwable: Throwable) { - try { - observer.onNext(Result.error(throwable)) - } catch (t: Throwable) { - try { - observer.onError(t) - } catch (inner: Throwable) { - Exceptions.throwIfFatal(inner) - RxJavaPlugins.onError(CompositeException(t, inner)) - } - return - } - observer.onComplete() - } - - override fun onComplete() { - observer.onComplete() - } - } -} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/RxJava3CallAdapter.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/RxJava3CallAdapter.kt deleted file mode 100644 index 1b0b9e442..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/RxJava3CallAdapter.kt +++ /dev/null @@ -1,55 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.BackpressureStrategy -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.core.Scheduler -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import retrofit2.Call -import retrofit2.CallAdapter -import java.lang.reflect.Type - -internal class RxJava3CallAdapter( - private val responseType: Type, - private val scheduler: Scheduler?, - private val isAsync: Boolean, - private val isResult: Boolean, - private val isBody: Boolean, - private val isFlowable: Boolean, - private val isSingle: Boolean, - private val isMaybe: Boolean, - private val isCompletable: Boolean -) : CallAdapter { - override fun responseType(): Type { - return responseType - } - - override fun adapt(call: Call): Any { - val responseObservable = - if (isAsync) CallEnqueueObservable(call) else CallExecuteObservable(call) - var observable: Observable<*> - observable = if (isResult) { - ResultObservable(responseObservable) - } else if (isBody) { - BodyObservable(responseObservable) - } else { - responseObservable - } - if (scheduler != null) { - observable = observable.subscribeOn(scheduler) - } - if (isFlowable) { - // We only ever deliver a single value, and the RS spec states that you MUST request at least - // one element which means we never need to honor backpressure. - return observable.toFlowable(BackpressureStrategy.MISSING) - } - if (isSingle) { - return observable.singleOrError() - } - if (isMaybe) { - return observable.singleElement() - } - return if (isCompletable) { - observable.ignoreElements() - } else RxJavaPlugins.onAssembly(observable) - } -} \ No newline at end of file diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/RxJava3CallAdapterFactory.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/RxJava3CallAdapterFactory.kt deleted file mode 100644 index ecfca1ca6..000000000 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/retrofit/rxjava3/RxJava3CallAdapterFactory.kt +++ /dev/null @@ -1,103 +0,0 @@ -package dev.ragnarok.filegallery.util.serializeble.retrofit.rxjava3 - -import io.reactivex.rxjava3.core.* -import retrofit2.CallAdapter -import retrofit2.Response -import retrofit2.Retrofit -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type - -class RxJava3CallAdapterFactory private constructor( - private val scheduler: Scheduler?, - private val isAsync: Boolean -) : CallAdapter.Factory() { - override fun get( - returnType: Type, annotations: Array, retrofit: Retrofit - ): CallAdapter<*, *>? { - val rawType = getRawType(returnType) - if (rawType == Completable::class.java) { - // Completable is not parameterized (which is what the rest of this method deals with) so it - // can only be created with a single configuration. - return RxJava3CallAdapter( - Void::class.java, scheduler, isAsync, - isResult = false, - isBody = true, - isFlowable = false, - isSingle = false, - isMaybe = false, - isCompletable = true - ) - } - val isFlowable = rawType == Flowable::class.java - val isSingle = rawType == Single::class.java - val isMaybe = rawType == Maybe::class.java - if (rawType != Observable::class.java && !isFlowable && !isSingle && !isMaybe) { - return null - } - var isResult = false - var isBody = false - val responseType: Type - if (returnType !is ParameterizedType) { - val name = - if (isFlowable) "Flowable" else if (isSingle) "Single" else if (isMaybe) "Maybe" else "Observable" - throw IllegalStateException( - name - + " return type must be parameterized" - + " as " - + name - + " or " - + name - + "" - ) - } - val observableType = getParameterUpperBound(0, returnType) - when (getRawType(observableType)) { - Response::class.java -> { - check(observableType is ParameterizedType) { "Response must be parameterized" + " as Response or Response" } - responseType = getParameterUpperBound(0, observableType) - } - Result::class.java -> { - check(observableType is ParameterizedType) { "Result must be parameterized" + " as Result or Result" } - responseType = getParameterUpperBound(0, observableType) - isResult = true - } - else -> { - responseType = observableType - isBody = true - } - } - return RxJava3CallAdapter( - responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false - ) - } - - companion object { - /** - * Returns an instance which creates asynchronous observables that run on a background thread by - * default. Applying `subscribeOn(..)` has no effect on instances created by the returned - * factory. - */ - fun create(): RxJava3CallAdapterFactory { - return RxJava3CallAdapterFactory(null, true) - } - - /** - * Returns an instance which creates synchronous observables that do not operate on any scheduler - * by default. Applying `subscribeOn(..)` will change the scheduler on which the HTTP calls - * are made. - */ - fun createSynchronous(): RxJava3CallAdapterFactory { - return RxJava3CallAdapterFactory(null, false) - } - - /** - * Returns an instance which creates synchronous observables that `subscribeOn(..)` the - * supplied `scheduler` by default. - */ - // Guarding public API nullability. - fun createWithScheduler(scheduler: Scheduler?): RxJava3CallAdapterFactory { - if (scheduler == null) throw NullPointerException("scheduler == null") - return RxJava3CallAdapterFactory(scheduler, false) - } - } -} \ No newline at end of file diff --git a/app_filegallery/src/main/res/values-be/strings.xml b/app_filegallery/src/main/res/values-be/strings.xml index 1aaa23d9c..5450477b1 100644 --- a/app_filegallery/src/main/res/values-be/strings.xml +++ b/app_filegallery/src/main/res/values-be/strings.xml @@ -250,4 +250,6 @@ Ліміт кэша карцінак у памяці Мова Сканаваць QR + Запыт скасаваны + Памылка сервера (HTTP: %1$d) diff --git a/app_filegallery/src/main/res/values-ru/strings.xml b/app_filegallery/src/main/res/values-ru/strings.xml index 1c7e312a2..fa0056737 100644 --- a/app_filegallery/src/main/res/values-ru/strings.xml +++ b/app_filegallery/src/main/res/values-ru/strings.xml @@ -250,4 +250,6 @@ Лимит кэша картинок в памяти Язык Сканировать QR + Запрос отменён + Ошибка сервера (HTTP: %1$d) diff --git a/app_filegallery/src/main/res/values/strings.xml b/app_filegallery/src/main/res/values/strings.xml index 42d1f8d94..b4140e1d8 100644 --- a/app_filegallery/src/main/res/values/strings.xml +++ b/app_filegallery/src/main/res/values/strings.xml @@ -377,4 +377,6 @@ In-Memory Image Cache Limit Language Scan QR + Request canceled + Server error (HTTP: %1$d) diff --git a/build.gradle b/build.gradle index 19188ca3b..4f4b60812 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ buildscript { ext.autoValueVersion = "1.10.1" //common libraries - ext.kotlin_version = "1.8.0-RC2" + ext.kotlin_version = "1.8.0" ext.kotlin_coroutines = "1.6.4" ext.kotlin_serializer = "1.4.1" ext.okhttpLibraryVersion = "5.0.0-alpha.11" diff --git a/compiled_native/libfenrir-release.aar b/compiled_native/libfenrir-release.aar index 77beafa12..05f632e03 100644 Binary files a/compiled_native/libfenrir-release.aar and b/compiled_native/libfenrir-release.aar differ diff --git a/libfenrir/src/main/java/com/github/luben/zstd/ZstdCompressCtx.java b/libfenrir/src/main/java/com/github/luben/zstd/ZstdCompressCtx.java index 3f718d689..a8490c96a 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/ZstdCompressCtx.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/ZstdCompressCtx.java @@ -201,6 +201,18 @@ private void ensureOpen() { } } + /** + * Tells how much data has been ingested (read from input), + * consumed (input actually compressed) and produced (output) for current frame. + */ + public ZstdFrameProgression getFrameProgression() { + ensureOpen(); + return getFrameProgression0(); + } + + private native ZstdFrameProgression getFrameProgression0(); + + /** * Clear all state and parameters from the compression context. This leaves the object in a * state identical to a newly created compression context. diff --git a/libfenrir/src/main/java/com/github/luben/zstd/ZstdDirectBufferCompressingStreamNoFinalizer.java b/libfenrir/src/main/java/com/github/luben/zstd/ZstdDirectBufferCompressingStreamNoFinalizer.java index a1d1fd671..67fa9ad9e 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/ZstdDirectBufferCompressingStreamNoFinalizer.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/ZstdDirectBufferCompressingStreamNoFinalizer.java @@ -7,8 +7,8 @@ public class ZstdDirectBufferCompressingStreamNoFinalizer implements Closeable, Flushable { private final long stream; - private final int consumed = 0; - private final int produced = 0; + private int consumed; + private int produced; private final int level; private ByteBuffer target; private boolean closed; diff --git a/libfenrir/src/main/java/com/github/luben/zstd/ZstdFrameProgression.java b/libfenrir/src/main/java/com/github/luben/zstd/ZstdFrameProgression.java new file mode 100644 index 000000000..4ea6d81ec --- /dev/null +++ b/libfenrir/src/main/java/com/github/luben/zstd/ZstdFrameProgression.java @@ -0,0 +1,65 @@ +package com.github.luben.zstd; + +public class ZstdFrameProgression { + + private final long ingested; + private final long consumed; + private final long produced; + private final long flushed; + private final int currentJobID; + private final int nbActiveWorkers; + + public ZstdFrameProgression(long ingested, long consumed, long produced, long flushed, int currentJobID, + int nbActiveWorkers) { + this.ingested = ingested; + this.consumed = consumed; + this.produced = produced; + this.flushed = flushed; + this.currentJobID = currentJobID; + this.nbActiveWorkers = nbActiveWorkers; + } + + /** + * The number of input bytes read and buffered. + */ + public long getIngested() { + return ingested; + } + + /** + * The number of input bytes actually compressed. + * Note: ingested - consumed = amount of input data buffered internally, not yet compressed. + */ + public long getConsumed() { + return consumed; + } + + /** + * The number of compressed bytes generated and buffered. + */ + public long getProduced() { + return produced; + } + + /** + * The number of compressed bytes flushed. + */ + public long getFlushed() { + return flushed; + } + + /** + * The last started job number. Only applicable if multi-threading is enabled. + */ + public int getCurrentJobID() { + return currentJobID; + } + + /** + * The number of workers actively compressing. Only applicable if multi-threading is enabled. + */ + public int getNbActiveWorkers() { + return nbActiveWorkers; + } + +} diff --git a/libfenrir/src/main/jni/compress/zstd/common/error_private.c b/libfenrir/src/main/jni/compress/zstd/common/error_private.c index fe73c5edd..fb4d70596 100644 --- a/libfenrir/src/main/jni/compress/zstd/common/error_private.c +++ b/libfenrir/src/main/jni/compress/zstd/common/error_private.c @@ -31,6 +31,7 @@ const char* ERR_getErrorString(ERR_enum code) case PREFIX(checksum_wrong): return "Restored data doesn't match checksum"; case PREFIX(literals_headerWrong): return "Header of Literals' block doesn't respect format specification"; case PREFIX(parameter_unsupported): return "Unsupported parameter"; + case PREFIX(parameter_combination_unsupported): return "Unsupported combination of parameters"; case PREFIX(parameter_outOfBound): return "Parameter is out of bound"; case PREFIX(init_missing): return "Context should be init first"; case PREFIX(memory_allocation): return "Allocation error : not enough memory"; @@ -51,6 +52,7 @@ const char* ERR_getErrorString(ERR_enum code) case PREFIX(seekableIO): return "An I/O error occurred when reading/seeking"; case PREFIX(dstBuffer_wrong): return "Destination buffer is wrong"; case PREFIX(srcBuffer_wrong): return "Source buffer is wrong"; + case PREFIX(externalMatchFinder_failed): return "External matchfinder returned an error code"; case PREFIX(maxCode): default: return notErrorCode; } diff --git a/libfenrir/src/main/jni/compress/zstd/compress/zstd_compress.c b/libfenrir/src/main/jni/compress/zstd/compress/zstd_compress.c index 4cf2c0945..759abe00a 100644 --- a/libfenrir/src/main/jni/compress/zstd/compress/zstd_compress.c +++ b/libfenrir/src/main/jni/compress/zstd/compress/zstd_compress.c @@ -278,6 +278,16 @@ static ZSTD_paramSwitch_e ZSTD_resolveEnableLdm(ZSTD_paramSwitch_e mode, return (cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 27) ? ZSTD_ps_enable : ZSTD_ps_disable; } +/* Enables validation for external sequences in debug builds. */ +static int ZSTD_resolveExternalSequenceValidation(int mode) { +#if defined(DEBUGLEVEL) && (DEBUGLEVEL>=2) + (void)mode; + return 1; +#else + return mode; +#endif +} + /* Returns 1 if compression parameters are such that CDict hashtable and chaintable indices are tagged. * If so, the tags need to be removed in ZSTD_resetCCtx_byCopyingCDict. */ static int ZSTD_CDictIndicesAreTagged(const ZSTD_compressionParameters* const cParams) { @@ -301,6 +311,7 @@ static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams( } cctxParams.useBlockSplitter = ZSTD_resolveBlockSplitterMode(cctxParams.useBlockSplitter, &cParams); cctxParams.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams.useRowMatchFinder, &cParams); + cctxParams.validateSequences = ZSTD_resolveExternalSequenceValidation(cctxParams.validateSequences); assert(!ZSTD_checkCParams(cParams)); return cctxParams; } @@ -346,10 +357,13 @@ size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) #define ZSTD_NO_CLEVEL 0 /** - * Initializes the cctxParams from params and compressionLevel. + * Initializes `cctxParams` from `params` and `compressionLevel`. * @param compressionLevel If params are derived from a compression level then that compression level, otherwise ZSTD_NO_CLEVEL. */ -static void ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, ZSTD_parameters const* params, int compressionLevel) +static void +ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, + const ZSTD_parameters* params, + int compressionLevel) { assert(!ZSTD_checkCParams(params->cParams)); ZSTD_memset(cctxParams, 0, sizeof(*cctxParams)); @@ -362,6 +376,7 @@ static void ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, ZSTD_par cctxParams->useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams->useRowMatchFinder, ¶ms->cParams); cctxParams->useBlockSplitter = ZSTD_resolveBlockSplitterMode(cctxParams->useBlockSplitter, ¶ms->cParams); cctxParams->ldmParams.enableLdm = ZSTD_resolveEnableLdm(cctxParams->ldmParams.enableLdm, ¶ms->cParams); + cctxParams->validateSequences = ZSTD_resolveExternalSequenceValidation(cctxParams->validateSequences); DEBUGLOG(4, "ZSTD_CCtxParams_init_internal: useRowMatchFinder=%d, useBlockSplitter=%d ldm=%d", cctxParams->useRowMatchFinder, cctxParams->useBlockSplitter, cctxParams->ldmParams.enableLdm); } @@ -584,6 +599,11 @@ ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param) bounds.upperBound = (int)ZSTD_ps_disable; return bounds; + case ZSTD_c_enableMatchFinderFallback: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + default: bounds.error = ERROR(parameter_unsupported); return bounds; @@ -649,6 +669,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) case ZSTD_c_useRowMatchFinder: case ZSTD_c_deterministicRefPrefix: case ZSTD_c_prefetchCDictTables: + case ZSTD_c_enableMatchFinderFallback: default: return 0; } @@ -661,7 +682,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value) if (ZSTD_isUpdateAuthorized(param)) { cctx->cParamsChanged = 1; } else { - RETURN_ERROR(stage_wrong, "can only set params in ctx init stage"); + RETURN_ERROR(stage_wrong, "can only set params in cctx init stage"); } } switch(param) @@ -705,6 +726,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value) case ZSTD_c_useRowMatchFinder: case ZSTD_c_deterministicRefPrefix: case ZSTD_c_prefetchCDictTables: + case ZSTD_c_enableMatchFinderFallback: break; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); @@ -796,14 +818,14 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, case ZSTD_c_forceAttachDict : { const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value; - BOUNDCHECK(ZSTD_c_forceAttachDict, pref); + BOUNDCHECK(ZSTD_c_forceAttachDict, (int)pref); CCtxParams->attachDictPref = pref; return CCtxParams->attachDictPref; } case ZSTD_c_literalCompressionMode : { const ZSTD_paramSwitch_e lcm = (ZSTD_paramSwitch_e)value; - BOUNDCHECK(ZSTD_c_literalCompressionMode, lcm); + BOUNDCHECK(ZSTD_c_literalCompressionMode, (int)lcm); CCtxParams->literalCompressionMode = lcm; return CCtxParams->literalCompressionMode; } @@ -937,6 +959,11 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, CCtxParams->prefetchCDictTables = (ZSTD_paramSwitch_e)value; return CCtxParams->prefetchCDictTables; + case ZSTD_c_enableMatchFinderFallback: + BOUNDCHECK(ZSTD_c_enableMatchFinderFallback, value); + CCtxParams->enableMatchFinderFallback = value; + return CCtxParams->enableMatchFinderFallback; + default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } } @@ -1072,6 +1099,9 @@ size_t ZSTD_CCtxParams_getParameter( case ZSTD_c_prefetchCDictTables: *value = (int)CCtxParams->prefetchCDictTables; break; + case ZSTD_c_enableMatchFinderFallback: + *value = CCtxParams->enableMatchFinderFallback; + break; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } return 0; @@ -1098,6 +1128,21 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( return 0; } +size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams) +{ + DEBUGLOG(4, "ZSTD_CCtx_setCParams"); + assert(cctx != NULL); + if (cctx->streamStage != zcss_init) { + /* All parameters in @cparams are allowed to be updated during MT compression. + * This must be signaled, so that MT compression picks up the changes */ + cctx->cParamsChanged = 1; + } + /* only update if parameters are valid */ + FORWARD_IF_ERROR(ZSTD_checkCParams(cparams), ""); + cctx->requestedParams.cParams = cparams; + return 0; +} + size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %llu bytes", pledgedSrcSize); @@ -1243,6 +1288,7 @@ size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset) RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "Can't reset parameters only when not in init stage."); ZSTD_clearAllDicts(cctx); + ZSTD_memset(&cctx->externalMatchCtx, 0, sizeof(cctx->externalMatchCtx)); return ZSTD_CCtxParams_reset(&cctx->requestedParams); } return 0; @@ -1485,6 +1531,13 @@ ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, return tableSpace + optSpace + slackSpace + lazyAdditionalSpace; } +/* Helper function for calculating memory requirements. + * Gives a tighter bound than ZSTD_sequenceBound() by taking minMatch into account. */ +static size_t ZSTD_maxNbSeq(size_t blockSize, unsigned minMatch, int useExternalMatchFinder) { + U32 const divider = (minMatch==3 || useExternalMatchFinder) ? 3 : 4; + return blockSize / divider; +} + static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( const ZSTD_compressionParameters* cParams, const ldmParams_t* ldmParams, @@ -1492,12 +1545,12 @@ static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( const ZSTD_paramSwitch_e useRowMatchFinder, const size_t buffInSize, const size_t buffOutSize, - const U64 pledgedSrcSize) + const U64 pledgedSrcSize, + int useExternalMatchFinder) { size_t const windowSize = (size_t) BOUNDED(1ULL, 1ULL << cParams->windowLog, pledgedSrcSize); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - U32 const divider = (cParams->minMatch==3) ? 3 : 4; - size_t const maxNbSeq = blockSize / divider; + size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, cParams->minMatch, useExternalMatchFinder); size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize) + ZSTD_cwksp_aligned_alloc_size(maxNbSeq * sizeof(seqDef)) + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE)); @@ -1516,6 +1569,11 @@ static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( size_t const cctxSpace = isStatic ? ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)) : 0; + size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize); + size_t const externalSeqSpace = useExternalMatchFinder + ? ZSTD_cwksp_alloc_size(maxNbExternalSeq * sizeof(ZSTD_Sequence)) + : 0; + size_t const neededSpace = cctxSpace + entropySpace + @@ -1524,7 +1582,8 @@ static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( ldmSeqSpace + matchStateSize + tokenSpace + - bufferSpace; + bufferSpace + + externalSeqSpace; DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace); return neededSpace; @@ -1542,7 +1601,7 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) * be needed. However, we still allocate two 0-sized buffers, which can * take space under ASAN. */ return ZSTD_estimateCCtxSize_usingCCtxParams_internal( - &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN); + &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN, params->useExternalMatchFinder); } size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams) @@ -1603,7 +1662,7 @@ size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params) return ZSTD_estimateCCtxSize_usingCCtxParams_internal( &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, inBuffSize, outBuffSize, - ZSTD_CONTENTSIZE_UNKNOWN); + ZSTD_CONTENTSIZE_UNKNOWN, params->useExternalMatchFinder); } } @@ -1886,8 +1945,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, { size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params->cParams.windowLog), pledgedSrcSize)); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - U32 const divider = (params->cParams.minMatch==3) ? 3 : 4; - size_t const maxNbSeq = blockSize / divider; + size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, params->cParams.minMatch, params->useExternalMatchFinder); size_t const buffOutSize = (zbuff == ZSTDb_buffered && params->outBufferMode == ZSTD_bm_buffered) ? ZSTD_compressBound(blockSize) + 1 : 0; @@ -1904,7 +1962,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, size_t const neededSpace = ZSTD_estimateCCtxSize_usingCCtxParams_internal( ¶ms->cParams, ¶ms->ldmParams, zc->staticSize != 0, params->useRowMatchFinder, - buffInSize, buffOutSize, pledgedSrcSize); + buffInSize, buffOutSize, pledgedSrcSize, params->useExternalMatchFinder); int resizeWorkspace; FORWARD_IF_ERROR(neededSpace, "cctx size estimate failed!"); @@ -2017,6 +2075,14 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->ldmState.loadedDictEnd = 0; } + /* reserve space for block-level external sequences */ + if (params->useExternalMatchFinder) { + size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize); + zc->externalMatchCtx.seqBufferCapacity = maxNbExternalSeq; + zc->externalMatchCtx.seqBuffer = + (ZSTD_Sequence*)ZSTD_cwksp_reserve_aligned(ws, maxNbExternalSeq * sizeof(ZSTD_Sequence)); + } + DEBUGLOG(3, "wksp: finished allocating, %zd bytes remain available", ZSTD_cwksp_available_space(ws)); assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace, resizeWorkspace)); @@ -2868,6 +2934,55 @@ void ZSTD_resetSeqStore(seqStore_t* ssPtr) ssPtr->longLengthType = ZSTD_llt_none; } +/* ZSTD_postProcessExternalMatchFinderResult() : + * Validates and post-processes sequences obtained through the external matchfinder API: + * - Checks whether nbExternalSeqs represents an error condition. + * - Appends a block delimiter to outSeqs if one is not already present. + * See zstd.h for context regarding block delimiters. + * Returns the number of sequences after post-processing, or an error code. */ +static size_t ZSTD_postProcessExternalMatchFinderResult( + ZSTD_Sequence* outSeqs, size_t nbExternalSeqs, size_t outSeqsCapacity, size_t srcSize +) { + RETURN_ERROR_IF( + nbExternalSeqs > outSeqsCapacity, + externalMatchFinder_failed, + "External matchfinder returned error code %lu", + (unsigned long)nbExternalSeqs + ); + + RETURN_ERROR_IF( + nbExternalSeqs == 0 && srcSize > 0, + externalMatchFinder_failed, + "External matchfinder produced zero sequences for a non-empty src buffer!" + ); + + if (srcSize == 0) { + ZSTD_memset(&outSeqs[0], 0, sizeof(ZSTD_Sequence)); + return 1; + } + + { + ZSTD_Sequence const lastSeq = outSeqs[nbExternalSeqs - 1]; + + /* We can return early if lastSeq is already a block delimiter. */ + if (lastSeq.offset == 0 && lastSeq.matchLength == 0) { + return nbExternalSeqs; + } + + /* This error condition is only possible if the external matchfinder + * produced an invalid parse, by definition of ZSTD_sequenceBound(). */ + RETURN_ERROR_IF( + nbExternalSeqs == outSeqsCapacity, + externalMatchFinder_failed, + "nbExternalSeqs == outSeqsCapacity but lastSeq is not a block delimiter!" + ); + + /* lastSeq is not a block delimiter, so we need to append one. */ + ZSTD_memset(&outSeqs[nbExternalSeqs], 0, sizeof(ZSTD_Sequence)); + return nbExternalSeqs + 1; + } +} + typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_buildSeqStore_e; static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) @@ -2915,6 +3030,15 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) } if (zc->externSeqStore.pos < zc->externSeqStore.size) { assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_disable); + + /* External matchfinder + LDM is technically possible, just not implemented yet. + * We need to revisit soon and implement it. */ + RETURN_ERROR_IF( + zc->appliedParams.useExternalMatchFinder, + parameter_combination_unsupported, + "Long-distance matching with external matchfinder enabled is not currently supported." + ); + /* Updates ldmSeqStore.pos */ lastLLSize = ZSTD_ldm_blockCompress(&zc->externSeqStore, @@ -2926,6 +3050,14 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) } else if (zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) { rawSeqStore_t ldmSeqStore = kNullRawSeqStore; + /* External matchfinder + LDM is technically possible, just not implemented yet. + * We need to revisit soon and implement it. */ + RETURN_ERROR_IF( + zc->appliedParams.useExternalMatchFinder, + parameter_combination_unsupported, + "Long-distance matching with external matchfinder enabled is not currently supported." + ); + ldmSeqStore.seq = zc->ldmSequences; ldmSeqStore.capacity = zc->maxNbLdmSequences; /* Updates ldmSeqStore.size */ @@ -2940,10 +3072,64 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) zc->appliedParams.useRowMatchFinder, src, srcSize); assert(ldmSeqStore.pos == ldmSeqStore.size); - } else { /* not long range mode */ + } else if (zc->appliedParams.useExternalMatchFinder) { + assert( + zc->externalMatchCtx.seqBufferCapacity >= ZSTD_sequenceBound(srcSize) + ); + assert(zc->externalMatchCtx.mFinder != NULL); + + { U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog; + + size_t const nbExternalSeqs = (zc->externalMatchCtx.mFinder)( + zc->externalMatchCtx.mState, + zc->externalMatchCtx.seqBuffer, + zc->externalMatchCtx.seqBufferCapacity, + src, srcSize, + NULL, 0, /* dict and dictSize, currently not supported */ + zc->appliedParams.compressionLevel, + windowSize + ); + + size_t const nbPostProcessedSeqs = ZSTD_postProcessExternalMatchFinderResult( + zc->externalMatchCtx.seqBuffer, + nbExternalSeqs, + zc->externalMatchCtx.seqBufferCapacity, + srcSize + ); + + /* Return early if there is no error, since we don't need to worry about last literals */ + if (!ZSTD_isError(nbPostProcessedSeqs)) { + ZSTD_sequencePosition seqPos = {0,0,0}; + ZSTD_copySequencesToSeqStoreExplicitBlockDelim( + zc, &seqPos, zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs, src, srcSize + ); + ms->ldmSeqStore = NULL; + DEBUGLOG(5, "Copied %lu sequences from external matchfinder to internal seqStore.", (unsigned long)nbExternalSeqs); + return ZSTDbss_compress; + } + + /* Propagate the error if fallback is disabled */ + if (!zc->appliedParams.enableMatchFinderFallback) { + return nbPostProcessedSeqs; + } + + /* Fallback to software matchfinder */ + { ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, + zc->appliedParams.useRowMatchFinder, + dictMode); + ms->ldmSeqStore = NULL; + DEBUGLOG( + 5, + "External matchfinder returned error code %lu. Falling back to internal matchfinder.", + (unsigned long)nbExternalSeqs + ); + lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize); + } } + } else { /* not long range mode and no external matchfinder */ ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, zc->appliedParams.useRowMatchFinder, dictMode); + assert(zc->externalMatchCtx.mFinder == NULL); ms->ldmSeqStore = NULL; lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize); } @@ -5726,6 +5912,7 @@ static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx, params.useBlockSplitter = ZSTD_resolveBlockSplitterMode(params.useBlockSplitter, ¶ms.cParams); params.ldmParams.enableLdm = ZSTD_resolveEnableLdm(params.ldmParams.enableLdm, ¶ms.cParams); params.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params.useRowMatchFinder, ¶ms.cParams); + params.validateSequences = ZSTD_resolveExternalSequenceValidation(params.validateSequences); #ifdef ZSTD_MULTITHREAD if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) { @@ -5927,12 +6114,6 @@ size_t ZSTD_compress2(ZSTD_CCtx* cctx, } } -typedef struct { - U32 idx; /* Index in array of ZSTD_Sequence */ - U32 posInSequence; /* Position within sequence at idx */ - size_t posInSrc; /* Number of bytes given by sequences provided so far */ -} ZSTD_sequencePosition; - /* ZSTD_validateSequence() : * @offCode : is presumed to follow format required by ZSTD_storeSeq() * @returns a ZSTD error code if sequence is not valid @@ -5970,10 +6151,7 @@ static U32 ZSTD_finalizeOffBase(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 return offBase; } -/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of - * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter. - */ -static size_t +size_t ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, @@ -6027,19 +6205,7 @@ ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, return 0; } -/* Returns the number of bytes to move the current read position back by. - * Only non-zero if we ended up splitting a sequence. - * Otherwise, it may return a ZSTD error if something went wrong. - * - * This function will attempt to scan through blockSize bytes - * represented by the sequences in @inSeqs, - * storing any (partial) sequences. - * - * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to - * avoid splitting a match, or to avoid splitting a match such that it would produce a match - * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block. - */ -static size_t +size_t ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, const void* src, size_t blockSize) @@ -6572,3 +6738,19 @@ ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeH if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN; return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize, ZSTD_cpm_unknown); } + +void ZSTD_registerExternalMatchFinder( + ZSTD_CCtx* zc, void* mState, + ZSTD_externalMatchFinder_F* mFinder +) { + ZSTD_externalMatchCtx emctx = { + mState, + mFinder, + + /* seqBuffer is allocated later (from the cwskp) */ + NULL, /* seqBuffer */ + 0 /* seqBufferCapacity */ + }; + zc->externalMatchCtx = emctx; + zc->requestedParams.useExternalMatchFinder = 1; +} diff --git a/libfenrir/src/main/jni/compress/zstd/compress/zstd_compress_internal.h b/libfenrir/src/main/jni/compress/zstd/compress/zstd_compress_internal.h index e4bb2f537..f755a1f79 100644 --- a/libfenrir/src/main/jni/compress/zstd/compress/zstd_compress_internal.h +++ b/libfenrir/src/main/jni/compress/zstd/compress/zstd_compress_internal.h @@ -150,6 +150,12 @@ typedef struct { size_t capacity; /* The capacity starting from `seq` pointer */ } rawSeqStore_t; +typedef struct { + U32 idx; /* Index in array of ZSTD_Sequence */ + U32 posInSequence; /* Position within sequence at idx */ + size_t posInSrc; /* Number of bytes given by sequences provided so far */ +} ZSTD_sequencePosition; + UNUSED_ATTR static const rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0, 0}; typedef struct { @@ -340,6 +346,15 @@ struct ZSTD_CCtx_params_s { /* Controls prefetching in some dictMatchState matchfinders */ ZSTD_paramSwitch_e prefetchCDictTables; + + /* Controls whether zstd will fall back to an internal matchfinder + * if the external matchfinder returns an error code. */ + int enableMatchFinderFallback; + + /* Indicates whether an external matchfinder has been referenced. + * Users can't set this externally. + * It is set internally in ZSTD_registerExternalMatchFinder(). */ + int useExternalMatchFinder; }; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */ #define COMPRESS_SEQUENCES_WORKSPACE_SIZE (sizeof(unsigned) * (MaxSeq + 2)) @@ -371,6 +386,14 @@ typedef struct { ZSTD_entropyCTablesMetadata_t entropyMetadata; } ZSTD_blockSplitCtx; +/* Context for block-level external matchfinder API */ +typedef struct { + void* mState; + ZSTD_externalMatchFinder_F* mFinder; + ZSTD_Sequence* seqBuffer; + size_t seqBufferCapacity; +} ZSTD_externalMatchCtx; + struct ZSTD_CCtx_s { ZSTD_compressionStage_e stage; int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */ @@ -440,6 +463,9 @@ struct ZSTD_CCtx_s { /* Workspace for block splitter */ ZSTD_blockSplitCtx blockSplitCtx; + + /* Workspace for external matchfinder */ + ZSTD_externalMatchCtx externalMatchCtx; }; typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; @@ -1411,4 +1437,31 @@ U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat); */ void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize); +/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of + * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter. + * Note that the block delimiter must include the last literals of the block. + */ +size_t +ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, + ZSTD_sequencePosition* seqPos, + const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, + const void* src, size_t blockSize); + +/* Returns the number of bytes to move the current read position back by. + * Only non-zero if we ended up splitting a sequence. + * Otherwise, it may return a ZSTD error if something went wrong. + * + * This function will attempt to scan through blockSize bytes + * represented by the sequences in @inSeqs, + * storing any (partial) sequences. + * + * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to + * avoid splitting a match, or to avoid splitting a match such that it would produce a match + * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block. + */ +size_t +ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, + const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, + const void* src, size_t blockSize); + #endif /* ZSTD_COMPRESS_H */ diff --git a/libfenrir/src/main/jni/compress/zstd/compress/zstd_opt.c b/libfenrir/src/main/jni/compress/zstd/compress/zstd_opt.c index 6e08dc8c7..9b14093cb 100644 --- a/libfenrir/src/main/jni/compress/zstd/compress/zstd_opt.c +++ b/libfenrir/src/main/jni/compress/zstd/compress/zstd_opt.c @@ -26,27 +26,35 @@ #if 0 /* approximation at bit level (for tests) */ # define BITCOST_ACCURACY 0 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat, opt) ((void)opt, ZSTD_bitWeight(stat)) +# define WEIGHT(stat, opt) ((void)(opt), ZSTD_bitWeight(stat)) #elif 0 /* fractional bit accuracy (for tests) */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat)) +# define WEIGHT(stat,opt) ((void)(opt), ZSTD_fracWeight(stat)) #else /* opt==approx, ultra==accurate */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat)) +# define WEIGHT(stat,opt) ((opt) ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat)) #endif +/* ZSTD_bitWeight() : + * provide estimated "cost" of a stat in full bits only */ MEM_STATIC U32 ZSTD_bitWeight(U32 stat) { return (ZSTD_highbit32(stat+1) * BITCOST_MULTIPLIER); } +/* ZSTD_fracWeight() : + * provide fractional-bit "cost" of a stat, + * using linear interpolation approximation */ MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat) { U32 const stat = rawStat + 1; U32 const hb = ZSTD_highbit32(stat); U32 const BWeight = hb * BITCOST_MULTIPLIER; + /* Fweight was meant for "Fractional weight" + * but it's effectively a value between 1 and 2 + * using fixed point arithmetic */ U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb; U32 const weight = BWeight + FWeight; assert(hb + BITCOST_ACCURACY < 31); @@ -91,17 +99,19 @@ static U32 sum_u32(const unsigned table[], size_t nbElts) static U32 ZSTD_downscaleStats(unsigned* table, U32 lastEltIndex, U32 shift) { U32 s, sum=0; - DEBUGLOG(5, "ZSTD_downscaleStats (nbElts=%u, shift=%u)", (unsigned)lastEltIndex+1, (unsigned)shift); + DEBUGLOG(5, "ZSTD_downscaleStats (nbElts=%u, shift=%u)", + (unsigned)lastEltIndex+1, (unsigned)shift ); assert(shift < 30); for (s=0; s> shift); - sum += table[s]; + unsigned newStat = 1 + (table[s] >> shift); + sum += newStat; + table[s] = newStat; } return sum; } /* ZSTD_scaleStats() : - * reduce all elements in table is sum too large + * reduce all elt frequencies in table if sum too large * return the resulting sum of elements */ static U32 ZSTD_scaleStats(unsigned* table, U32 lastEltIndex, U32 logTarget) { @@ -129,18 +139,22 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize); optPtr->priceType = zop_dynamic; - if (optPtr->litLengthSum == 0) { /* first block : init */ - if (srcSize <= ZSTD_PREDEF_THRESHOLD) { /* heuristic */ - DEBUGLOG(5, "(srcSize <= ZSTD_PREDEF_THRESHOLD) => zop_predef"); + if (optPtr->litLengthSum == 0) { /* no literals stats collected -> first block assumed -> init */ + + /* heuristic: use pre-defined stats for too small inputs */ + if (srcSize <= ZSTD_PREDEF_THRESHOLD) { + DEBUGLOG(5, "srcSize <= %i : use predefined stats", ZSTD_PREDEF_THRESHOLD); optPtr->priceType = zop_predef; } assert(optPtr->symbolCosts != NULL); if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) { - /* huffman table presumed generated by dictionary */ + + /* huffman stats covering the full value set : table presumed generated by dictionary */ optPtr->priceType = zop_dynamic; if (compressedLiterals) { + /* generate literals statistics from huffman table */ unsigned lit; assert(optPtr->litFreq != NULL); optPtr->litSum = 0; @@ -188,10 +202,11 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->offCodeSum += optPtr->offCodeFreq[of]; } } - } else { /* not a dictionary */ + } else { /* huf.repeatMode != HUF_repeat_valid => presumed not a dictionary */ assert(optPtr->litFreq != NULL); if (compressedLiterals) { + /* base initial cost of literals on direct frequency within src */ unsigned lit = MaxLit; HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ optPtr->litSum = ZSTD_downscaleStats(optPtr->litFreq, MaxLit, 8); @@ -224,10 +239,9 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->offCodeSum = sum_u32(baseOFCfreqs, MaxOff+1); } - } - } else { /* new block : re-use previous statistics, scaled down */ + } else { /* new block : scale down accumulated statistics */ if (compressedLiterals) optPtr->litSum = ZSTD_scaleStats(optPtr->litFreq, MaxLit, 12); @@ -275,10 +289,11 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP assert(litLength <= ZSTD_BLOCKSIZE_MAX); if (optPtr->priceType == zop_predef) return WEIGHT(litLength, optLevel); - /* We can't compute the litLength price for sizes >= ZSTD_BLOCKSIZE_MAX - * because it isn't representable in the zstd format. So instead just - * call it 1 bit more than ZSTD_BLOCKSIZE_MAX - 1. In this case the block - * would be all literals. + + /* ZSTD_LLcode() can't compute litLength price for sizes >= ZSTD_BLOCKSIZE_MAX + * because it isn't representable in the zstd format. + * So instead just pretend it would cost 1 bit more than ZSTD_BLOCKSIZE_MAX - 1. + * In such a case, the block would be all literals. */ if (litLength == ZSTD_BLOCKSIZE_MAX) return BITCOST_MULTIPLIER + ZSTD_litLengthPrice(ZSTD_BLOCKSIZE_MAX - 1, optPtr, optLevel); @@ -292,7 +307,7 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP } /* ZSTD_getMatchPrice() : - * Provides the cost of the match part (offset + matchLength) of a sequence + * Provides the cost of the match part (offset + matchLength) of a sequence. * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence. * @offBase : sumtype, representing an offset or a repcode, and using numeric representation of ZSTD_storeSeq() * @optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) @@ -308,8 +323,9 @@ ZSTD_getMatchPrice(U32 const offBase, U32 const mlBase = matchLength - MINMATCH; assert(matchLength >= MINMATCH); - if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ - return WEIGHT(mlBase, optLevel) + ((16 + offCode) * BITCOST_MULTIPLIER); + if (optPtr->priceType == zop_predef) /* fixed scheme, does not use statistics */ + return WEIGHT(mlBase, optLevel) + + ((16 + offCode) * BITCOST_MULTIPLIER); /* emulated offset cost */ /* dynamic statistics */ price = (offCode * BITCOST_MULTIPLIER) + (optPtr->offCodeSumBasePrice - WEIGHT(optPtr->offCodeFreq[offCode], optLevel)); @@ -347,7 +363,7 @@ static void ZSTD_updateStats(optState_t* const optPtr, optPtr->litLengthSum++; } - /* offset code : expected to follow storeSeq() numeric representation */ + /* offset code : follows storeSeq() numeric representation */ { U32 const offCode = ZSTD_highbit32(offBase); assert(offCode <= MaxOff); optPtr->offCodeFreq[offCode]++; @@ -1352,7 +1368,7 @@ size_t ZSTD_compressBlock_btopt( /* ZSTD_initStats_ultra(): * make a first compression pass, just to seed stats with more accurate starting values. * only works on first block, with no dictionary and no ldm. - * this function cannot error, hence its contract must be respected. + * this function cannot error out, its narrow contract must be respected. */ static void ZSTD_initStats_ultra(ZSTD_matchState_t* ms, @@ -1371,7 +1387,7 @@ ZSTD_initStats_ultra(ZSTD_matchState_t* ms, ZSTD_compressBlock_opt2(ms, seqStore, tmpRep, src, srcSize, ZSTD_noDict); /* generate stats into ms->opt*/ - /* invalidate first scan from history */ + /* invalidate first scan from history, only keep entropy stats */ ZSTD_resetSeqStore(seqStore); ms->window.base -= srcSize; ms->window.dictLimit += (U32)srcSize; @@ -1395,20 +1411,20 @@ size_t ZSTD_compressBlock_btultra2( U32 const curr = (U32)((const BYTE*)src - ms->window.base); DEBUGLOG(5, "ZSTD_compressBlock_btultra2 (srcSize=%zu)", srcSize); - /* 2-pass strategy: + /* 2-passes strategy: * this strategy makes a first pass over first block to collect statistics - * and seed next round's statistics with it. - * After 1st pass, function forgets everything, and starts a new block. + * in order to seed next round's statistics with it. + * After 1st pass, function forgets history, and starts a new block. * Consequently, this can only work if no data has been previously loaded in tables, * aka, no dictionary, no prefix, no ldm preprocessing. * The compression ratio gain is generally small (~0.5% on first block), - * the cost is 2x cpu time on first block. */ + ** the cost is 2x cpu time on first block. */ assert(srcSize <= ZSTD_BLOCKSIZE_MAX); if ( (ms->opt.litLengthSum==0) /* first block */ && (seqStore->sequences == seqStore->sequencesStart) /* no ldm */ && (ms->window.dictLimit == ms->window.lowLimit) /* no dictionary */ - && (curr == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */ - && (srcSize > ZSTD_PREDEF_THRESHOLD) + && (curr == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */ + && (srcSize > ZSTD_PREDEF_THRESHOLD) /* input large enough to not employ default stats */ ) { ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize); } diff --git a/libfenrir/src/main/jni/compress/zstd/jni_fast_zstd.c b/libfenrir/src/main/jni/compress/zstd/jni_fast_zstd.c index 0d785c7c2..805b24964 100644 --- a/libfenrir/src/main/jni/compress/zstd/jni_fast_zstd.c +++ b/libfenrir/src/main/jni/compress/zstd/jni_fast_zstd.c @@ -1,3 +1,6 @@ +#ifndef ZSTD_STATIC_LINKING_ONLY +#define ZSTD_STATIC_LINKING_ONLY +#endif #include #include #include @@ -330,6 +333,19 @@ JNIEXPORT jlong JNICALL Java_com_github_luben_zstd_ZstdCompressCtx_reset0 return ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters); } +JNIEXPORT jobject JNICALL Java_com_github_luben_zstd_ZstdCompressCtx_getFrameProgression0 + (JNIEnv *env, jclass jctx) { + ZSTD_CCtx* cctx = (ZSTD_CCtx*)(intptr_t)(*env)->GetLongField(env, jctx, compress_ctx_nativePtr); + ZSTD_frameProgression native_progression = ZSTD_getFrameProgression(cctx); + + jclass frame_progression_class = (*env)->FindClass(env, "com/github/luben/zstd/ZstdFrameProgression"); + jmethodID frame_progression_constructor = (*env)->GetMethodID(env, frame_progression_class, "", "(JJJJII)V"); + return (*env)->NewObject( + env, frame_progression_class, frame_progression_constructor, native_progression.ingested, + native_progression.consumed, native_progression.produced, native_progression.flushed, + native_progression.currentJobID, native_progression.nbActiveWorkers); +} + JNIEXPORT jlong JNICALL Java_com_github_luben_zstd_ZstdCompressCtx_setPledgedSrcSize0 (JNIEnv *env, jclass jctx, jlong src_size) { if (src_size < 0) { diff --git a/libfenrir/src/main/jni/compress/zstd/zdict.h b/libfenrir/src/main/jni/compress/zstd/zdict.h index 7b37e50e8..2268f948a 100644 --- a/libfenrir/src/main/jni/compress/zstd/zdict.h +++ b/libfenrir/src/main/jni/compress/zstd/zdict.h @@ -461,8 +461,8 @@ ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_legacy( # endif #endif /* ZDICT_DISABLE_DEPRECATE_WARNINGS */ -ZDICTLIB_STATIC_API ZDICT_DEPRECATED("use ZDICT_finalizeDictionary() instead") +ZDICTLIB_STATIC_API size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); diff --git a/libfenrir/src/main/jni/compress/zstd/zstd.h b/libfenrir/src/main/jni/compress/zstd/zstd.h index dd72e17ed..480d65f67 100644 --- a/libfenrir/src/main/jni/compress/zstd/zstd.h +++ b/libfenrir/src/main/jni/compress/zstd/zstd.h @@ -478,6 +478,7 @@ typedef enum { * ZSTD_c_useBlockSplitter * ZSTD_c_useRowMatchFinder * ZSTD_c_prefetchCDictTables + * ZSTD_c_enableMatchFinderFallback * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them. * note : never ever use experimentalParam? names directly; * also, the enums values themselves are unstable and can still change. @@ -497,7 +498,8 @@ typedef enum { ZSTD_c_experimentalParam13=1010, ZSTD_c_experimentalParam14=1011, ZSTD_c_experimentalParam15=1012, - ZSTD_c_experimentalParam16=1013 + ZSTD_c_experimentalParam16=1013, + ZSTD_c_experimentalParam17=1014 } ZSTD_cParameter; typedef struct { @@ -560,7 +562,7 @@ typedef enum { * They will be used to compress next frame. * Resetting session never fails. * - The parameters : changes all parameters back to "default". - * This removes any reference to any dictionary too. + * This also removes any reference to any dictionary or external matchfinder. * Parameters can only be changed between 2 sessions (i.e. no compression is currently ongoing) * otherwise the reset fails, and function returns an error value (which can be tested using ZSTD_isError()) * - Both : similar to resetting the session, followed by resetting parameters. @@ -1740,6 +1742,13 @@ ZSTDLIB_STATIC_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params); * This function never fails (wide contract) */ ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize); +/*! ZSTD_CCtx_setCParams() : + * Set all parameters provided within @cparams into the working @cctx. + * Note : if modifying parameters during compression (MT mode only), + * note that changes to the .windowLog parameter will be ignored. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()) */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams); + /*! ZSTD_compress_advanced() : * Note : this function is now DEPRECATED. * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters. @@ -1747,10 +1756,10 @@ ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressio ZSTD_DEPRECATED("use ZSTD_compress2") ZSTDLIB_STATIC_API size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const void* dict,size_t dictSize, - ZSTD_parameters params); + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize, + ZSTD_parameters params); /*! ZSTD_compress_usingCDict_advanced() : * Note : this function is now DEPRECATED. @@ -2044,6 +2053,20 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo */ #define ZSTD_c_prefetchCDictTables ZSTD_c_experimentalParam16 +/* ZSTD_c_enableMatchFinderFallback + * Allowed values are 0 (disable) and 1 (enable). The default setting is 0. + * + * Controls whether zstd will fall back to an internal matchfinder if an + * external matchfinder is registered and returns an error code. This fallback is + * block-by-block: the internal matchfinder will only be called for blocks where + * the external matchfinder returns an error code. Fallback compression will + * follow any other cParam settings, such as compression level, the same as in a + * normal (fully-internal) compression operation. + * + * The user is strongly encouraged to read the full external matchfinder API + * documentation (below) before setting this parameter. */ +#define ZSTD_c_enableMatchFinderFallback ZSTD_c_experimentalParam17 + /*! ZSTD_CCtx_getParameter() : * Get the requested compression parameter value, selected by enum ZSTD_cParameter, * and store it into int* value. @@ -2510,8 +2533,8 @@ ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLev ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */ -ZSTDLIB_STATIC_API ZSTD_DEPRECATED("This function will likely be removed in a future release. It is misleading and has very limited utility.") +ZSTDLIB_STATIC_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */ ZSTDLIB_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); @@ -2676,6 +2699,142 @@ ZSTDLIB_STATIC_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_ ZSTDLIB_STATIC_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */ +/* ********************** EXTERNAL MATCHFINDER API ********************** + * + * *** OVERVIEW *** + * This API allows users to replace the zstd internal block-level matchfinder + * with an external matchfinder function. Potential applications of the API + * include hardware-accelerated matchfinders and matchfinders specialized to + * particular types of data. + * + * See contrib/externalMatchfinder for an example program employing the + * external matchfinder API. + * + * *** USAGE *** + * The user is responsible for implementing a function of type + * ZSTD_externalMatchFinder_F. For each block, zstd will pass the following + * arguments to the user-provided function: + * + * - externalMatchState: a pointer to a user-managed state for the external + * matchfinder. + * + * - outSeqs, outSeqsCapacity: an output buffer for sequences produced by the + * external matchfinder. outSeqsCapacity is guaranteed >= + * ZSTD_sequenceBound(srcSize). The memory backing outSeqs is managed by + * the CCtx. + * + * - src, srcSize: an input buffer which the external matchfinder must parse + * into sequences. srcSize is guaranteed to be <= ZSTD_BLOCKSIZE_MAX. + * + * - dict, dictSize: a history buffer, which may be empty, which the external + * matchfinder may reference as it produces sequences for the src buffer. + * Currently, zstd will always pass dictSize == 0 into external matchfinders, + * but this will change in the future. + * + * - compressionLevel: a signed integer representing the zstd compression level + * set by the user for the current operation. The external matchfinder may + * choose to use this information to change its compression strategy and + * speed/ratio tradeoff. Note: The compression level does not reflect zstd + * parameters set through the advanced API. + * + * - windowSize: a size_t representing the maximum allowed offset for external + * sequences. Note that sequence offsets are sometimes allowed to exceed the + * windowSize if a dictionary is present, see doc/zstd_compression_format.md + * for details. + * + * The user-provided function shall return a size_t representing the number of + * sequences written to outSeqs. This return value will be treated as an error + * code if it is greater than outSeqsCapacity. The return value must be non-zero + * if srcSize is non-zero. The ZSTD_EXTERNAL_MATCHFINDER_ERROR macro is provided + * for convenience, but any value greater than outSeqsCapacity will be treated as + * an error code. + * + * If the user-provided function does not return an error code, the sequences + * written to outSeqs must be a valid parse of the src buffer. Data corruption may + * occur if the parse is not valid. A parse is defined to be valid if the + * following conditions hold: + * - The sum of matchLengths and literalLengths is equal to srcSize. + * - All sequences in the parse have matchLength != 0, except for the final + * sequence. matchLength is not constrained for the final sequence. + * - All offsets respect the windowSize parameter as specified in + * doc/zstd_compression_format.md. + * + * zstd will only validate these conditions (and fail compression if they do not + * hold) if the ZSTD_c_validateSequences cParam is enabled. Note that sequence + * validation has a performance cost. + * + * If the user-provided function returns an error, zstd will either fall back + * to an internal matchfinder or fail the compression operation. The user can + * choose between the two behaviors by setting the + * ZSTD_c_enableMatchFinderFallback cParam. Fallback compression will follow any + * other cParam settings, such as compression level, the same as in a normal + * compression operation. + * + * The user shall instruct zstd to use a particular ZSTD_externalMatchFinder_F + * function by calling ZSTD_registerExternalMatchFinder(cctx, externalMatchState, + * externalMatchFinder). This setting will persist until the next parameter reset + * of the CCtx. + * + * The externalMatchState must be initialized by the user before calling + * ZSTD_registerExternalMatchFinder. The user is responsible for destroying the + * externalMatchState. + * + * *** LIMITATIONS *** + * External matchfinders are compatible with all zstd compression APIs. There are + * only two limitations. + * + * First, the ZSTD_c_enableLongDistanceMatching cParam is not supported. + * COMPRESSION WILL FAIL if it is enabled and the user tries to compress with an + * external matchfinder. + * - Note that ZSTD_c_enableLongDistanceMatching is auto-enabled by default in + * some cases (see its documentation for details). Users must explicitly set + * ZSTD_c_enableLongDistanceMatching to ZSTD_ps_disable in such cases if an + * external matchfinder is registered. + * - As of this writing, ZSTD_c_enableLongDistanceMatching is disabled by default + * whenever ZSTD_c_windowLog < 128MB, but that's subject to change. Users should + * check the docs on ZSTD_c_enableLongDistanceMatching whenever the external + * matchfinder API is used in conjunction with advanced settings (like windowLog). + * + * Second, history buffers are not supported. Concretely, zstd will always pass + * dictSize == 0 to the external matchfinder (for now). This has two implications: + * - Dictionaries are not supported. Compression will *not* fail if the user + * references a dictionary, but the dictionary won't have any effect. + * - Stream history is not supported. All compression APIs, including streaming + * APIs, work with the external matchfinder, but the external matchfinder won't + * receive any history from the previous block. Each block is an independent chunk. + * + * Long-term, we plan to overcome both limitations. There is no technical blocker to + * overcoming them. It is purely a question of engineering effort. + */ + +#define ZSTD_EXTERNAL_MATCHFINDER_ERROR ((size_t)(-1)) + +typedef size_t ZSTD_externalMatchFinder_F ( + void* externalMatchState, + ZSTD_Sequence* outSeqs, size_t outSeqsCapacity, + const void* src, size_t srcSize, + const void* dict, size_t dictSize, + int compressionLevel, + size_t windowSize +); + +/*! ZSTD_registerExternalMatchFinder() : + * Instruct zstd to use an external matchfinder function. + * + * The externalMatchState must be initialized by the caller, and the caller is + * responsible for managing its lifetime. This parameter is sticky across + * compressions. It will remain set until the user explicitly resets compression + * parameters. + * + * The user is strongly encouraged to read the full API documentation (above) + * before calling this function. */ +ZSTDLIB_STATIC_API void +ZSTD_registerExternalMatchFinder( + ZSTD_CCtx* cctx, + void* externalMatchState, + ZSTD_externalMatchFinder_F* externalMatchFinder +); + #endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */ #if defined (__cplusplus) diff --git a/libfenrir/src/main/jni/compress/zstd/zstd_errors.h b/libfenrir/src/main/jni/compress/zstd/zstd_errors.h index 880e8e4f6..bd6dbee5f 100644 --- a/libfenrir/src/main/jni/compress/zstd/zstd_errors.h +++ b/libfenrir/src/main/jni/compress/zstd/zstd_errors.h @@ -75,6 +75,7 @@ typedef enum { ZSTD_error_dictionary_wrong = 32, ZSTD_error_dictionaryCreation_failed = 34, ZSTD_error_parameter_unsupported = 40, + ZSTD_error_parameter_combination_unsupported = 41, ZSTD_error_parameter_outOfBound = 42, ZSTD_error_tableLog_tooLarge = 44, ZSTD_error_maxSymbolValue_tooLarge = 46, @@ -92,6 +93,7 @@ typedef enum { ZSTD_error_seekableIO = 102, ZSTD_error_dstBuffer_wrong = 104, ZSTD_error_srcBuffer_wrong = 105, + ZSTD_error_externalMatchFinder_failed = 106, ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */ } ZSTD_ErrorCode; diff --git a/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/document.h b/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/document.h index 4f1e24673..2cd9a70a6 100644 --- a/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/document.h +++ b/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/document.h @@ -1078,6 +1078,7 @@ class GenericValue { */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } +#ifndef __cpp_impl_three_way_comparison //! Not-equal-to operator /*! \return !(*this == rhs) */ @@ -1092,7 +1093,6 @@ class GenericValue { */ template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } -#ifndef __cpp_impl_three_way_comparison //! Equal-to operator with arbitrary types (symmetric version) /*! \return (rhs == lhs) */ diff --git a/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/error/en.h b/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/error/en.h index f27d62a34..c87b04eb1 100644 --- a/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/error/en.h +++ b/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/error/en.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ERROR_EN_H_ @@ -109,6 +109,9 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode val case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); + case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing."); + case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading."); + default: return RAPIDJSON_ERROR_STRING("Unknown error."); } } @@ -134,6 +137,10 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode val case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider."); case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema."); case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'."); + case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized."); + case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported."); + case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document."); + case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'."); default: return RAPIDJSON_ERROR_STRING("Unknown error."); } diff --git a/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/error/error.h b/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/error/error.h index d2a2fc486..8bdc39541 100644 --- a/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/error/error.h +++ b/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/error/error.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ERROR_ERROR_H_ @@ -192,7 +192,10 @@ enum ValidateErrorCode { kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. - kValidateErrorNot //!< Property matched the sub-schema specified by 'not'. + kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'. + + kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing + kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading }; //! Function pointer type of GetValidateError(). @@ -225,7 +228,11 @@ enum SchemaErrorCode { kSchemaErrorRefCyclical, //!< $ref is cyclical kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema - kSchemaErrorRegexInvalid //!< Invalid regular expression in 'pattern' or 'patternProperties' + kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties' + kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized + kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported + kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document + kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly' }; //! Function pointer type of GetSchemaError(). diff --git a/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/schema.h b/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/schema.h index 836c0f1f3..6e51177ac 100644 --- a/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/schema.h +++ b/libfenrir/src/main/jni/rlottie/src/lottie/rapidjson/schema.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available-> -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource->org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the // specific language governing permissions and limitations under the License-> #ifndef RAPIDJSON_SCHEMA_H_ @@ -74,48 +74,94 @@ RAPIDJSON_NAMESPACE_BEGIN namespace internal { -inline void PrintInvalidKeyword(const char* keyword) { - printf("Fail keyword: %s\n", keyword); +inline void PrintInvalidKeywordData(const char* keyword) { + printf(" Fail keyword: '%s'\n", keyword); +} + +inline void PrintInvalidKeywordData(const wchar_t* keyword) { + wprintf(L" Fail keyword: '%ls'\n", keyword); +} + +inline void PrintInvalidDocumentData(const char* document) { + printf(" Fail document: '%s'\n", document); +} + +inline void PrintInvalidDocumentData(const wchar_t* document) { + wprintf(L" Fail document: '%ls'\n", document); +} + +inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) { + printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) { + wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) { + printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved); } -inline void PrintInvalidKeyword(const wchar_t* keyword) { - wprintf(L"Fail keyword: %ls\n", keyword); +inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) { + wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved); } -inline void PrintInvalidDocument(const char* document) { - printf("Fail document: %s\n\n", document); +inline void PrintMethodData(const char* method) { + printf("%s\n", method); } -inline void PrintInvalidDocument(const wchar_t* document) { - wprintf(L"Fail document: %ls\n\n", document); +inline void PrintMethodData(const char* method, bool b) { + printf("%s, Data: '%s'\n", method, b ? "true" : "false"); } -inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { - printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +inline void PrintMethodData(const char* method, int64_t i) { + printf("%s, Data: '%" PRId64 "'\n", method, i); } -inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { - wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +inline void PrintMethodData(const char* method, uint64_t u) { + printf("%s, Data: '%" PRIu64 "'\n", method, u); +} + +inline void PrintMethodData(const char* method, double d) { + printf("%s, Data: '%lf'\n", method, d); +} + +inline void PrintMethodData(const char* method, const char* s) { + printf("%s, Data: '%s'\n", method, s); +} + +inline void PrintMethodData(const char* method, const wchar_t* s) { + wprintf(L"%hs, Data: '%ls'\n", method, s); +} + +inline void PrintMethodData(const char* method, const char* s1, const char* s2) { + printf("%s, Data: '%s', '%s'\n", method, s1, s2); +} + +inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) { + wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2); } } // namespace internal #endif // RAPIDJSON_SCHEMA_VERBOSE -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_INVALID_KEYWORD_RETURN - +#ifndef RAPIDJSON_SCHEMA_PRINT #if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__) #else -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#define RAPIDJSON_SCHEMA_PRINT(name, ...) #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN #define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ RAPIDJSON_MULTILINEMACRO_BEGIN\ context.invalidCode = code;\ context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ - RAPIDJSON_INVALID_KEYWORD_VERBOSE(context.invalidKeyword);\ + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\ return false;\ RAPIDJSON_MULTILINEMACRO_END @@ -138,9 +184,53 @@ RAPIDJSON_MULTILINEMACRO_END enum ValidateFlag { kValidateNoFlags = 0, //!< No flags are set. kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. + kValidateReadFlag = 2, //!< Validation is for a read semantic. + kValidateWriteFlag = 4, //!< Validation is for a write semantic. kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS }; +/////////////////////////////////////////////////////////////////////////////// +// Specification +enum SchemaDraft { + kDraftUnknown = -1, + kDraftNone = 0, + kDraft03 = 3, + kDraftMin = 4, //!< Current minimum supported draft + kDraft04 = 4, + kDraft05 = 5, + kDraftMax = 5, //!< Current maximum supported draft + kDraft06 = 6, + kDraft07 = 7, + kDraft2019_09 = 8, + kDraft2020_12 = 9 +}; + +enum OpenApiVersion { + kVersionUnknown = -1, + kVersionNone = 0, + kVersionMin = 2, //!< Current minimum supported version + kVersion20 = 2, + kVersion30 = 3, + kVersionMax = 3, //!< Current maximum supported version + kVersion31 = 4, +}; + +struct Specification { + Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {} + Specification(OpenApiVersion o) : oapi(o) { + if (oapi == kVersion20) draft = kDraft04; + else if (oapi == kVersion30) draft = kDraft05; + else if (oapi == kVersion31) draft = kDraft2020_12; + else draft = kDraft04; + } + ~Specification() {} + bool IsSupported() const { + return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax))); + } + SchemaDraft draft; + OpenApiVersion oapi; +}; + /////////////////////////////////////////////////////////////////////////////// // Forward declarations @@ -233,6 +323,8 @@ class IValidationErrorHandler { virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0; virtual void Disallowed() = 0; + virtual void DisallowedWhenWriting() = 0; + virtual void DisallowedWhenReading() = 0; }; @@ -253,10 +345,10 @@ class Hasher { bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } - bool Double(double d) { - Number n; + bool Double(double d) { + Number n; if (d < 0) n.u.i = static_cast(d); - else n.u.u = static_cast(d); + else n.u.u = static_cast(d); n.d = d; return WriteNumber(n); } @@ -350,10 +442,11 @@ struct SchemaValidationContext { kPatternValidatorWithAdditionalProperty }; - SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) : + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) : factory(f), error_handler(eh), schema(s), + flags(fl), valueSchema(), invalidKeyword(), invalidCode(), @@ -401,6 +494,7 @@ struct SchemaValidationContext { SchemaValidatorFactoryType& factory; ErrorHandlerType& error_handler; const SchemaType* schema; + unsigned flags; const SchemaType* valueSchema; const Ch* invalidKeyword; ValidateErrorCode invalidCode; @@ -443,6 +537,7 @@ class Schema { allocator_(allocator), uri_(schemaDocument->GetURI(), *allocator), id_(id, allocator), + spec_(schemaDocument->GetSpecification()), pointer_(p, allocator), typeless_(schemaDocument->GetTypeless()), enum_(), @@ -475,8 +570,15 @@ class Schema { maxLength_(~SizeType(0)), exclusiveMinimum_(false), exclusiveMaximum_(false), - defaultValueLength_(0) + defaultValueLength_(0), + readOnly_(false), + writeOnly_(false), + nullable_(false) { + GenericStringBuffer sb; + p.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString()); + typedef typename ValueType::ConstValueIterator ConstValueIterator; typedef typename ValueType::ConstMemberIterator ConstMemberIterator; @@ -495,10 +597,13 @@ class Schema { return; // If we have an id property, resolve it with the in-scope id + // Not supported for open api 2.0 or 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (const ValueType* v = GetMember(value, GetIdString())) { if (v->IsString()) { UriType local(*v, allocator); id_ = local.Resolve(id_, allocator); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString()); } } @@ -525,8 +630,11 @@ class Schema { } } - if (schemaDocument) { + if (schemaDocument) AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + + // AnyOf, OneOf, Not not supported for open api 2.0 + if (schemaDocument && spec_.oapi != kVersion20) { AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); @@ -555,6 +663,8 @@ class Schema { if (itr->IsString()) AddUniqueElement(allProperties, *itr); + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (dependencies && dependencies->IsObject()) for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { AddUniqueElement(allProperties, itr->name); @@ -584,6 +694,8 @@ class Schema { } } + // PatternProperties not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { PointerType q = p.Append(GetPatternPropertiesString(), allocator_); patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); @@ -608,6 +720,8 @@ class Schema { } } + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (dependencies && dependencies->IsObject()) { PointerType q = p.Append(GetDependenciesString(), allocator_); hasDependencies_ = true; @@ -659,6 +773,8 @@ class Schema { AssignIfExist(minItems_, value, GetMinItemsString()); AssignIfExist(maxItems_, value, GetMaxItemsString()); + // AdditionalItems not supported for openapi 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { if (v->IsBool()) additionalItems_ = v->GetBool(); @@ -696,6 +812,23 @@ class Schema { if (v->IsString()) defaultValueLength_ = v->GetStringLength(); + // ReadOnly - open api only (until draft 7 supported) + // WriteOnly - open api 3 only (until draft 7 supported) + // Both can't be true + if (spec_.oapi != kVersionNone) + AssignIfExist(readOnly_, value, GetReadOnlyString()); + if (spec_.oapi >= kVersion30) + AssignIfExist(writeOnly_, value, GetWriteOnlyString()); + if (readOnly_ && writeOnly_) + schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p); + + // Nullable - open api 3 only + // If true add 'null' as allowable type + if (spec_.oapi >= kVersion30) { + AssignIfExist(nullable_, value, GetNullableString()); + if (nullable_) + AddType(GetNullString()); + } } ~Schema() { @@ -727,11 +860,16 @@ class Schema { return id_; } + const Specification& GetSpecification() const { + return spec_; + } + const PointerType& GetPointer() const { return pointer_; } bool BeginValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue"); if (context.inArray) { if (uniqueItems_) context.valueUniqueness = true; @@ -763,6 +901,7 @@ class Schema { } RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue"); // Only check pattern properties if we have validators if (context.patternPropertiesValidatorCount > 0) { bool otherValid = false; @@ -853,6 +992,7 @@ class Schema { } bool Null(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null"); if (!(type_ & (1 << kNullSchemaType))) { DisallowedType(context, GetNullString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); @@ -860,39 +1000,43 @@ class Schema { return CreateParallelValidator(context); } - bool Bool(Context& context, bool) const { - if (!(type_ & (1 << kBooleanSchemaType))) { - DisallowedType(context, GetBooleanString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); - } + bool Bool(Context& context, bool b) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b); + if (!CheckBool(context, b)) + return false; return CreateParallelValidator(context); } bool Int(Context& context, int i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i); if (!CheckInt(context, i)) return false; return CreateParallelValidator(context); } bool Uint(Context& context, unsigned u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u); if (!CheckUint(context, u)) return false; return CreateParallelValidator(context); } bool Int64(Context& context, int64_t i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i); if (!CheckInt(context, i)) return false; return CreateParallelValidator(context); } bool Uint64(Context& context, uint64_t u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u); if (!CheckUint(context, u)) return false; return CreateParallelValidator(context); } bool Double(Context& context, double d) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d); if (!(type_ & (1 << kNumberSchemaType))) { DisallowedType(context, GetNumberString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); @@ -911,6 +1055,7 @@ class Schema { } bool String(Context& context, const Ch* str, SizeType length, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str); if (!(type_ & (1 << kStringSchemaType))) { DisallowedType(context, GetStringString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); @@ -939,6 +1084,7 @@ class Schema { } bool StartObject(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject"); if (!(type_ & (1 << kObjectSchemaType))) { DisallowedType(context, GetObjectString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); @@ -960,6 +1106,8 @@ class Schema { } bool Key(Context& context, const Ch* str, SizeType len, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str); + if (patternProperties_) { context.patternPropertiesSchemaCount = 0; for (SizeType i = 0; i < patternPropertyCount_; i++) @@ -1011,6 +1159,7 @@ class Schema { } bool EndObject(Context& context, SizeType memberCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject"); if (hasRequired_) { context.error_handler.StartMissingProperties(); for (SizeType index = 0; index < propertyCount_; index++) @@ -1058,6 +1207,7 @@ class Schema { } bool StartArray(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray"); context.arrayElementIndex = 0; context.inArray = true; // Ensure we note that we are in an array @@ -1070,6 +1220,7 @@ class Schema { } bool EndArray(Context& context, SizeType elementCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray"); context.inArray = false; if (elementCount < minItems_) { @@ -1118,6 +1269,9 @@ class Schema { case kValidateErrorAnyOf: return GetAnyOfString(); case kValidateErrorNot: return GetNotString(); + case kValidateErrorReadOnly: return GetReadOnlyString(); + case kValidateErrorWriteOnly: return GetWriteOnlyString(); + default: return GetNullString(); } } @@ -1165,15 +1319,14 @@ class Schema { RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a') RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f') RAPIDJSON_STRING_(Id, 'i', 'd') - - RAPIDJSON_STRING_(SchemeEnd, ':') - RAPIDJSON_STRING_(AuthStart, '/', '/') - RAPIDJSON_STRING_(QueryStart, '?') - RAPIDJSON_STRING_(FragStart, '#') - RAPIDJSON_STRING_(Slash, '/') - RAPIDJSON_STRING_(Dot, '.') + RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r') + RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i') + RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e') #undef RAPIDJSON_STRING_ @@ -1307,6 +1460,7 @@ class Schema { // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required. // Also creates a hasher for enums and array uniqueness, if required. + // Also a useful place to add type-independent error checks. bool CreateParallelValidator(Context& context) const { if (enum_ || context.arrayUniqueness) context.hasher = context.factory.CreateHasher(); @@ -1337,6 +1491,16 @@ class Schema { } } + // Add any other type-independent checks here + if (readOnly_ && (context.flags & kValidateWriteFlag)) { + context.error_handler.DisallowedWhenWriting(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly); + } + if (writeOnly_ && (context.flags & kValidateReadFlag)) { + context.error_handler.DisallowedWhenReading(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly); + } + return true; } @@ -1359,6 +1523,14 @@ class Schema { return false; } + bool CheckBool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return true; + } + bool CheckInt(Context& context, int64_t i) const { if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { DisallowedType(context, GetIntegerString()); @@ -1524,6 +1696,7 @@ class Schema { AllocatorType* allocator_; SValue uri_; UriType id_; + Specification spec_; PointerType pointer_; const SchemaType* typeless_; uint64_t* enum_; @@ -1568,6 +1741,10 @@ class Schema { bool exclusiveMaximum_; SizeType defaultValueLength_; + + bool readOnly_; + bool writeOnly_; + bool nullable_; }; template @@ -1614,7 +1791,13 @@ class IGenericRemoteSchemaDocumentProvider { virtual ~IGenericRemoteSchemaDocumentProvider() {} virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; - virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri uri) { return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); } + virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri uri, Specification& spec) { + // Default implementation just calls through for compatibility + // Following line suppresses unused parameter warning + (void)spec; + // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi); + return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); + } }; /////////////////////////////////////////////////////////////////////////////// @@ -1656,10 +1839,12 @@ class GenericSchemaDocument { \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. \param allocator An optional allocator instance for allocating memory. Can be null. \param pointer An optional JSON pointer to the start of the schema document + \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04. */ explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, - const PointerType& pointer = PointerType()) : // PR #1393 + const PointerType& pointer = PointerType(), // PR #1393 + const Specification& spec = Specification(kDraft04)) : remoteProvider_(remoteProvider), allocator_(allocator), ownAllocator_(), @@ -1667,9 +1852,11 @@ class GenericSchemaDocument { typeless_(), schemaMap_(allocator, kInitialSchemaMapSize), schemaRef_(allocator, kInitialSchemaRefSize), + spec_(spec), error_(kObjectType), currentError_() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument"); if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); @@ -1680,6 +1867,10 @@ class GenericSchemaDocument { typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); + // Establish the schema draft or open api version. + // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document. + SetSchemaSpecification(document); + // Generate root schema, it will call CreateSchema() to create sub-schemas, // And call HandleRefSchema() if there are $ref. // PR #1393 use input pointer if supplied @@ -1713,6 +1904,7 @@ class GenericSchemaDocument { schemaRef_(std::move(rhs.schemaRef_)), uri_(std::move(rhs.uri_)), docId_(std::move(rhs.docId_)), + spec_(rhs.spec_), error_(std::move(rhs.error_)), currentError_(std::move(rhs.currentError_)) { @@ -1743,6 +1935,23 @@ class GenericSchemaDocument { const GValue& GetURI() const { return uri_; } + const Specification& GetSpecification() const { return spec_; } + bool IsSupportedSpecification() const { return spec_.IsSupported(); } + + //! Static method to get the specification of any schema document + // Returns kDraftNone if document is silent + static const Specification GetSpecification(const ValueType& document) { + SchemaDraft draft = GetSchemaDraft(document); + if (draft != kDraftNone) + return Specification(draft); + else { + OpenApiVersion oapi = GetOpenApiVersion(document); + if (oapi != kVersionNone) + return Specification(oapi); + } + return Specification(kDraftNone); + } + //! Get the root schema. const SchemaType& GetRoot() const { return *root_; } @@ -1761,6 +1970,10 @@ class GenericSchemaDocument { case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString(); case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString(); case kSchemaErrorRegexInvalid: return GetRegexInvalidString(); + case kSchemaErrorSpecUnknown: return GetSpecUnknownString(); + case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString(); + case kSchemaErrorSpecIllegal: return GetSpecIllegalString(); + case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString(); default: return GetNullString(); } } @@ -1829,6 +2042,7 @@ class GenericSchemaDocument { } void AddCurrentError(const SchemaErrorCode code, const PointerType& location) { + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code)); currentError_.AddMember(GetErrorCodeString(), code, *allocator_); AddErrorInstanceLocation(currentError_, location); AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_); @@ -1847,6 +2061,9 @@ class GenericSchemaDocument { RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't') RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd') + RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l') RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n') RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e') RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd') @@ -1855,10 +2072,94 @@ class GenericSchemaDocument { RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l') RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r') RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd') #undef RAPIDJSON_STRING_ + // Static method to get schema draft of any schema document + static SchemaDraft GetSchemaDraft(const ValueType& document) { + static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + + if (!document.IsObject()) { + return kDraftNone; + } + + // Get the schema draft from the $schema keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kDraftUnknown; + const UriType draftUri(itr->value); + // Check base uri for match + if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04; + if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05; + if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06; + if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07; + if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03; + if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09; + if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12; + return kDraftUnknown; + } + // $schema not found + return kDraftNone; + } + + + // Get open api version of any schema document + static OpenApiVersion GetOpenApiVersion(const ValueType& document) { + static const Ch kVersion20String[] = { '2', '.', '0', '\0' }; + static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level + static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level + static SizeType len = internal::StrLen(kVersion30String); + + if (!document.IsObject()) { + return kVersionNone; + } + + // Get the open api version from the swagger / openapi keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString()); + if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kVersionUnknown; + const ValueType kVersion20Value(kVersion20String); + if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly + const ValueType kVersion30Value(kVersion30String); + if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x + const ValueType kVersion31Value(kVersion31String); + if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x + return kVersionUnknown; + } + // swagger or openapi not found + return kVersionNone; + } + + // Get the draft of the schema or the open api version (which implies the draft). + // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on. + void SetSchemaSpecification(const ValueType& document) { + // Look for '$schema', 'swagger' or 'openapi' keyword at document root + SchemaDraft docDraft = GetSchemaDraft(document); + OpenApiVersion docOapi = GetOpenApiVersion(document); + // Error if both in document + if (docDraft != kDraftNone && docOapi != kVersionNone) + SchemaError(kSchemaErrorSpecIllegal, PointerType()); + // Use document draft or open api version if present or use spec from constructor + if (docDraft != kDraftNone) + spec_ = Specification(docDraft); + else if (docOapi != kVersionNone) + spec_ = Specification(docOapi); + // Error if draft or version unknown + if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown) + SchemaError(kSchemaErrorSpecUnknown, PointerType()); + else if (!spec_.IsSupported()) + SchemaError(kSchemaErrorSpecUnsupported, PointerType()); + } + // Changed by PR #1393 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { if (v.GetType() == kObjectType) { @@ -1875,6 +2176,9 @@ class GenericSchemaDocument { // Changed by PR #1393 const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { RAPIDJSON_ASSERT(pointer.IsValid()); + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString()); if (v.IsObject()) { if (const SchemaType* sc = GetSchema(pointer)) { if (schema) @@ -1904,6 +2208,9 @@ class GenericSchemaDocument { if (itr == v.MemberEnd()) return false; + GenericStringBuffer sb; + source.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString()); // Resolve the source pointer to the $ref'ed schema (finally) new (schemaRef_.template Push()) SchemaRefPtr(&source); @@ -1915,6 +2222,7 @@ class GenericSchemaDocument { // First resolve $ref against the in-scope id UriType scopeId = UriType(id, allocator_); UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString()); // See if the resolved $ref minus the fragment matches a resolved id in this document // Search from the root. Returns the subschema in the document and its absolute JSON pointer. PointerType basePointer = PointerType(); @@ -1924,7 +2232,7 @@ class GenericSchemaDocument { if (!remoteProvider_) SchemaError(kSchemaErrorRefNoRemoteProvider, source); else { - if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) { const Ch* s = ref.GetFragString(); len = ref.GetFragStringLength(); if (len <= 1 || s[1] == '/') { @@ -1979,10 +2287,13 @@ class GenericSchemaDocument { } } else { // Plain name fragment, relative to the resolved URI + // Not supported in open api 2.0 and 3.0 PointerType pointer(allocator_); + if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30) + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); // See if the fragment matches an id in this document. // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. - if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { + else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { if (IsCyclicRef(pointer)) SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); else { @@ -2024,6 +2335,7 @@ class GenericSchemaDocument { } // See if it matches if (localuri.Match(finduri, full)) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString()); resval = const_cast(&doc); resptr = here; return resval; @@ -2050,6 +2362,7 @@ class GenericSchemaDocument { // Added by PR #1393 void AddSchemaRefs(SchemaType* schema) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs"); while (!schemaRef_.Empty()) { SchemaRefPtr *ref = schemaRef_.template Pop(1); SchemaEntry *entry = schemaMap_.template Push(); @@ -2093,6 +2406,7 @@ class GenericSchemaDocument { internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved GValue uri_; // Schema document URI UriType docId_; + Specification spec_; GValue error_; GValue currentError_; }; @@ -2158,11 +2472,10 @@ class GenericSchemaValidator : currentError_(), missingDependents_(), valid_(true), - flags_(kValidateDefaultFlags) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(0) -#endif + flags_(kValidateDefaultFlags), + depth_(0) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator"); } //! Constructor with output handler. @@ -2190,11 +2503,10 @@ class GenericSchemaValidator : currentError_(), missingDependents_(), valid_(true), - flags_(kValidateDefaultFlags) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(0) -#endif + flags_(kValidateDefaultFlags), + depth_(0) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)"); } //! Destructor. @@ -2455,6 +2767,14 @@ class GenericSchemaValidator : currentError_.SetObject(); AddCurrentError(kValidateErrorNot); } + void DisallowedWhenWriting() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorReadOnly); + } + void DisallowedWhenReading() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorWriteOnly); + } #define RAPIDJSON_STRING_(name, ...) \ static const StringRefType& Get##name##String() {\ @@ -2477,21 +2797,12 @@ class GenericSchemaValidator : #undef RAPIDJSON_STRING_ -#if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ -RAPIDJSON_MULTILINEMACRO_BEGIN\ - *documentStack_.template Push() = '\0';\ - documentStack_.template Pop(1);\ - internal::PrintInvalidDocument(documentStack_.template Bottom());\ -RAPIDJSON_MULTILINEMACRO_END -#else -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() -#endif - #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ if (!valid_) return false; \ if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ - RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom());\ valid_ = false;\ return valid_;\ } @@ -2530,6 +2841,7 @@ RAPIDJSON_MULTILINEMACRO_END { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } bool StartObject() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject"); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); valid_ = !outputHandler_ || outputHandler_->StartObject(); @@ -2537,6 +2849,7 @@ RAPIDJSON_MULTILINEMACRO_END } bool Key(const Ch* str, SizeType len, bool copy) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str); if (!valid_) return false; AppendToken(str, len); if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) { @@ -2549,6 +2862,7 @@ RAPIDJSON_MULTILINEMACRO_END } bool EndObject(SizeType memberCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject"); if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) { @@ -2559,6 +2873,7 @@ RAPIDJSON_MULTILINEMACRO_END } bool StartArray() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray"); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); valid_ = !outputHandler_ || outputHandler_->StartArray(); @@ -2566,6 +2881,7 @@ RAPIDJSON_MULTILINEMACRO_END } bool EndArray(SizeType elementCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray"); if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) { @@ -2575,17 +2891,16 @@ RAPIDJSON_MULTILINEMACRO_END RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); } -#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ // Implementation of ISchemaStateFactory virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), -#if RAPIDJSON_SCHEMA_VERBOSE depth_ + 1, -#endif &GetStateAllocator()); sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag); return sv; @@ -2629,9 +2944,7 @@ RAPIDJSON_MULTILINEMACRO_END const SchemaDocumentType& schemaDocument, const SchemaType& root, const char* basePath, size_t basePathSize, -#if RAPIDJSON_SCHEMA_VERBOSE unsigned depth, -#endif StateAllocator* allocator = 0, size_t schemaStackCapacity = kDefaultSchemaStackCapacity, size_t documentStackCapacity = kDefaultDocumentStackCapacity) @@ -2647,11 +2960,10 @@ RAPIDJSON_MULTILINEMACRO_END currentError_(), missingDependents_(), valid_(true), - flags_(kValidateDefaultFlags) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(depth) -#endif + flags_(kValidateDefaultFlags), + depth_(depth) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : ""); if (basePath && basePathSize) memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); } @@ -2667,6 +2979,7 @@ RAPIDJSON_MULTILINEMACRO_END } bool BeginValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue"); if (schemaStack_.Empty()) PushSchema(root_); else { @@ -2699,17 +3012,15 @@ RAPIDJSON_MULTILINEMACRO_END } bool EndValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue"); if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) return false; -#if RAPIDJSON_SCHEMA_VERBOSE GenericStringBuffer sb; - schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); - + schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb); *documentStack_.template Push() = '\0'; documentStack_.template Pop(1); - internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); -#endif + RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom(), depth_); void* hasher = CurrentContext().hasher; uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; @@ -2760,7 +3071,7 @@ RAPIDJSON_MULTILINEMACRO_END } } - RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema); } + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema, flags_); } RAPIDJSON_FORCEINLINE void PopSchema() { Context* c = schemaStack_.template Pop(1); @@ -2862,9 +3173,7 @@ RAPIDJSON_MULTILINEMACRO_END ValueType missingDependents_; bool valid_; unsigned flags_; -#if RAPIDJSON_SCHEMA_VERBOSE unsigned depth_; -#endif }; typedef GenericSchemaValidator SchemaValidator; diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgMath.h b/libfenrir/src/main/jni/thorvg/src/lib/tvgMath.h index 6120216d7..74f34fb74 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgMath.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgMath.h @@ -53,6 +53,12 @@ static inline bool mathRightAngle(const Matrix* m) } +static inline bool mathSkewed(const Matrix* m) +{ + return (fabsf(m->e21 + m->e12) > FLT_EPSILON); +} + + static inline bool mathIdentity(const Matrix* m) { if (!mathEqual(m->e11, 1.0f) || !mathZero(m->e12) || !mathZero(m->e13) || diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgPaint.cpp b/libfenrir/src/main/jni/thorvg/src/lib/tvgPaint.cpp index d0e908ddf..984f24e16 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgPaint.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgPaint.cpp @@ -38,9 +38,9 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, if (rTransform) rTransform->update(); - //No rotational. - if (pTransform && !mathRightAngle(&pTransform->m)) return false; - if (rTransform && !mathRightAngle(&rTransform->m)) return false; + //No rotation and no skewing + if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) return false; + if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) return false; //Perpendicular Rectangle? auto pt1 = pts + 0; diff --git a/retrofit/build.gradle b/retrofit/build.gradle deleted file mode 100644 index fbdb9c239..000000000 --- a/retrofit/build.gradle +++ /dev/null @@ -1,35 +0,0 @@ -plugins { - id("com.android.library") - id("kotlin-android") -} - -android { - namespace "com.retrofit2" - compileSdk = appCompileSDK - buildToolsVersion = appBuildTools - - defaultConfig { - minSdk = appMinSDK - targetSdk = appTargetSDK - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - encoding = "utf-8" - } - kotlinOptions { - jvmTarget = "1.8" - } -} - -dependencies { - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") - implementation("org.jetbrains.kotlin:kotlin-parcelize-runtime:$kotlin_version") - implementation("org.jetbrains.kotlin:kotlin-android-extensions-runtime:$kotlin_version") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines") - compileOnly("org.jetbrains.kotlin:kotlin-annotations-jvm:$kotlin_version") - implementation("com.squareup.okio:okio:$okioVersion") - implementation("com.squareup.okhttp3:okhttp-android:$okhttpLibraryVersion") - implementation("androidx.annotation:annotation:$annotationVersion") -} diff --git a/retrofit/src/main/java/retrofit2/BuiltInConverters.java b/retrofit/src/main/java/retrofit2/BuiltInConverters.java deleted file mode 100644 index 30aee2e2a..000000000 --- a/retrofit/src/main/java/retrofit2/BuiltInConverters.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; - -import kotlin.Unit; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; -import retrofit2.http.Streaming; - -final class BuiltInConverters extends Converter.Factory { - - @Override - public @Nullable - Converter responseBodyConverter( - Type type, Annotation[] annotations, Retrofit retrofit) { - if (type == ResponseBody.class) { - return Utils.isAnnotationPresent(annotations, Streaming.class) - ? StreamingResponseBodyConverter.INSTANCE - : BufferingResponseBodyConverter.INSTANCE; - } - if (type == Void.class) { - return VoidResponseBodyConverter.INSTANCE; - } - if (Utils.isUnit(type)) { - return UnitResponseBodyConverter.INSTANCE; - } - return null; - } - - @Override - public @Nullable - Converter requestBodyConverter( - Type type, - Annotation[] parameterAnnotations, - Annotation[] methodAnnotations, - Retrofit retrofit) { - if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) { - return RequestBodyConverter.INSTANCE; - } - return null; - } - - static final class VoidResponseBodyConverter implements Converter { - static final VoidResponseBodyConverter INSTANCE = new VoidResponseBodyConverter(); - - @Override - public Void convert(ResponseBody value) { - value.close(); - return null; - } - } - - static final class UnitResponseBodyConverter implements Converter { - static final UnitResponseBodyConverter INSTANCE = new UnitResponseBodyConverter(); - - @Override - public Unit convert(ResponseBody value) { - value.close(); - return Unit.INSTANCE; - } - } - - static final class RequestBodyConverter implements Converter { - static final RequestBodyConverter INSTANCE = new RequestBodyConverter(); - - @Override - public RequestBody convert(RequestBody value) { - return value; - } - } - - static final class StreamingResponseBodyConverter - implements Converter { - static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter(); - - @Override - public ResponseBody convert(ResponseBody value) { - return value; - } - } - - static final class BufferingResponseBodyConverter - implements Converter { - static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter(); - - @Override - public ResponseBody convert(ResponseBody value) throws IOException { - try { - // Buffer the entire body to avoid future I/O. - return Utils.buffer(value); - } finally { - value.close(); - } - } - } - - static final class ToStringConverter implements Converter { - static final ToStringConverter INSTANCE = new ToStringConverter(); - - @Override - public String convert(Object value) { - return value.toString(); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/Call.java b/retrofit/src/main/java/retrofit2/Call.java deleted file mode 100644 index 9342fd220..000000000 --- a/retrofit/src/main/java/retrofit2/Call.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.NonNull; - -import java.io.IOException; - -import okhttp3.Request; -import okio.Timeout; - -/** - * An invocation of a Retrofit method that sends a request to a webserver and returns a response. - * Each call yields its own HTTP request and response pair. Use {@link #clone} to make multiple - * calls with the same parameters to the same webserver; this may be used to implement polling or to - * retry a failed call. - * - *

Calls may be executed synchronously with {@link #execute}, or asynchronously with {@link - * #enqueue}. In either case the call can be canceled at any time with {@link #cancel}. A call that - * is busy writing its request or reading its response may receive a {@link IOException}; this is - * working as designed. - * - * @param Successful response body type. - */ -public interface Call extends Cloneable { - /** - * Synchronously send the request and return its response. - * - * @throws IOException if a problem occurred talking to the server. - * @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request or - * decoding the response. - */ - Response execute() throws IOException; - - /** - * Asynchronously send the request and notify {@code callback} of its response or if an error - * occurred talking to the server, creating the request, or processing the response. - */ - void enqueue(Callback callback); - - /** - * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain - * #enqueue(Callback) enqueued}. It is an error to execute or enqueue a call more than once. - */ - boolean isExecuted(); - - /** - * Cancel this call. An attempt will be made to cancel in-flight calls, and if the call has not - * yet been executed it never will be. - */ - void cancel(); - - /** - * True if {@link #cancel()} was called. - */ - boolean isCanceled(); - - /** - * Create a new, identical call to this one which can be enqueued or executed even if this call - * has already been. - */ - @NonNull - Call clone(); - - /** - * The original HTTP request. - */ - Request request(); - - /** - * Returns a timeout that spans the entire call: resolving DNS, connecting, writing the request - * body, server processing, and reading the response body. If the call requires redirects or - * retries all must complete within one timeout period. - */ - Timeout timeout(); -} diff --git a/retrofit/src/main/java/retrofit2/CallAdapter.java b/retrofit/src/main/java/retrofit2/CallAdapter.java deleted file mode 100644 index e0ce21b12..000000000 --- a/retrofit/src/main/java/retrofit2/CallAdapter.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.Nullable; - -import java.lang.annotation.Annotation; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -/** - * Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are - * created by {@linkplain Factory a factory} which is {@linkplain - * Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit} instance. - */ -public interface CallAdapter { - /** - * Returns the value type that this adapter uses when converting the HTTP response body to a Java - * object. For example, the response type for {@code Call} is {@code Repo}. This type is - * used to prepare the {@code call} passed to {@code #adapt}. - * - *

Note: This is typically not the same type as the {@code returnType} provided to this call - * adapter's factory. - */ - Type responseType(); - - /** - * Returns an instance of {@code T} which delegates to {@code call}. - * - *

For example, given an instance for a hypothetical utility, {@code Async}, this instance - * would return a new {@code Async} which invoked {@code call} when run. - * - *


-     * @Override
-     * public <R> Async<R> adapt(final Call<R> call) {
-     *   return Async.create(new Callable<Response<R>>() {
-     *     @Override
-     *     public Response<R> call() throws Exception {
-     *       return call.execute();
-     *     }
-     *   });
-     * }
-     * 
- */ - T adapt(Call call); - - /** - * Creates {@link CallAdapter} instances based on the return type of {@linkplain - * Retrofit#create(Class) the service interface} methods. - */ - abstract class Factory { - /** - * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For - * example, index 1 of {@code Map} returns {@code Runnable}. - */ - protected static Type getParameterUpperBound(int index, ParameterizedType type) { - return Utils.getParameterUpperBound(index, type); - } - - /** - * Extract the raw class type from {@code type}. For example, the type representing {@code - * List} returns {@code List.class}. - */ - protected static Class getRawType(Type type) { - return Utils.getRawType(type); - } - - /** - * Returns a call adapter for interface methods that return {@code returnType}, or null if it - * cannot be handled by this factory. - */ - public abstract @Nullable - CallAdapter get( - Type returnType, Annotation[] annotations, Retrofit retrofit); - } -} diff --git a/retrofit/src/main/java/retrofit2/Callback.java b/retrofit/src/main/java/retrofit2/Callback.java deleted file mode 100644 index d644d808e..000000000 --- a/retrofit/src/main/java/retrofit2/Callback.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2012 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -/** - * Communicates responses from a server or offline requests. One and only one method will be invoked - * in response to a given request. - * - *

Callback methods are executed using the {@link Retrofit} callback executor. When none is - * specified, the following defaults are used: - * - *

    - *
  • Android: Callbacks are executed on the application's main (UI) thread. - *
  • JVM: Callbacks are executed on the background thread which performed the request. - *
- * - * @param Successful response body type. - */ -public interface Callback { - /** - * Invoked for a received HTTP response. - * - *

Note: An HTTP response may still indicate an application-level failure such as a 404 or 500. - * Call {@link Response#isSuccessful()} to determine if the response indicates success. - */ - void onResponse(Call call, Response response); - - /** - * Invoked when a network exception occurred talking to the server or when an unexpected exception - * occurred creating the request or processing the response. - */ - void onFailure(Call call, Throwable t); -} diff --git a/retrofit/src/main/java/retrofit2/CompletableFutureCallAdapterFactory.java b/retrofit/src/main/java/retrofit2/CompletableFutureCallAdapterFactory.java deleted file mode 100644 index 7cfd0899e..000000000 --- a/retrofit/src/main/java/retrofit2/CompletableFutureCallAdapterFactory.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2016 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import android.annotation.TargetApi; - -import androidx.annotation.Nullable; - -import java.lang.annotation.Annotation; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.concurrent.CompletableFuture; - -// Only added when CompletableFuture is available (Java 8+ / Android API 24+). -@TargetApi(24) -final class CompletableFutureCallAdapterFactory extends CallAdapter.Factory { - @Override - public @Nullable - CallAdapter get( - Type returnType, Annotation[] annotations, Retrofit retrofit) { - if (getRawType(returnType) != CompletableFuture.class) { - return null; - } - if (!(returnType instanceof ParameterizedType)) { - throw new IllegalStateException( - "CompletableFuture return type must be parameterized" - + " as CompletableFuture or CompletableFuture"); - } - Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType); - - if (getRawType(innerType) != Response.class) { - // Generic type is not Response. Use it for body-only adapter. - return new BodyCallAdapter<>(innerType); - } - - // Generic type is Response. Extract T and create the Response version of the adapter. - if (!(innerType instanceof ParameterizedType)) { - throw new IllegalStateException( - "Response must be parameterized" + " as Response or Response"); - } - Type responseType = getParameterUpperBound(0, (ParameterizedType) innerType); - return new ResponseCallAdapter<>(responseType); - } - - private static final class BodyCallAdapter implements CallAdapter> { - private final Type responseType; - - BodyCallAdapter(Type responseType) { - this.responseType = responseType; - } - - @Override - public Type responseType() { - return responseType; - } - - @Override - public CompletableFuture adapt(Call call) { - CompletableFuture future = new CallCancelCompletableFuture<>(call); - call.enqueue(new BodyCallback(future)); - return future; - } - - private class BodyCallback implements Callback { - private final CompletableFuture future; - - public BodyCallback(CompletableFuture future) { - this.future = future; - } - - @Override - public void onResponse(Call call, Response response) { - if (response.isSuccessful()) { - future.complete(response.body()); - } else { - future.completeExceptionally(new HttpException(response)); - } - } - - @Override - public void onFailure(Call call, Throwable t) { - future.completeExceptionally(t); - } - } - } - - private static final class ResponseCallAdapter - implements CallAdapter>> { - private final Type responseType; - - ResponseCallAdapter(Type responseType) { - this.responseType = responseType; - } - - @Override - public Type responseType() { - return responseType; - } - - @Override - public CompletableFuture> adapt(Call call) { - CompletableFuture> future = new CallCancelCompletableFuture<>(call); - call.enqueue(new ResponseCallback(future)); - return future; - } - - private class ResponseCallback implements Callback { - private final CompletableFuture> future; - - public ResponseCallback(CompletableFuture> future) { - this.future = future; - } - - @Override - public void onResponse(Call call, Response response) { - future.complete(response); - } - - @Override - public void onFailure(Call call, Throwable t) { - future.completeExceptionally(t); - } - } - } - - private static final class CallCancelCompletableFuture extends CompletableFuture { - private final Call call; - - CallCancelCompletableFuture(Call call) { - this.call = call; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - if (mayInterruptIfRunning) { - call.cancel(); - } - return super.cancel(mayInterruptIfRunning); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/Converter.java b/retrofit/src/main/java/retrofit2/Converter.java deleted file mode 100644 index e0607ba2c..000000000 --- a/retrofit/src/main/java/retrofit2/Converter.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2012 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -import okhttp3.RequestBody; -import okhttp3.ResponseBody; -import retrofit2.http.Body; -import retrofit2.http.Field; -import retrofit2.http.FieldMap; -import retrofit2.http.Header; -import retrofit2.http.HeaderMap; -import retrofit2.http.Part; -import retrofit2.http.PartMap; -import retrofit2.http.Path; -import retrofit2.http.Query; -import retrofit2.http.QueryMap; - -/** - * Convert objects to and from their representation in HTTP. Instances are created by {@linkplain - * Factory a factory} which is {@linkplain Retrofit.Builder#addConverterFactory(Factory) installed} - * into the {@link Retrofit} instance. - */ -public interface Converter { - @Nullable - T convert(F value) throws IOException; - - /** - * Creates {@link Converter} instances based on a type and target usage. - */ - abstract class Factory { - /** - * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For - * example, index 1 of {@code Map} returns {@code Runnable}. - */ - protected static Type getParameterUpperBound(int index, ParameterizedType type) { - return Utils.getParameterUpperBound(index, type); - } - - /** - * Extract the raw class type from {@code type}. For example, the type representing {@code - * List} returns {@code List.class}. - */ - protected static Class getRawType(Type type) { - return Utils.getRawType(type); - } - - /** - * Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null if - * {@code type} cannot be handled by this factory. This is used to create converters for - * response types such as {@code SimpleResponse} from a {@code Call} - * declaration. - */ - public @Nullable - Converter responseBodyConverter( - Type type, Annotation[] annotations, Retrofit retrofit) { - return null; - } - - /** - * Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if - * {@code type} cannot be handled by this factory. This is used to create converters for types - * specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap} values. - */ - public @Nullable - Converter requestBodyConverter( - Type type, - Annotation[] parameterAnnotations, - Annotation[] methodAnnotations, - Retrofit retrofit) { - return null; - } - - /** - * Returns a {@link Converter} for converting {@code type} to a {@link String}, or null if - * {@code type} cannot be handled by this factory. This is used to create converters for types - * specified by {@link Field @Field}, {@link FieldMap @FieldMap} values, {@link Header @Header}, - * {@link HeaderMap @HeaderMap}, {@link Path @Path}, {@link Query @Query}, and {@link - * QueryMap @QueryMap} values. - */ - public @Nullable - Converter stringConverter( - Type type, Annotation[] annotations, Retrofit retrofit) { - return null; - } - } -} diff --git a/retrofit/src/main/java/retrofit2/DefaultCallAdapterFactory.java b/retrofit/src/main/java/retrofit2/DefaultCallAdapterFactory.java deleted file mode 100644 index 1502ed04c..000000000 --- a/retrofit/src/main/java/retrofit2/DefaultCallAdapterFactory.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Objects; -import java.util.concurrent.Executor; - -import okhttp3.Request; -import okio.Timeout; - -final class DefaultCallAdapterFactory extends CallAdapter.Factory { - private final @Nullable - Executor callbackExecutor; - - DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) { - this.callbackExecutor = callbackExecutor; - } - - @Override - public @Nullable - CallAdapter get( - Type returnType, Annotation[] annotations, Retrofit retrofit) { - if (getRawType(returnType) != Call.class) { - return null; - } - if (!(returnType instanceof ParameterizedType)) { - throw new IllegalArgumentException( - "Call return type must be parameterized as Call or Call"); - } - Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType); - - Executor executor = - Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) - ? null - : callbackExecutor; - - return new CallAdapter>() { - @Override - public Type responseType() { - return responseType; - } - - @Override - public Call adapt(Call call) { - return executor == null ? call : new ExecutorCallbackCall<>(executor, call); - } - }; - } - - static final class ExecutorCallbackCall implements Call { - final Executor callbackExecutor; - final Call delegate; - - ExecutorCallbackCall(Executor callbackExecutor, Call delegate) { - this.callbackExecutor = callbackExecutor; - this.delegate = delegate; - } - - @Override - public void enqueue(Callback callback) { - Objects.requireNonNull(callback, "callback == null"); - - delegate.enqueue( - new Callback() { - @Override - public void onResponse(Call call, Response response) { - callbackExecutor.execute( - () -> { - if (delegate.isCanceled()) { - // Emulate OkHttp's behavior of throwing/delivering an IOException on - // cancellation. - callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); - } else { - callback.onResponse(ExecutorCallbackCall.this, response); - } - }); - } - - @Override - public void onFailure(Call call, Throwable t) { - callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t)); - } - }); - } - - @Override - public boolean isExecuted() { - return delegate.isExecuted(); - } - - @Override - public Response execute() throws IOException { - return delegate.execute(); - } - - @Override - public void cancel() { - delegate.cancel(); - } - - @Override - public boolean isCanceled() { - return delegate.isCanceled(); - } - - @NonNull - @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone. - @Override - public Call clone() { - return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone()); - } - - @Override - public Request request() { - return delegate.request(); - } - - @Override - public Timeout timeout() { - return delegate.timeout(); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/HttpException.java b/retrofit/src/main/java/retrofit2/HttpException.java deleted file mode 100644 index 2812138ba..000000000 --- a/retrofit/src/main/java/retrofit2/HttpException.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2016 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.Nullable; - -import java.util.Objects; - -/** - * Exception for an unexpected, non-2xx HTTP response. - */ -public class HttpException extends RuntimeException { - private final int code; - private final String message; - private final transient Response response; - - public HttpException(Response response) { - super(getMessage(response)); - code = response.code(); - message = response.message(); - this.response = response; - } - - private static String getMessage(Response response) { - Objects.requireNonNull(response, "response == null"); - return "HTTP " + response.code() + " " + response.message(); - } - - /** - * HTTP status code. - */ - public int code() { - return code; - } - - /** - * HTTP status message. - */ - public String message() { - return message; - } - - /** - * The full HTTP response. This may be null if the exception was serialized. - */ - public @Nullable - Response response() { - return response; - } -} diff --git a/retrofit/src/main/java/retrofit2/HttpServiceMethod.java b/retrofit/src/main/java/retrofit2/HttpServiceMethod.java deleted file mode 100644 index dcf082451..000000000 --- a/retrofit/src/main/java/retrofit2/HttpServiceMethod.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import static retrofit2.Utils.getRawType; -import static retrofit2.Utils.methodError; - -import androidx.annotation.Nullable; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -import kotlin.Unit; -import kotlin.coroutines.Continuation; -import okhttp3.OkHttpClient; -import okhttp3.ResponseBody; - -/** - * Adapts an invocation of an interface method into an HTTP call. - */ -abstract class HttpServiceMethod extends ServiceMethod { - private final RequestFactory requestFactory; - private final OkHttpClient callFactory; - private final Converter responseConverter; - - HttpServiceMethod( - RequestFactory requestFactory, - OkHttpClient callFactory, - Converter responseConverter) { - this.requestFactory = requestFactory; - this.callFactory = callFactory; - this.responseConverter = responseConverter; - } - - /** - * Inspects the annotations on an interface method to construct a reusable service method that - * speaks HTTP. This requires potentially-expensive reflection so it is best to build each service - * method only once and reuse it. - */ - static HttpServiceMethod parseAnnotations( - Retrofit retrofit, Method method, RequestFactory requestFactory) { - boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; - boolean continuationWantsResponse = false; - boolean continuationBodyNullable = false; - boolean continuationIsUnit = false; - - Annotation[] annotations = method.getAnnotations(); - Type adapterType; - if (isKotlinSuspendFunction) { - Type[] parameterTypes = method.getGenericParameterTypes(); - Type responseType = - Utils.getParameterLowerBound( - 0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]); - if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) { - // Unwrap the actual body type from Response. - responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType); - continuationWantsResponse = true; - } else { - continuationIsUnit = Utils.isUnit(responseType); - // TODO figure out if type is nullable or not - // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class) - // Find the entry for method - // Determine if return type is nullable or not - } - - adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType); - annotations = SkipCallbackExecutorImpl.ensurePresent(annotations); - } else { - adapterType = method.getGenericReturnType(); - } - - CallAdapter callAdapter = - createCallAdapter(retrofit, method, adapterType, annotations); - Type responseType = callAdapter.responseType(); - if (responseType == okhttp3.Response.class) { - throw methodError( - method, - "'" - + getRawType(responseType).getName() - + "' is not a valid response body type. Did you mean ResponseBody?"); - } - if (responseType == Response.class) { - throw methodError(method, "Response must include generic type (e.g., Response)"); - } - // TODO support Unit for Kotlin? - if (requestFactory.httpMethod.equals("HEAD") - && !Void.class.equals(responseType) - && !Utils.isUnit(responseType)) { - throw methodError(method, "HEAD method must use Void or Unit as response type."); - } - - Converter responseConverter = - createResponseConverter(retrofit, method, responseType); - - OkHttpClient callFactory = retrofit.callFactory; - if (!isKotlinSuspendFunction) { - return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); - } else if (continuationWantsResponse) { - //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. - return (HttpServiceMethod) - new SuspendForResponse<>( - requestFactory, - callFactory, - responseConverter, - (CallAdapter>) callAdapter); - } else { - //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. - return (HttpServiceMethod) - new SuspendForBody<>( - requestFactory, - callFactory, - responseConverter, - (CallAdapter>) callAdapter, - continuationBodyNullable, - continuationIsUnit); - } - } - - private static CallAdapter createCallAdapter( - Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) { - try { - //noinspection unchecked - return (CallAdapter) retrofit.callAdapter(returnType, annotations); - } catch (RuntimeException e) { // Wide exception range because factories are user code. - throw methodError(method, e, "Unable to create call adapter for %s", returnType); - } - } - - private static Converter createResponseConverter( - Retrofit retrofit, Method method, Type responseType) { - Annotation[] annotations = method.getAnnotations(); - try { - return retrofit.responseBodyConverter(responseType, annotations); - } catch (RuntimeException e) { // Wide exception range because factories are user code. - throw methodError(method, e, "Unable to create converter for %s", responseType); - } - } - - @Override - final @Nullable - ReturnT invoke(Object[] args) { - Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); - return adapt(call, args); - } - - protected abstract @Nullable - ReturnT adapt(Call call, Object[] args); - - static final class CallAdapted extends HttpServiceMethod { - private final CallAdapter callAdapter; - - CallAdapted( - RequestFactory requestFactory, - OkHttpClient callFactory, - Converter responseConverter, - CallAdapter callAdapter) { - super(requestFactory, callFactory, responseConverter); - this.callAdapter = callAdapter; - } - - @Override - protected ReturnT adapt(Call call, Object[] args) { - return callAdapter.adapt(call); - } - } - - static final class SuspendForResponse extends HttpServiceMethod { - private final CallAdapter> callAdapter; - - SuspendForResponse( - RequestFactory requestFactory, - OkHttpClient callFactory, - Converter responseConverter, - CallAdapter> callAdapter) { - super(requestFactory, callFactory, responseConverter); - this.callAdapter = callAdapter; - } - - @Override - protected Object adapt(Call call, Object[] args) { - call = callAdapter.adapt(call); - - //noinspection unchecked Checked by reflection inside RequestFactory. - Continuation> continuation = - (Continuation>) args[args.length - 1]; - - // See SuspendForBody for explanation about this try/catch. - try { - return KotlinExtensions.awaitResponse(call, continuation); - } catch (Exception e) { - return KotlinExtensions.suspendAndThrow(e, continuation); - } - } - } - - static final class SuspendForBody extends HttpServiceMethod { - private final CallAdapter> callAdapter; - private final boolean isNullable; - private final boolean isUnit; - - SuspendForBody( - RequestFactory requestFactory, - OkHttpClient callFactory, - Converter responseConverter, - CallAdapter> callAdapter, - boolean isNullable, - boolean isUnit) { - super(requestFactory, callFactory, responseConverter); - this.callAdapter = callAdapter; - this.isNullable = isNullable; - this.isUnit = isUnit; - } - - @Override - protected Object adapt(Call call, Object[] args) { - call = callAdapter.adapt(call); - - //noinspection unchecked Checked by reflection inside RequestFactory. - Continuation continuation = (Continuation) args[args.length - 1]; - - // Calls to OkHttp Call.enqueue() like those inside await and awaitNullable can sometimes - // invoke the supplied callback with an exception before the invoking stack frame can return. - // Coroutines will intercept the subsequent invocation of the Continuation and throw the - // exception synchronously. A Java Proxy cannot throw checked exceptions without them being - // declared on the interface method. To avoid the synchronous checked exception being wrapped - // in an UndeclaredThrowableException, it is intercepted and supplied to a helper which will - // force suspension to occur so that it can be instead delivered to the continuation to - // bypass this restriction. - try { - if (isUnit) { - //noinspection unchecked Checked by isUnit - return KotlinExtensions.awaitUnit((Call) call, (Continuation) continuation); - } else if (isNullable) { - return KotlinExtensions.awaitNullable(call, continuation); - } else { - return KotlinExtensions.await(call, continuation); - } - } catch (Exception e) { - return KotlinExtensions.suspendAndThrow(e, continuation); - } - } - } -} diff --git a/retrofit/src/main/java/retrofit2/Invocation.java b/retrofit/src/main/java/retrofit2/Invocation.java deleted file mode 100644 index 5eca0cfd6..000000000 --- a/retrofit/src/main/java/retrofit2/Invocation.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.NonNull; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * A single invocation of a Retrofit service interface method. This class captures both the method - * that was called and the arguments to the method. - * - *

Retrofit automatically adds an invocation to each OkHttp request as a tag. You can retrieve - * the invocation in an OkHttp interceptor for metrics and monitoring. - * - *


- * class InvocationLogger implements Interceptor {
- *   @Override public Response intercept(Chain chain) throws IOException {
- *     Request request = chain.request();
- *     Invocation invocation = request.tag(Invocation.class);
- *     if (invocation != null) {
- *       System.out.printf("%s.%s %s%n",
- *           invocation.method().getDeclaringClass().getSimpleName(),
- *           invocation.method().getName(), invocation.arguments());
- *     }
- *     return chain.proceed(request);
- *   }
- * }
- * 
- * - * Note: use caution when examining an invocation's arguments. Although the - * arguments list is unmodifiable, the arguments themselves may be mutable. They may also be unsafe - * for concurrent access. For best results declare Retrofit service interfaces using only immutable - * types for parameters! - */ -public final class Invocation { - private final Method method; - private final List arguments; - - /** - * Trusted constructor assumes ownership of {@code arguments}. - */ - Invocation(Method method, List arguments) { - this.method = method; - this.arguments = Collections.unmodifiableList(arguments); - } - - public static Invocation of(Method method, List arguments) { - Objects.requireNonNull(method, "method == null"); - Objects.requireNonNull(arguments, "arguments == null"); - return new Invocation(method, new ArrayList<>(arguments)); // Defensive copy. - } - - public Method method() { - return method; - } - - public List arguments() { - return arguments; - } - - @NonNull - @Override - public String toString() { - return String.format( - "%s.%s() %s", method.getDeclaringClass().getName(), method.getName(), arguments); - } -} diff --git a/retrofit/src/main/java/retrofit2/KotlinExtensions.kt b/retrofit/src/main/java/retrofit2/KotlinExtensions.kt deleted file mode 100644 index 306ebf18c..000000000 --- a/retrofit/src/main/java/retrofit2/KotlinExtensions.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2018 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:JvmName("KotlinExtensions") - -package retrofit2 - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.suspendCancellableCoroutine -import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED -import kotlin.coroutines.intrinsics.intercepted -import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException - -inline fun Retrofit.create(): T = create(T::class.java) - -suspend fun Call.await(): T { - return suspendCancellableCoroutine { continuation -> - continuation.invokeOnCancellation { - cancel() - } - enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful) { - val body = response.body() - if (body == null) { - val invocation = call.request().tag(Invocation::class.java) ?: return - val method = invocation.method() - val e = KotlinNullPointerException( - "Response from " + - method.declaringClass.name + - '.' + - method.name + - " was null but response body type was declared as non-null" - ) - continuation.resumeWithException(e) - } else { - continuation.resume(body) - } - } else { - continuation.resumeWithException(HttpException(response)) - } - } - - override fun onFailure(call: Call, t: Throwable) { - continuation.resumeWithException(t) - } - }) - } -} - -@JvmName("awaitNullable") -suspend fun Call.await(): T? { - return suspendCancellableCoroutine { continuation -> - continuation.invokeOnCancellation { - cancel() - } - enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful) { - continuation.resume(response.body()) - } else { - continuation.resumeWithException(HttpException(response)) - } - } - - override fun onFailure(call: Call, t: Throwable) { - continuation.resumeWithException(t) - } - }) - } -} - -@JvmName("awaitUnit") -suspend fun Call.await() { - @Suppress("UNCHECKED_CAST") - (this as Call).await() -} - -suspend fun Call.awaitResponse(): Response { - return suspendCancellableCoroutine { continuation -> - continuation.invokeOnCancellation { - cancel() - } - enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - continuation.resume(response) - } - - override fun onFailure(call: Call, t: Throwable) { - continuation.resumeWithException(t) - } - }) - } -} - -/** - * Force the calling coroutine to suspend before throwing [this]. - * - * This is needed when a checked exception is synchronously caught in a [java.lang.reflect.Proxy] - * invocation to avoid being wrapped in [java.lang.reflect.UndeclaredThrowableException]. - * - * The implementation is derived from: - * https://github.com/Kotlin/kotlinx.coroutines/pull/1667#issuecomment-556106349 - */ -internal suspend fun Exception.suspendAndThrow(): Nothing { - suspendCoroutineUninterceptedOrReturn { continuation -> - Dispatchers.Default.dispatch(continuation.context) { - continuation.intercepted().resumeWithException(this@suspendAndThrow) - } - COROUTINE_SUSPENDED - } -} diff --git a/retrofit/src/main/java/retrofit2/OkHttpCall.java b/retrofit/src/main/java/retrofit2/OkHttpCall.java deleted file mode 100644 index b4004758d..000000000 --- a/retrofit/src/main/java/retrofit2/OkHttpCall.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import static retrofit2.Utils.throwIfFatal; - -import androidx.annotation.GuardedBy; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.util.Objects; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.ResponseBody; -import okio.Buffer; -import okio.BufferedSource; -import okio.ForwardingSource; -import okio.Okio; -import okio.Timeout; - -final class OkHttpCall implements Call { - private final RequestFactory requestFactory; - private final Object[] args; - private final OkHttpClient callFactory; - private final Converter responseConverter; - - private volatile boolean canceled; - - @GuardedBy("this") - private @Nullable - okhttp3.Call rawCall; - - @GuardedBy("this") // Either a RuntimeException, non-fatal Error, or IOException. - private @Nullable - Throwable creationFailure; - - @GuardedBy("this") - private boolean executed; - - OkHttpCall( - RequestFactory requestFactory, - Object[] args, - OkHttpClient callFactory, - Converter responseConverter) { - this.requestFactory = requestFactory; - this.args = args; - this.callFactory = callFactory; - this.responseConverter = responseConverter; - } - - @NonNull - @SuppressWarnings("CloneDoesntCallSuperClone") - // We are a final type & this saves clearing state. - @Override - public OkHttpCall clone() { - return new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); - } - - @Override - public synchronized Request request() { - try { - return getRawCall().request(); - } catch (IOException e) { - throw new RuntimeException("Unable to create request.", e); - } - } - - @Override - public synchronized Timeout timeout() { - try { - return getRawCall().timeout(); - } catch (IOException e) { - throw new RuntimeException("Unable to create call.", e); - } - } - - /** - * Returns the raw call, initializing it if necessary. Throws if initializing the raw call throws, - * or has thrown in previous attempts to create it. - */ - @GuardedBy("this") - private okhttp3.Call getRawCall() throws IOException { - okhttp3.Call call = rawCall; - if (call != null) return call; - - // Re-throw previous failures if this isn't the first attempt. - if (creationFailure != null) { - if (creationFailure instanceof IOException) { - throw (IOException) creationFailure; - } else if (creationFailure instanceof RuntimeException) { - throw (RuntimeException) creationFailure; - } else { - throw (Error) creationFailure; - } - } - - // Create and remember either the success or the failure. - try { - return rawCall = createRawCall(); - } catch (RuntimeException | Error | IOException e) { - throwIfFatal(e); // Do not assign a fatal error to creationFailure. - creationFailure = e; - throw e; - } - } - - @Override - public void enqueue(Callback callback) { - Objects.requireNonNull(callback, "callback == null"); - - okhttp3.Call call; - Throwable failure; - - synchronized (this) { - if (executed) throw new IllegalStateException("Already executed."); - executed = true; - - call = rawCall; - failure = creationFailure; - if (call == null && failure == null) { - try { - call = rawCall = createRawCall(); - } catch (Throwable t) { - throwIfFatal(t); - failure = creationFailure = t; - } - } - } - - if (failure != null) { - callback.onFailure(this, failure); - return; - } - - if (canceled) { - call.cancel(); - } - - call.enqueue( - new okhttp3.Callback() { - @Override - public void onResponse(@NonNull okhttp3.Call call, @NonNull okhttp3.Response rawResponse) { - Response response; - try { - response = parseResponse(rawResponse); - } catch (Throwable e) { - throwIfFatal(e); - callFailure(e); - return; - } - - try { - callback.onResponse(OkHttpCall.this, response); - } catch (Throwable t) { - throwIfFatal(t); - t.printStackTrace(); // TODO this is not great - } - } - - @Override - public void onFailure(@NonNull okhttp3.Call call, @NonNull IOException e) { - callFailure(e); - } - - private void callFailure(Throwable e) { - try { - callback.onFailure(OkHttpCall.this, e); - } catch (Throwable t) { - throwIfFatal(t); - t.printStackTrace(); // TODO this is not great - } - } - }); - } - - @Override - public synchronized boolean isExecuted() { - return executed; - } - - @Override - public Response execute() throws IOException { - okhttp3.Call call; - - synchronized (this) { - if (executed) throw new IllegalStateException("Already executed."); - executed = true; - - call = getRawCall(); - } - - if (canceled) { - call.cancel(); - } - - return parseResponse(call.execute()); - } - - private okhttp3.Call createRawCall() throws IOException { - okhttp3.Call call = callFactory.newCall(requestFactory.create(args)); - if (call == null) { - throw new NullPointerException("Call.Factory returned null."); - } - return call; - } - - Response parseResponse(okhttp3.Response rawResponse) throws IOException { - ResponseBody rawBody = rawResponse.body(); - - // Remove the body's source (the only stateful object) so we can pass the response along. - rawResponse = - rawResponse - .newBuilder() - .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) - .build(); - - int code = rawResponse.code(); - if (code < 200 || code >= 300) { - try { - // Buffer the entire body to avoid future I/O. - ResponseBody bufferedBody = Utils.buffer(rawBody); - return Response.error(bufferedBody, rawResponse); - } finally { - rawBody.close(); - } - } - - if (code == 204 || code == 205) { - rawBody.close(); - return Response.success(null, rawResponse); - } - - ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody); - try { - T body = responseConverter.convert(catchingBody); - return Response.success(body, rawResponse); - } catch (RuntimeException e) { - // If the underlying source threw an exception, propagate that rather than indicating it was - // a runtime exception. - catchingBody.throwIfCaught(); - throw e; - } - } - - @Override - public void cancel() { - canceled = true; - - okhttp3.Call call; - synchronized (this) { - call = rawCall; - } - if (call != null) { - call.cancel(); - } - } - - @Override - public boolean isCanceled() { - if (canceled) { - return true; - } - synchronized (this) { - return rawCall != null && rawCall.isCanceled(); - } - } - - static final class NoContentResponseBody extends ResponseBody { - private final @Nullable - MediaType contentType; - private final long contentLength; - - NoContentResponseBody(@Nullable MediaType contentType, long contentLength) { - this.contentType = contentType; - this.contentLength = contentLength; - } - - @Override - public MediaType contentType() { - return contentType; - } - - @Override - public long contentLength() { - return contentLength; - } - - @NonNull - @Override - public BufferedSource source() { - throw new IllegalStateException("Cannot read raw response body of a converted body."); - } - } - - static final class ExceptionCatchingResponseBody extends ResponseBody { - private final ResponseBody delegate; - private final BufferedSource delegateSource; - @Nullable - IOException thrownException; - - ExceptionCatchingResponseBody(ResponseBody delegate) { - this.delegate = delegate; - delegateSource = - Okio.buffer( - new ForwardingSource(delegate.source()) { - @Override - public long read(@NonNull Buffer sink, long byteCount) throws IOException { - try { - return super.read(sink, byteCount); - } catch (IOException e) { - thrownException = e; - throw e; - } - } - }); - } - - @Override - public MediaType contentType() { - return delegate.contentType(); - } - - @Override - public long contentLength() { - return delegate.contentLength(); - } - - @NonNull - @Override - public BufferedSource source() { - return delegateSource; - } - - @Override - public void close() { - delegate.close(); - } - - void throwIfCaught() throws IOException { - if (thrownException != null) { - throw thrownException; - } - } - } -} diff --git a/retrofit/src/main/java/retrofit2/OptionalConverterFactory.java b/retrofit/src/main/java/retrofit2/OptionalConverterFactory.java deleted file mode 100644 index 1a15ea114..000000000 --- a/retrofit/src/main/java/retrofit2/OptionalConverterFactory.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2017 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import android.annotation.TargetApi; - -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Optional; - -import okhttp3.ResponseBody; - -// Only added when Optional is available (Java 8+ / Android API 24+). -@TargetApi(24) -final class OptionalConverterFactory extends Converter.Factory { - @Override - public @Nullable - Converter responseBodyConverter( - Type type, Annotation[] annotations, Retrofit retrofit) { - if (getRawType(type) != Optional.class) { - return null; - } - - Type innerType = getParameterUpperBound(0, (ParameterizedType) type); - Converter delegate = - retrofit.responseBodyConverter(innerType, annotations); - return new OptionalConverter<>(delegate); - } - - static final class OptionalConverter implements Converter> { - final Converter delegate; - - OptionalConverter(Converter delegate) { - this.delegate = delegate; - } - - @Override - public Optional convert(ResponseBody value) throws IOException { - return Optional.ofNullable(delegate.convert(value)); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/ParameterHandler.java b/retrofit/src/main/java/retrofit2/ParameterHandler.java deleted file mode 100644 index 4847c019a..000000000 --- a/retrofit/src/main/java/retrofit2/ParameterHandler.java +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.util.Map; -import java.util.Objects; - -import okhttp3.MultipartBody; -import okhttp3.RequestBody; - -abstract class ParameterHandler { - abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException; - - final ParameterHandler> iterable() { - return new ParameterHandler>() { - @Override - void apply(RequestBuilder builder, @Nullable Iterable values) throws IOException { - if (values == null) return; // Skip null values. - - for (T value : values) { - ParameterHandler.this.apply(builder, value); - } - } - }; - } - - final ParameterHandler array() { - return new ParameterHandler() { - @Override - void apply(RequestBuilder builder, @Nullable Object values) throws IOException { - if (values == null) return; // Skip null values. - - for (int i = 0, size = Array.getLength(values); i < size; i++) { - //noinspection unchecked - ParameterHandler.this.apply(builder, (T) Array.get(values, i)); - } - } - }; - } - - static final class RelativeUrl extends ParameterHandler { - private final Method method; - private final int p; - - RelativeUrl(Method method, int p) { - this.method = method; - this.p = p; - } - - @Override - void apply(RequestBuilder builder, @Nullable Object value) { - if (value == null) { - throw Utils.parameterError(method, p, "@Url parameter is null."); - } - builder.setRelativeUrl(value); - } - } - - static final class Header extends ParameterHandler { - private final String name; - private final Converter valueConverter; - private final boolean allowUnsafeNonAsciiValues; - - Header(String name, Converter valueConverter, boolean allowUnsafeNonAsciiValues) { - this.name = Objects.requireNonNull(name, "name == null"); - this.valueConverter = valueConverter; - this.allowUnsafeNonAsciiValues = allowUnsafeNonAsciiValues; - } - - @Override - void apply(RequestBuilder builder, @Nullable T value) throws IOException { - if (value == null) return; // Skip null values. - - String headerValue = valueConverter.convert(value); - if (headerValue == null) return; // Skip converted but null values. - - builder.addHeader(name, headerValue, allowUnsafeNonAsciiValues); - } - } - - static final class Path extends ParameterHandler { - private final Method method; - private final int p; - private final String name; - private final Converter valueConverter; - private final boolean encoded; - - Path(Method method, int p, String name, Converter valueConverter, boolean encoded) { - this.method = method; - this.p = p; - this.name = Objects.requireNonNull(name, "name == null"); - this.valueConverter = valueConverter; - this.encoded = encoded; - } - - @Override - void apply(RequestBuilder builder, @Nullable T value) throws IOException { - if (value == null) { - throw Utils.parameterError( - method, p, "Path parameter \"" + name + "\" value must not be null."); - } - builder.addPathParam(name, valueConverter.convert(value), encoded); - } - } - - static final class Query extends ParameterHandler { - private final String name; - private final Converter valueConverter; - private final boolean encoded; - - Query(String name, Converter valueConverter, boolean encoded) { - this.name = Objects.requireNonNull(name, "name == null"); - this.valueConverter = valueConverter; - this.encoded = encoded; - } - - @Override - void apply(RequestBuilder builder, @Nullable T value) throws IOException { - if (value == null) return; // Skip null values. - - String queryValue = valueConverter.convert(value); - if (queryValue == null) return; // Skip converted but null values - - builder.addQueryParam(name, queryValue, encoded); - } - } - - static final class QueryName extends ParameterHandler { - private final Converter nameConverter; - private final boolean encoded; - - QueryName(Converter nameConverter, boolean encoded) { - this.nameConverter = nameConverter; - this.encoded = encoded; - } - - @Override - void apply(RequestBuilder builder, @Nullable T value) throws IOException { - if (value == null) return; // Skip null values. - builder.addQueryParam(nameConverter.convert(value), null, encoded); - } - } - - static final class QueryMap extends ParameterHandler> { - private final Method method; - private final int p; - private final Converter valueConverter; - private final boolean encoded; - - QueryMap(Method method, int p, Converter valueConverter, boolean encoded) { - this.method = method; - this.p = p; - this.valueConverter = valueConverter; - this.encoded = encoded; - } - - @Override - void apply(RequestBuilder builder, @Nullable Map value) throws IOException { - if (value == null) { - throw Utils.parameterError(method, p, "Query map was null"); - } - - for (Map.Entry entry : value.entrySet()) { - String entryKey = entry.getKey(); - if (entryKey == null) { - throw Utils.parameterError(method, p, "Query map contained null key."); - } - T entryValue = entry.getValue(); - if (entryValue == null) { - throw Utils.parameterError( - method, p, "Query map contained null value for key '" + entryKey + "'."); - } - - String convertedEntryValue = valueConverter.convert(entryValue); - if (convertedEntryValue == null) { - throw Utils.parameterError( - method, - p, - "Query map value '" - + entryValue - + "' converted to null by " - + valueConverter.getClass().getName() - + " for key '" - + entryKey - + "'."); - } - - builder.addQueryParam(entryKey, convertedEntryValue, encoded); - } - } - } - - static final class HeaderMap extends ParameterHandler> { - private final Method method; - private final int p; - private final Converter valueConverter; - private final boolean allowUnsafeNonAsciiValues; - - HeaderMap(Method method, int p, Converter valueConverter, boolean allowUnsafeNonAsciiValues) { - this.method = method; - this.p = p; - this.valueConverter = valueConverter; - this.allowUnsafeNonAsciiValues = allowUnsafeNonAsciiValues; - } - - @Override - void apply(RequestBuilder builder, @Nullable Map value) throws IOException { - if (value == null) { - throw Utils.parameterError(method, p, "Header map was null."); - } - - for (Map.Entry entry : value.entrySet()) { - String headerName = entry.getKey(); - if (headerName == null) { - throw Utils.parameterError(method, p, "Header map contained null key."); - } - T headerValue = entry.getValue(); - if (headerValue == null) { - throw Utils.parameterError( - method, p, "Header map contained null value for key '" + headerName + "'."); - } - builder.addHeader( - headerName, valueConverter.convert(headerValue), allowUnsafeNonAsciiValues); - } - } - } - - static final class Headers extends ParameterHandler { - private final Method method; - private final int p; - - Headers(Method method, int p) { - this.method = method; - this.p = p; - } - - @Override - void apply(RequestBuilder builder, @Nullable okhttp3.Headers headers) { - if (headers == null) { - throw Utils.parameterError(method, p, "Headers parameter must not be null."); - } - builder.addHeaders(headers); - } - } - - static final class Field extends ParameterHandler { - private final String name; - private final Converter valueConverter; - private final boolean encoded; - - Field(String name, Converter valueConverter, boolean encoded) { - this.name = Objects.requireNonNull(name, "name == null"); - this.valueConverter = valueConverter; - this.encoded = encoded; - } - - @Override - void apply(RequestBuilder builder, @Nullable T value) throws IOException { - if (value == null) return; // Skip null values. - - String fieldValue = valueConverter.convert(value); - if (fieldValue == null) return; // Skip null converted values - - builder.addFormField(name, fieldValue, encoded); - } - } - - static final class FieldMap extends ParameterHandler> { - private final Method method; - private final int p; - private final Converter valueConverter; - private final boolean encoded; - - FieldMap(Method method, int p, Converter valueConverter, boolean encoded) { - this.method = method; - this.p = p; - this.valueConverter = valueConverter; - this.encoded = encoded; - } - - @Override - void apply(RequestBuilder builder, @Nullable Map value) throws IOException { - if (value == null) { - throw Utils.parameterError(method, p, "Field map was null."); - } - - for (Map.Entry entry : value.entrySet()) { - String entryKey = entry.getKey(); - if (entryKey == null) { - throw Utils.parameterError(method, p, "Field map contained null key."); - } - T entryValue = entry.getValue(); - if (entryValue == null) { - throw Utils.parameterError( - method, p, "Field map contained null value for key '" + entryKey + "'."); - } - - String fieldEntry = valueConverter.convert(entryValue); - if (fieldEntry == null) { - throw Utils.parameterError( - method, - p, - "Field map value '" - + entryValue - + "' converted to null by " - + valueConverter.getClass().getName() - + " for key '" - + entryKey - + "'."); - } - - builder.addFormField(entryKey, fieldEntry, encoded); - } - } - } - - static final class Part extends ParameterHandler { - private final Method method; - private final int p; - private final okhttp3.Headers headers; - private final Converter converter; - - Part(Method method, int p, okhttp3.Headers headers, Converter converter) { - this.method = method; - this.p = p; - this.headers = headers; - this.converter = converter; - } - - @Override - void apply(RequestBuilder builder, @Nullable T value) { - if (value == null) return; // Skip null values. - - RequestBody body; - try { - body = converter.convert(value); - } catch (IOException e) { - throw Utils.parameterError(method, p, "Unable to convert " + value + " to RequestBody", e); - } - builder.addPart(headers, body); - } - } - - static final class RawPart extends ParameterHandler { - static final RawPart INSTANCE = new RawPart(); - - private RawPart() { - } - - @Override - void apply(RequestBuilder builder, @Nullable MultipartBody.Part value) { - if (value != null) { // Skip null values. - builder.addPart(value); - } - } - } - - static final class PartMap extends ParameterHandler> { - private final Method method; - private final int p; - private final Converter valueConverter; - private final String transferEncoding; - - PartMap( - Method method, int p, Converter valueConverter, String transferEncoding) { - this.method = method; - this.p = p; - this.valueConverter = valueConverter; - this.transferEncoding = transferEncoding; - } - - @Override - void apply(RequestBuilder builder, @Nullable Map value) throws IOException { - if (value == null) { - throw Utils.parameterError(method, p, "Part map was null."); - } - - for (Map.Entry entry : value.entrySet()) { - String entryKey = entry.getKey(); - if (entryKey == null) { - throw Utils.parameterError(method, p, "Part map contained null key."); - } - T entryValue = entry.getValue(); - if (entryValue == null) { - throw Utils.parameterError( - method, p, "Part map contained null value for key '" + entryKey + "'."); - } - - okhttp3.Headers headers = - okhttp3.Headers.of( - "Content-Disposition", - "form-data; name=\"" + entryKey + "\"", - "Content-Transfer-Encoding", - transferEncoding); - - builder.addPart(headers, valueConverter.convert(entryValue)); - } - } - } - - static final class Body extends ParameterHandler { - private final Method method; - private final int p; - private final Converter converter; - - Body(Method method, int p, Converter converter) { - this.method = method; - this.p = p; - this.converter = converter; - } - - @Override - void apply(RequestBuilder builder, @Nullable T value) { - if (value == null) { - throw Utils.parameterError(method, p, "Body parameter value must not be null."); - } - RequestBody body; - try { - body = converter.convert(value); - } catch (IOException e) { - throw Utils.parameterError(method, e, p, "Unable to convert " + value + " to RequestBody"); - } - builder.setBody(body); - } - } - - static final class Tag extends ParameterHandler { - final Class cls; - - Tag(Class cls) { - this.cls = cls; - } - - @Override - void apply(RequestBuilder builder, @Nullable T value) { - builder.addTag(cls, value); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/Platform.java b/retrofit/src/main/java/retrofit2/Platform.java deleted file mode 100644 index e5c50c55e..000000000 --- a/retrofit/src/main/java/retrofit2/Platform.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; - -import android.annotation.TargetApi; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; - -import androidx.annotation.ChecksSdkIntAtLeast; -import androidx.annotation.Nullable; - -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.util.List; -import java.util.concurrent.Executor; - -abstract class Platform { - private static final Platform PLATFORM = createPlatform(); - - static Platform get() { - return PLATFORM; - } - - private static Platform createPlatform() { - if (Android24.isSupported()) { - return new Android24(); - } - return new Android21(); - } - - abstract @Nullable - Executor defaultCallbackExecutor(); - - abstract List createDefaultCallAdapterFactories( - @Nullable Executor callbackExecutor); - - abstract List createDefaultConverterFactories(); - - abstract boolean isDefaultMethod(Method method); - - abstract @Nullable - Object invokeDefaultMethod( - Method method, Class declaringClass, Object proxy, Object... args) throws Throwable; - - static final class Android21 extends Platform { - @Override - boolean isDefaultMethod(Method method) { - return false; - } - - @Nullable - @Override - Object invokeDefaultMethod( - Method method, Class declaringClass, Object proxy, Object... args) { - throw new AssertionError(); - } - - @Override - Executor defaultCallbackExecutor() { - return MainThreadExecutor.INSTANCE; - } - - @Override - List createDefaultCallAdapterFactories( - @Nullable Executor callbackExecutor) { - return singletonList(new DefaultCallAdapterFactory(callbackExecutor)); - } - - @Override - List createDefaultConverterFactories() { - return emptyList(); - } - } - - // Only used on Android API 24+ - @TargetApi(24) - static final class Android24 extends Platform { - private @Nullable - Constructor lookupConstructor; - - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) - static boolean isSupported() { - return Build.VERSION.SDK_INT >= 24; - } - - @Override - Executor defaultCallbackExecutor() { - return MainThreadExecutor.INSTANCE; - } - - @Override - List createDefaultCallAdapterFactories( - @Nullable Executor callbackExecutor) { - return asList( - new CompletableFutureCallAdapterFactory(), - new DefaultCallAdapterFactory(callbackExecutor)); - } - - @Override - List createDefaultConverterFactories() { - return singletonList(new OptionalConverterFactory()); - } - - @Override - public boolean isDefaultMethod(Method method) { - return method.isDefault(); - } - - @Nullable - @Override - public Object invokeDefaultMethod( - Method method, Class declaringClass, Object proxy, Object... args) throws Throwable { - if (Build.VERSION.SDK_INT < 26) { - throw new UnsupportedOperationException( - "Calling default methods on API 24 and 25 is not supported"); - } - Constructor lookupConstructor = this.lookupConstructor; - if (lookupConstructor == null) { - lookupConstructor = Lookup.class.getDeclaredConstructor(Class.class, int.class); - lookupConstructor.setAccessible(true); - this.lookupConstructor = lookupConstructor; - } - return lookupConstructor - .newInstance(declaringClass, -1 /* trusted */) - .unreflectSpecial(method, declaringClass) - .bindTo(proxy) - .invokeWithArguments(args); - } - } - - private static final class MainThreadExecutor implements Executor { - static final Executor INSTANCE = new MainThreadExecutor(); - - private final Handler handler = new Handler(Looper.getMainLooper()); - - @Override - public void execute(Runnable r) { - handler.post(r); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/RequestBuilder.java b/retrofit/src/main/java/retrofit2/RequestBuilder.java deleted file mode 100644 index f9d776c2e..000000000 --- a/retrofit/src/main/java/retrofit2/RequestBuilder.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (C) 2012 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.retrofit2.BuildConfig; - -import java.io.IOException; -import java.util.regex.Pattern; - -import okhttp3.FormBody; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.Request; -import okhttp3.RequestBody; -import okio.Buffer; -import okio.BufferedSink; - -final class RequestBuilder { - private static final char[] HEX_DIGITS = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' - }; - private static final String PATH_SEGMENT_ALWAYS_ENCODE_SET = " \"<>^`{}|\\?#"; - - /** - * Matches strings that contain {@code .} or {@code ..} as a complete path segment. This also - * matches dots in their percent-encoded form, {@code %2E}. - * - *

It is okay to have these strings within a larger path segment (like {@code a..z} or {@code - * index.html}) but when alone they have a special meaning. A single dot resolves to no path - * segment so {@code /one/./three/} becomes {@code /one/three/}. A double-dot pops the preceding - * directory, so {@code /one/../three/} becomes {@code /three/}. - * - *

We forbid these in Retrofit paths because they're likely to have the unintended effect. For - * example, passing {@code ..} to {@code DELETE /account/book/{isbn}/} yields {@code DELETE - * /account/}. - */ - private static final Pattern PATH_TRAVERSAL = Pattern.compile("(.*/)?(\\.|%2e|%2E){1,2}(/.*)?"); - - private final String method; - - private final HttpUrl baseUrl; - private final Request.Builder requestBuilder; - private final Headers.Builder headersBuilder; - private final boolean hasBody; - private @Nullable - String relativeUrl; - private @Nullable - HttpUrl.Builder urlBuilder; - private @Nullable - MediaType contentType; - private @Nullable - MultipartBody.Builder multipartBuilder; - private @Nullable - FormBody.Builder formBuilder; - private @Nullable - RequestBody body; - - RequestBuilder( - String method, - HttpUrl baseUrl, - @Nullable String relativeUrl, - @Nullable Headers headers, - @Nullable MediaType contentType, - boolean hasBody, - boolean isFormEncoded, - boolean isMultipart) { - this.method = method; - this.baseUrl = baseUrl; - this.relativeUrl = relativeUrl; - requestBuilder = new Request.Builder(); - this.contentType = contentType; - this.hasBody = hasBody; - - if (headers != null) { - headersBuilder = headers.newBuilder(); - } else { - headersBuilder = new Headers.Builder(); - } - - if (isFormEncoded) { - // Will be set to 'body' in 'build'. - formBuilder = new FormBody.Builder(); - } else if (isMultipart) { - // Will be set to 'body' in 'build'. - multipartBuilder = new MultipartBody.Builder(); - multipartBuilder.setType(MultipartBody.FORM); - } - } - - private static String canonicalizeForPath(String input, boolean alreadyEncoded) { - int codePoint; - for (int i = 0, limit = input.length(); i < limit; i += Character.charCount(codePoint)) { - codePoint = input.codePointAt(i); - if (codePoint < 0x20 - || codePoint >= 0x7f - || PATH_SEGMENT_ALWAYS_ENCODE_SET.indexOf(codePoint) != -1 - || (!alreadyEncoded && (codePoint == '/' || codePoint == '%'))) { - // Slow path: the character at i requires encoding! - Buffer out = new Buffer(); - out.writeUtf8(input, 0, i); - canonicalizeForPath(out, input, i, limit, alreadyEncoded); - return out.readUtf8(); - } - } - - // Fast path: no characters required encoding. - return input; - } - - private static void canonicalizeForPath( - Buffer out, String input, int pos, int limit, boolean alreadyEncoded) { - Buffer utf8Buffer = null; // Lazily allocated. - int codePoint; - for (int i = pos; i < limit; i += Character.charCount(codePoint)) { - codePoint = input.codePointAt(i); - if (!alreadyEncoded - || (codePoint != '\t' && codePoint != '\n' && codePoint != '\f' && codePoint != '\r')) { - if (codePoint < 0x20 - || codePoint >= 0x7f - || PATH_SEGMENT_ALWAYS_ENCODE_SET.indexOf(codePoint) != -1 - || (!alreadyEncoded && (codePoint == '/' || codePoint == '%'))) { - // Percent encode this character. - if (utf8Buffer == null) { - utf8Buffer = new Buffer(); - } - utf8Buffer.writeUtf8CodePoint(codePoint); - try { - while (!utf8Buffer.exhausted()) { - int b = utf8Buffer.readByte() & 0xff; - out.writeByte('%'); - out.writeByte(HEX_DIGITS[(b >> 4) & 0xf]); - out.writeByte(HEX_DIGITS[b & 0xf]); - } - } catch (IOException e) { - if (BuildConfig.DEBUG) { - e.printStackTrace(); - } - } - } else { - // This character doesn't need encoding. Just copy it over. - out.writeUtf8CodePoint(codePoint); - } - } // Skip this character. - - } - } - - void setRelativeUrl(Object relativeUrl) { - this.relativeUrl = relativeUrl.toString(); - } - - void addHeader(String name, String value, boolean allowUnsafeNonAsciiValues) { - if ("Content-Type".equalsIgnoreCase(name)) { - try { - contentType = MediaType.get(value); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Malformed content type: " + value, e); - } - } else if (allowUnsafeNonAsciiValues) { - headersBuilder.addUnsafeNonAscii(name, value); - } else { - headersBuilder.add(name, value); - } - } - - void addHeaders(Headers headers) { - headersBuilder.addAll(headers); - } - - void addPathParam(String name, String value, boolean encoded) { - if (relativeUrl == null) { - // The relative URL is cleared when the first query parameter is set. - throw new AssertionError(); - } - String replacement = canonicalizeForPath(value, encoded); - String newRelativeUrl = relativeUrl.replace("{" + name + "}", replacement); - if (PATH_TRAVERSAL.matcher(newRelativeUrl).matches()) { - throw new IllegalArgumentException( - "@Path parameters shouldn't perform path traversal ('.' or '..'): " + value); - } - relativeUrl = newRelativeUrl; - } - - void addQueryParam(String name, @Nullable String value, boolean encoded) { - if (relativeUrl != null) { - // Do a one-time combination of the built relative URL and the base URL. - urlBuilder = baseUrl.newBuilder(relativeUrl); - if (urlBuilder == null) { - throw new IllegalArgumentException( - "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl); - } - relativeUrl = null; - } - - if (encoded) { - //noinspection ConstantConditions Checked to be non-null by above 'if' block. - urlBuilder.addEncodedQueryParameter(name, value); - } else { - //noinspection ConstantConditions Checked to be non-null by above 'if' block. - urlBuilder.addQueryParameter(name, value); - } - } - - @SuppressWarnings("ConstantConditions") - // Only called when isFormEncoded was true. - void addFormField(String name, String value, boolean encoded) { - if (encoded) { - formBuilder.addEncoded(name, value); - } else { - formBuilder.add(name, value); - } - } - - @SuppressWarnings("ConstantConditions") - // Only called when isMultipart was true. - void addPart(Headers headers, RequestBody body) { - multipartBuilder.addPart(headers, body); - } - - @SuppressWarnings("ConstantConditions") - // Only called when isMultipart was true. - void addPart(MultipartBody.Part part) { - multipartBuilder.addPart(part); - } - - void setBody(@Nullable RequestBody body) { - this.body = body; - } - - void addTag(Class cls, @Nullable T value) { - requestBuilder.tag(cls, value); - } - - Request.Builder get() { - HttpUrl url; - HttpUrl.Builder urlBuilder = this.urlBuilder; - if (urlBuilder != null) { - url = urlBuilder.build(); - } else { - // No query parameters triggered builder creation, just combine the relative URL and base URL. - //noinspection ConstantConditions Non-null if urlBuilder is null. - url = baseUrl.resolve(relativeUrl); - if (url == null) { - throw new IllegalArgumentException( - "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl); - } - } - - RequestBody body = this.body; - if (body == null) { - // Try to pull from one of the builders. - if (formBuilder != null) { - body = formBuilder.build(); - } else if (multipartBuilder != null) { - body = multipartBuilder.build(); - } else if (hasBody) { - // Body is absent, make an empty body. - body = RequestBody.create(new byte[0], null); - } - } - - MediaType contentType = this.contentType; - if (contentType != null) { - if (body != null) { - body = new ContentTypeOverridingRequestBody(body, contentType); - } else { - headersBuilder.add("Content-Type", contentType.toString()); - } - } - - return requestBuilder.url(url).headers(headersBuilder.build()).method(method, body); - } - - private static class ContentTypeOverridingRequestBody extends RequestBody { - private final RequestBody delegate; - private final MediaType contentType; - - ContentTypeOverridingRequestBody(RequestBody delegate, MediaType contentType) { - this.delegate = delegate; - this.contentType = contentType; - } - - @Override - public MediaType contentType() { - return contentType; - } - - @Override - public long contentLength() throws IOException { - return delegate.contentLength(); - } - - @Override - public void writeTo(@NonNull BufferedSink sink) throws IOException { - delegate.writeTo(sink); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/RequestFactory.java b/retrofit/src/main/java/retrofit2/RequestFactory.java deleted file mode 100644 index f86186dc2..000000000 --- a/retrofit/src/main/java/retrofit2/RequestFactory.java +++ /dev/null @@ -1,877 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import static retrofit2.Utils.methodError; -import static retrofit2.Utils.parameterError; - -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.net.URI; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import kotlin.coroutines.Continuation; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.RequestBody; -import retrofit2.http.Body; -import retrofit2.http.DELETE; -import retrofit2.http.Field; -import retrofit2.http.FieldMap; -import retrofit2.http.FormUrlEncoded; -import retrofit2.http.GET; -import retrofit2.http.HEAD; -import retrofit2.http.HTTP; -import retrofit2.http.Header; -import retrofit2.http.HeaderMap; -import retrofit2.http.Multipart; -import retrofit2.http.OPTIONS; -import retrofit2.http.PATCH; -import retrofit2.http.POST; -import retrofit2.http.PUT; -import retrofit2.http.Part; -import retrofit2.http.PartMap; -import retrofit2.http.Path; -import retrofit2.http.Query; -import retrofit2.http.QueryMap; -import retrofit2.http.QueryName; -import retrofit2.http.Tag; -import retrofit2.http.Url; - -final class RequestFactory { - final String httpMethod; - final boolean isKotlinSuspendFunction; - private final Method method; - private final HttpUrl baseUrl; - private final @Nullable - String relativeUrl; - private final @Nullable - Headers headers; - private final @Nullable - MediaType contentType; - private final boolean hasBody; - private final boolean isFormEncoded; - private final boolean isMultipart; - private final ParameterHandler[] parameterHandlers; - - RequestFactory(Builder builder) { - method = builder.method; - baseUrl = builder.retrofit.baseUrl; - httpMethod = builder.httpMethod; - relativeUrl = builder.relativeUrl; - headers = builder.headers; - contentType = builder.contentType; - hasBody = builder.hasBody; - isFormEncoded = builder.isFormEncoded; - isMultipart = builder.isMultipart; - parameterHandlers = builder.parameterHandlers; - isKotlinSuspendFunction = builder.isKotlinSuspendFunction; - } - - static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { - return new Builder(retrofit, method).build(); - } - - okhttp3.Request create(Object[] args) throws IOException { - @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types. - ParameterHandler[] handlers = (ParameterHandler[]) parameterHandlers; - - int argumentCount = args.length; - if (argumentCount != handlers.length) { - throw new IllegalArgumentException( - "Argument count (" - + argumentCount - + ") doesn't match expected count (" - + handlers.length - + ")"); - } - - RequestBuilder requestBuilder = - new RequestBuilder( - httpMethod, - baseUrl, - relativeUrl, - headers, - contentType, - hasBody, - isFormEncoded, - isMultipart); - - if (isKotlinSuspendFunction) { - // The Continuation is the last parameter and the handlers array contains null at that index. - argumentCount--; - } - - List argumentList = new ArrayList<>(argumentCount); - for (int p = 0; p < argumentCount; p++) { - argumentList.add(args[p]); - handlers[p].apply(requestBuilder, args[p]); - } - - return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build(); - } - - /** - * Inspects the annotations on an interface method to construct a reusable service method. This - * requires potentially-expensive reflection so it is best to build each service method only once - * and reuse it. Builders cannot be reused. - */ - static final class Builder { - // Upper and lower characters, digits, underscores, and hyphens, starting with a character. - private static final String PARAM = "[a-zA-Z][a-zA-Z\\d_-]*"; - private static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}"); - private static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM); - - final Retrofit retrofit; - final Method method; - final Annotation[] methodAnnotations; - final Annotation[][] parameterAnnotationsArray; - final Type[] parameterTypes; - - boolean gotField; - boolean gotPart; - boolean gotBody; - boolean gotPath; - boolean gotQuery; - boolean gotQueryName; - boolean gotQueryMap; - boolean gotUrl; - @Nullable - String httpMethod; - boolean hasBody; - boolean isFormEncoded; - boolean isMultipart; - @Nullable - String relativeUrl; - @Nullable - Headers headers; - @Nullable - MediaType contentType; - @Nullable - Set relativeUrlParamNames; - @Nullable - ParameterHandler[] parameterHandlers; - boolean isKotlinSuspendFunction; - - Builder(Retrofit retrofit, Method method) { - this.retrofit = retrofit; - this.method = method; - methodAnnotations = method.getAnnotations(); - parameterTypes = method.getGenericParameterTypes(); - parameterAnnotationsArray = method.getParameterAnnotations(); - } - - /** - * Gets the set of unique path parameters used in the given URI. If a parameter is used twice in - * the URI, it will only show up once in the set. - */ - static Set parsePathParameters(String path) { - Matcher m = PARAM_URL_REGEX.matcher(path); - Set patterns = new LinkedHashSet<>(); - while (m.find()) { - patterns.add(m.group(1)); - } - return patterns; - } - - private static Class boxIfPrimitive(Class type) { - if (boolean.class == type) return Boolean.class; - if (byte.class == type) return Byte.class; - if (char.class == type) return Character.class; - if (double.class == type) return Double.class; - if (float.class == type) return Float.class; - if (int.class == type) return Integer.class; - if (long.class == type) return Long.class; - if (short.class == type) return Short.class; - return type; - } - - RequestFactory build() { - for (Annotation annotation : methodAnnotations) { - parseMethodAnnotation(annotation); - } - - if (httpMethod == null) { - throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.)."); - } - - if (!hasBody) { - if (isMultipart) { - throw methodError( - method, - "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); - } - if (isFormEncoded) { - throw methodError( - method, - "FormUrlEncoded can only be specified on HTTP methods with " - + "request body (e.g., @POST)."); - } - } - - int parameterCount = parameterAnnotationsArray.length; - parameterHandlers = new ParameterHandler[parameterCount]; - for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) { - parameterHandlers[p] = - parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter); - } - - if (relativeUrl == null && !gotUrl) { - throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod); - } - if (!isFormEncoded && !isMultipart && !hasBody && gotBody) { - throw methodError(method, "Non-body HTTP method cannot contain @Body."); - } - if (isFormEncoded && !gotField) { - throw methodError(method, "Form-encoded method must contain at least one @Field."); - } - if (isMultipart && !gotPart) { - throw methodError(method, "Multipart method must contain at least one @Part."); - } - - return new RequestFactory(this); - } - - private void parseMethodAnnotation(Annotation annotation) { - if (annotation instanceof DELETE) { - parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false); - } else if (annotation instanceof GET) { - parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); - } else if (annotation instanceof HEAD) { - parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); - } else if (annotation instanceof PATCH) { - parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); - } else if (annotation instanceof POST) { - parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); - } else if (annotation instanceof PUT) { - parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true); - } else if (annotation instanceof OPTIONS) { - parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false); - } else if (annotation instanceof HTTP) { - HTTP http = (HTTP) annotation; - parseHttpMethodAndPath(http.method(), http.path(), http.hasBody()); - } else if (annotation instanceof retrofit2.http.Headers) { - retrofit2.http.Headers headers = (retrofit2.http.Headers) annotation; - String[] headersToParse = headers.value(); - if (headersToParse.length == 0) { - throw methodError(method, "@Headers annotation is empty."); - } - this.headers = parseHeaders(headersToParse, headers.allowUnsafeNonAsciiValues()); - } else if (annotation instanceof Multipart) { - if (isFormEncoded) { - throw methodError(method, "Only one encoding annotation is allowed."); - } - isMultipart = true; - } else if (annotation instanceof FormUrlEncoded) { - if (isMultipart) { - throw methodError(method, "Only one encoding annotation is allowed."); - } - isFormEncoded = true; - } - } - - private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) { - if (this.httpMethod != null) { - throw methodError( - method, - "Only one HTTP method is allowed. Found: %s and %s.", - this.httpMethod, - httpMethod); - } - this.httpMethod = httpMethod; - this.hasBody = hasBody; - - if (value.isEmpty()) { - return; - } - - // Get the relative URL path and existing query string, if present. - int question = value.indexOf('?'); - if (question != -1 && question < value.length() - 1) { - // Ensure the query string does not have any named parameters. - String queryParams = value.substring(question + 1); - Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams); - if (queryParamMatcher.find()) { - throw methodError( - method, - "URL query string \"%s\" must not have replace block. " - + "For dynamic query parameters use @Query.", - queryParams); - } - } - - relativeUrl = value; - relativeUrlParamNames = parsePathParameters(value); - } - - private Headers parseHeaders(String[] headers, boolean allowUnsafeNonAsciiValues) { - Headers.Builder builder = new Headers.Builder(); - for (String header : headers) { - int colon = header.indexOf(':'); - if (colon == -1 || colon == 0 || colon == header.length() - 1) { - throw methodError( - method, "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header); - } - String headerName = header.substring(0, colon); - String headerValue = header.substring(colon + 1).trim(); - if ("Content-Type".equalsIgnoreCase(headerName)) { - try { - contentType = MediaType.get(headerValue); - } catch (IllegalArgumentException e) { - throw methodError(method, e, "Malformed content type: %s", headerValue); - } - } else if (allowUnsafeNonAsciiValues) { - builder.addUnsafeNonAscii(headerName, headerValue); - } else { - builder.add(headerName, headerValue); - } - } - return builder.build(); - } - - private @Nullable - ParameterHandler parseParameter( - int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) { - ParameterHandler result = null; - if (annotations != null) { - for (Annotation annotation : annotations) { - ParameterHandler annotationAction = - parseParameterAnnotation(p, parameterType, annotations, annotation); - - if (annotationAction == null) { - continue; - } - - if (result != null) { - throw parameterError( - method, p, "Multiple Retrofit annotations found, only one allowed."); - } - - result = annotationAction; - } - } - - if (result == null) { - if (allowContinuation) { - try { - if (Utils.getRawType(parameterType) == Continuation.class) { - isKotlinSuspendFunction = true; - return null; - } - } catch (NoClassDefFoundError ignored) { - // Ignored - } - } - throw parameterError(method, p, "No Retrofit annotation found."); - } - - return result; - } - - @Nullable - private ParameterHandler parseParameterAnnotation( - int p, Type type, Annotation[] annotations, Annotation annotation) { - if (annotation instanceof Url) { - validateResolvableType(p, type); - if (gotUrl) { - throw parameterError(method, p, "Multiple @Url method annotations found."); - } - if (gotPath) { - throw parameterError(method, p, "@Path parameters may not be used with @Url."); - } - if (gotQuery) { - throw parameterError(method, p, "A @Url parameter must not come after a @Query."); - } - if (gotQueryName) { - throw parameterError(method, p, "A @Url parameter must not come after a @QueryName."); - } - if (gotQueryMap) { - throw parameterError(method, p, "A @Url parameter must not come after a @QueryMap."); - } - if (relativeUrl != null) { - throw parameterError(method, p, "@Url cannot be used with @%s URL", httpMethod); - } - - gotUrl = true; - - if (type == HttpUrl.class - || type == String.class - || type == URI.class - || (type instanceof Class && "android.net.Uri".equals(((Class) type).getName()))) { - return new ParameterHandler.RelativeUrl(method, p); - } else { - throw parameterError( - method, - p, - "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type."); - } - - } else if (annotation instanceof Path) { - validateResolvableType(p, type); - if (gotQuery) { - throw parameterError(method, p, "A @Path parameter must not come after a @Query."); - } - if (gotQueryName) { - throw parameterError(method, p, "A @Path parameter must not come after a @QueryName."); - } - if (gotQueryMap) { - throw parameterError(method, p, "A @Path parameter must not come after a @QueryMap."); - } - if (gotUrl) { - throw parameterError(method, p, "@Path parameters may not be used with @Url."); - } - if (relativeUrl == null) { - throw parameterError( - method, p, "@Path can only be used with relative url on @%s", httpMethod); - } - gotPath = true; - - Path path = (Path) annotation; - String name = path.value(); - validatePathName(p, name); - - Converter converter = retrofit.stringConverter(type, annotations); - return new ParameterHandler.Path<>(method, p, name, converter, path.encoded()); - - } else if (annotation instanceof Query) { - validateResolvableType(p, type); - Query query = (Query) annotation; - String name = query.value(); - boolean encoded = query.encoded(); - - Class rawParameterType = Utils.getRawType(type); - gotQuery = true; - if (Iterable.class.isAssignableFrom(rawParameterType)) { - if (!(type instanceof ParameterizedType)) { - throw parameterError( - method, - p, - rawParameterType.getSimpleName() - + " must include generic type (e.g., " - + rawParameterType.getSimpleName() - + ")"); - } - ParameterizedType parameterizedType = (ParameterizedType) type; - Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); - Converter converter = retrofit.stringConverter(iterableType, annotations); - return new ParameterHandler.Query<>(name, converter, encoded).iterable(); - } else if (rawParameterType.isArray()) { - Class arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); - Converter converter = - retrofit.stringConverter(arrayComponentType, annotations); - return new ParameterHandler.Query<>(name, converter, encoded).array(); - } else { - Converter converter = retrofit.stringConverter(type, annotations); - return new ParameterHandler.Query<>(name, converter, encoded); - } - - } else if (annotation instanceof QueryName) { - validateResolvableType(p, type); - QueryName query = (QueryName) annotation; - boolean encoded = query.encoded(); - - Class rawParameterType = Utils.getRawType(type); - gotQueryName = true; - if (Iterable.class.isAssignableFrom(rawParameterType)) { - if (!(type instanceof ParameterizedType)) { - throw parameterError( - method, - p, - rawParameterType.getSimpleName() - + " must include generic type (e.g., " - + rawParameterType.getSimpleName() - + ")"); - } - ParameterizedType parameterizedType = (ParameterizedType) type; - Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); - Converter converter = retrofit.stringConverter(iterableType, annotations); - return new ParameterHandler.QueryName<>(converter, encoded).iterable(); - } else if (rawParameterType.isArray()) { - Class arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); - Converter converter = - retrofit.stringConverter(arrayComponentType, annotations); - return new ParameterHandler.QueryName<>(converter, encoded).array(); - } else { - Converter converter = retrofit.stringConverter(type, annotations); - return new ParameterHandler.QueryName<>(converter, encoded); - } - - } else if (annotation instanceof QueryMap) { - validateResolvableType(p, type); - Class rawParameterType = Utils.getRawType(type); - gotQueryMap = true; - if (!Map.class.isAssignableFrom(rawParameterType)) { - throw parameterError(method, p, "@QueryMap parameter type must be Map."); - } - Type mapType = Utils.getSupertype(type, rawParameterType, Map.class); - if (!(mapType instanceof ParameterizedType)) { - throw parameterError( - method, p, "Map must include generic types (e.g., Map)"); - } - ParameterizedType parameterizedType = (ParameterizedType) mapType; - Type keyType = Utils.getParameterUpperBound(0, parameterizedType); - if (String.class != keyType) { - throw parameterError(method, p, "@QueryMap keys must be of type String: " + keyType); - } - Type valueType = Utils.getParameterUpperBound(1, parameterizedType); - Converter valueConverter = retrofit.stringConverter(valueType, annotations); - - return new ParameterHandler.QueryMap<>( - method, p, valueConverter, ((QueryMap) annotation).encoded()); - - } else if (annotation instanceof Header) { - validateResolvableType(p, type); - Header header = (Header) annotation; - String name = header.value(); - - Class rawParameterType = Utils.getRawType(type); - if (Iterable.class.isAssignableFrom(rawParameterType)) { - if (!(type instanceof ParameterizedType)) { - throw parameterError( - method, - p, - rawParameterType.getSimpleName() - + " must include generic type (e.g., " - + rawParameterType.getSimpleName() - + ")"); - } - ParameterizedType parameterizedType = (ParameterizedType) type; - Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); - Converter converter = retrofit.stringConverter(iterableType, annotations); - return new ParameterHandler.Header<>(name, converter, header.allowUnsafeNonAsciiValues()) - .iterable(); - } else if (rawParameterType.isArray()) { - Class arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); - Converter converter = - retrofit.stringConverter(arrayComponentType, annotations); - return new ParameterHandler.Header<>(name, converter, header.allowUnsafeNonAsciiValues()) - .array(); - } else { - Converter converter = retrofit.stringConverter(type, annotations); - return new ParameterHandler.Header<>(name, converter, header.allowUnsafeNonAsciiValues()); - } - - } else if (annotation instanceof HeaderMap) { - if (type == Headers.class) { - return new ParameterHandler.Headers(method, p); - } - - validateResolvableType(p, type); - Class rawParameterType = Utils.getRawType(type); - if (!Map.class.isAssignableFrom(rawParameterType)) { - throw parameterError(method, p, "@HeaderMap parameter type must be Map or Headers."); - } - Type mapType = Utils.getSupertype(type, rawParameterType, Map.class); - if (!(mapType instanceof ParameterizedType)) { - throw parameterError( - method, p, "Map must include generic types (e.g., Map)"); - } - ParameterizedType parameterizedType = (ParameterizedType) mapType; - Type keyType = Utils.getParameterUpperBound(0, parameterizedType); - if (String.class != keyType) { - throw parameterError(method, p, "@HeaderMap keys must be of type String: " + keyType); - } - Type valueType = Utils.getParameterUpperBound(1, parameterizedType); - Converter valueConverter = retrofit.stringConverter(valueType, annotations); - - return new ParameterHandler.HeaderMap<>( - method, p, valueConverter, ((HeaderMap) annotation).allowUnsafeNonAsciiValues()); - - } else if (annotation instanceof Field) { - validateResolvableType(p, type); - if (!isFormEncoded) { - throw parameterError(method, p, "@Field parameters can only be used with form encoding."); - } - Field field = (Field) annotation; - String name = field.value(); - boolean encoded = field.encoded(); - - gotField = true; - - Class rawParameterType = Utils.getRawType(type); - if (Iterable.class.isAssignableFrom(rawParameterType)) { - if (!(type instanceof ParameterizedType)) { - throw parameterError( - method, - p, - rawParameterType.getSimpleName() - + " must include generic type (e.g., " - + rawParameterType.getSimpleName() - + ")"); - } - ParameterizedType parameterizedType = (ParameterizedType) type; - Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); - Converter converter = retrofit.stringConverter(iterableType, annotations); - return new ParameterHandler.Field<>(name, converter, encoded).iterable(); - } else if (rawParameterType.isArray()) { - Class arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); - Converter converter = - retrofit.stringConverter(arrayComponentType, annotations); - return new ParameterHandler.Field<>(name, converter, encoded).array(); - } else { - Converter converter = retrofit.stringConverter(type, annotations); - return new ParameterHandler.Field<>(name, converter, encoded); - } - - } else if (annotation instanceof FieldMap) { - validateResolvableType(p, type); - if (!isFormEncoded) { - throw parameterError( - method, p, "@FieldMap parameters can only be used with form encoding."); - } - Class rawParameterType = Utils.getRawType(type); - if (!Map.class.isAssignableFrom(rawParameterType)) { - throw parameterError(method, p, "@FieldMap parameter type must be Map."); - } - Type mapType = Utils.getSupertype(type, rawParameterType, Map.class); - if (!(mapType instanceof ParameterizedType)) { - throw parameterError( - method, p, "Map must include generic types (e.g., Map)"); - } - ParameterizedType parameterizedType = (ParameterizedType) mapType; - Type keyType = Utils.getParameterUpperBound(0, parameterizedType); - if (String.class != keyType) { - throw parameterError(method, p, "@FieldMap keys must be of type String: " + keyType); - } - Type valueType = Utils.getParameterUpperBound(1, parameterizedType); - Converter valueConverter = retrofit.stringConverter(valueType, annotations); - - gotField = true; - return new ParameterHandler.FieldMap<>( - method, p, valueConverter, ((FieldMap) annotation).encoded()); - - } else if (annotation instanceof Part) { - validateResolvableType(p, type); - if (!isMultipart) { - throw parameterError( - method, p, "@Part parameters can only be used with multipart encoding."); - } - Part part = (Part) annotation; - gotPart = true; - - String partName = part.value(); - Class rawParameterType = Utils.getRawType(type); - if (partName.isEmpty()) { - if (Iterable.class.isAssignableFrom(rawParameterType)) { - if (!(type instanceof ParameterizedType)) { - throw parameterError( - method, - p, - rawParameterType.getSimpleName() - + " must include generic type (e.g., " - + rawParameterType.getSimpleName() - + ")"); - } - ParameterizedType parameterizedType = (ParameterizedType) type; - Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); - if (!MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) { - throw parameterError( - method, - p, - "@Part annotation must supply a name or use MultipartBody.Part parameter type."); - } - return ParameterHandler.RawPart.INSTANCE.iterable(); - } else if (rawParameterType.isArray()) { - Class arrayComponentType = rawParameterType.getComponentType(); - if (!MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) { - throw parameterError( - method, - p, - "@Part annotation must supply a name or use MultipartBody.Part parameter type."); - } - return ParameterHandler.RawPart.INSTANCE.array(); - } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) { - return ParameterHandler.RawPart.INSTANCE; - } else { - throw parameterError( - method, - p, - "@Part annotation must supply a name or use MultipartBody.Part parameter type."); - } - } else { - Headers headers = - Headers.of( - "Content-Disposition", - "form-data; name=\"" + partName + "\"", - "Content-Transfer-Encoding", - part.encoding()); - - if (Iterable.class.isAssignableFrom(rawParameterType)) { - if (!(type instanceof ParameterizedType)) { - throw parameterError( - method, - p, - rawParameterType.getSimpleName() - + " must include generic type (e.g., " - + rawParameterType.getSimpleName() - + ")"); - } - ParameterizedType parameterizedType = (ParameterizedType) type; - Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); - if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) { - throw parameterError( - method, - p, - "@Part parameters using the MultipartBody.Part must not " - + "include a part name in the annotation."); - } - Converter converter = - retrofit.requestBodyConverter(iterableType, annotations, methodAnnotations); - return new ParameterHandler.Part<>(method, p, headers, converter).iterable(); - } else if (rawParameterType.isArray()) { - Class arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); - if (MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) { - throw parameterError( - method, - p, - "@Part parameters using the MultipartBody.Part must not " - + "include a part name in the annotation."); - } - Converter converter = - retrofit.requestBodyConverter(arrayComponentType, annotations, methodAnnotations); - return new ParameterHandler.Part<>(method, p, headers, converter).array(); - } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) { - throw parameterError( - method, - p, - "@Part parameters using the MultipartBody.Part must not " - + "include a part name in the annotation."); - } else { - Converter converter = - retrofit.requestBodyConverter(type, annotations, methodAnnotations); - return new ParameterHandler.Part<>(method, p, headers, converter); - } - } - - } else if (annotation instanceof PartMap) { - validateResolvableType(p, type); - if (!isMultipart) { - throw parameterError( - method, p, "@PartMap parameters can only be used with multipart encoding."); - } - gotPart = true; - Class rawParameterType = Utils.getRawType(type); - if (!Map.class.isAssignableFrom(rawParameterType)) { - throw parameterError(method, p, "@PartMap parameter type must be Map."); - } - Type mapType = Utils.getSupertype(type, rawParameterType, Map.class); - if (!(mapType instanceof ParameterizedType)) { - throw parameterError( - method, p, "Map must include generic types (e.g., Map)"); - } - ParameterizedType parameterizedType = (ParameterizedType) mapType; - - Type keyType = Utils.getParameterUpperBound(0, parameterizedType); - if (String.class != keyType) { - throw parameterError(method, p, "@PartMap keys must be of type String: " + keyType); - } - - Type valueType = Utils.getParameterUpperBound(1, parameterizedType); - if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(valueType))) { - throw parameterError( - method, - p, - "@PartMap values cannot be MultipartBody.Part. " - + "Use @Part List or a different value type instead."); - } - - Converter valueConverter = - retrofit.requestBodyConverter(valueType, annotations, methodAnnotations); - - PartMap partMap = (PartMap) annotation; - return new ParameterHandler.PartMap<>(method, p, valueConverter, partMap.encoding()); - - } else if (annotation instanceof Body) { - validateResolvableType(p, type); - if (isFormEncoded || isMultipart) { - throw parameterError( - method, p, "@Body parameters cannot be used with form or multi-part encoding."); - } - if (gotBody) { - throw parameterError(method, p, "Multiple @Body method annotations found."); - } - - Converter converter; - try { - converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations); - } catch (RuntimeException e) { - // Wide exception range because factories are user code. - throw parameterError(method, e, p, "Unable to create @Body converter for %s", type); - } - gotBody = true; - return new ParameterHandler.Body<>(method, p, converter); - - } else if (annotation instanceof Tag) { - validateResolvableType(p, type); - - Class tagType = Utils.getRawType(type); - for (int i = p - 1; i >= 0; i--) { - ParameterHandler otherHandler = parameterHandlers[i]; - if (otherHandler instanceof ParameterHandler.Tag - && ((ParameterHandler.Tag) otherHandler).cls.equals(tagType)) { - throw parameterError( - method, - p, - "@Tag type " - + tagType.getName() - + " is duplicate of parameter #" - + (i + 1) - + " and would always overwrite its value."); - } - } - - return new ParameterHandler.Tag<>(tagType); - } - - return null; // Not a Retrofit annotation. - } - - private void validateResolvableType(int p, Type type) { - if (Utils.hasUnresolvableType(type)) { - throw parameterError( - method, p, "Parameter type must not include a type variable or wildcard: %s", type); - } - } - - private void validatePathName(int p, String name) { - if (!PARAM_NAME_REGEX.matcher(name).matches()) { - throw parameterError( - method, - p, - "@Path parameter name must match %s. Found: %s", - PARAM_URL_REGEX.pattern(), - name); - } - // Verify URL replacement name is actually present in the URL path. - if (!relativeUrlParamNames.contains(name)) { - throw parameterError(method, p, "URL \"%s\" does not contain \"{%s}\".", relativeUrl, name); - } - } - } -} diff --git a/retrofit/src/main/java/retrofit2/Response.java b/retrofit/src/main/java/retrofit2/Response.java deleted file mode 100644 index 39f487d8e..000000000 --- a/retrofit/src/main/java/retrofit2/Response.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.Objects; - -import okhttp3.Headers; -import okhttp3.Protocol; -import okhttp3.Request; -import okhttp3.ResponseBody; - -/** - * An HTTP response. - */ -public final class Response { - private final okhttp3.Response rawResponse; - private final @Nullable - T body; - private final @Nullable - ResponseBody errorBody; - - private Response( - okhttp3.Response rawResponse, @Nullable T body, @Nullable ResponseBody errorBody) { - this.rawResponse = rawResponse; - this.body = body; - this.errorBody = errorBody; - } - - /** - * Create a synthetic successful response with {@code body} as the deserialized body. - */ - public static Response success(@Nullable T body) { - return success( - body, - new okhttp3.Response.Builder() // - .code(200) - .message("OK") - .protocol(Protocol.HTTP_1_1) - .request(new Request.Builder().url("http://localhost/").build()) - .build()); - } - - /** - * Create a synthetic successful response with an HTTP status code of {@code code} and {@code - * body} as the deserialized body. - */ - public static Response success(int code, @Nullable T body) { - if (code < 200 || code >= 300) { - throw new IllegalArgumentException("code < 200 or >= 300: " + code); - } - return success( - body, - new okhttp3.Response.Builder() // - .code(code) - .message("Response.success()") - .protocol(Protocol.HTTP_1_1) - .request(new Request.Builder().url("http://localhost/").build()) - .build()); - } - - /** - * Create a synthetic successful response using {@code headers} with {@code body} as the - * deserialized body. - */ - public static Response success(@Nullable T body, Headers headers) { - Objects.requireNonNull(headers, "headers == null"); - return success( - body, - new okhttp3.Response.Builder() // - .code(200) - .message("OK") - .protocol(Protocol.HTTP_1_1) - .headers(headers) - .request(new Request.Builder().url("http://localhost/").build()) - .build()); - } - - /** - * Create a successful response from {@code rawResponse} with {@code body} as the deserialized - * body. - */ - public static Response success(@Nullable T body, okhttp3.Response rawResponse) { - Objects.requireNonNull(rawResponse, "rawResponse == null"); - if (!rawResponse.isSuccessful()) { - throw new IllegalArgumentException("rawResponse must be successful response"); - } - return new Response<>(rawResponse, body, null); - } - - /** - * Create a synthetic error response with an HTTP status code of {@code code} and {@code body} as - * the error body. - */ - public static Response error(int code, ResponseBody body) { - Objects.requireNonNull(body, "body == null"); - if (code < 400) throw new IllegalArgumentException("code < 400: " + code); - return error( - body, - new okhttp3.Response.Builder() // - .body(new OkHttpCall.NoContentResponseBody(body.contentType(), body.contentLength())) - .code(code) - .message("Response.error()") - .protocol(Protocol.HTTP_1_1) - .request(new Request.Builder().url("http://localhost/").build()) - .build()); - } - - /** - * Create an error response from {@code rawResponse} with {@code body} as the error body. - */ - public static Response error(ResponseBody body, okhttp3.Response rawResponse) { - Objects.requireNonNull(body, "body == null"); - Objects.requireNonNull(rawResponse, "rawResponse == null"); - if (rawResponse.isSuccessful()) { - throw new IllegalArgumentException("rawResponse should not be successful response"); - } - return new Response<>(rawResponse, null, body); - } - - /** - * The raw response from the HTTP client. - */ - public okhttp3.Response raw() { - return rawResponse; - } - - /** - * HTTP status code. - */ - public int code() { - return rawResponse.code(); - } - - /** - * HTTP status message or null if unknown. - */ - public String message() { - return rawResponse.message(); - } - - /** - * HTTP headers. - */ - public Headers headers() { - return rawResponse.headers(); - } - - /** - * Returns true if {@link #code()} is in the range [200..300). - */ - public boolean isSuccessful() { - return rawResponse.isSuccessful(); - } - - /** - * The deserialized response body of a {@linkplain #isSuccessful() successful} response. - */ - public @Nullable - T body() { - return body; - } - - /** - * The raw response body of an {@linkplain #isSuccessful() unsuccessful} response. - */ - public @Nullable - ResponseBody errorBody() { - return errorBody; - } - - @NonNull - @Override - public String toString() { - return rawResponse.toString(); - } -} diff --git a/retrofit/src/main/java/retrofit2/Retrofit.java b/retrofit/src/main/java/retrofit2/Retrofit.java deleted file mode 100644 index 00a6c9767..000000000 --- a/retrofit/src/main/java/retrofit2/Retrofit.java +++ /dev/null @@ -1,674 +0,0 @@ -/* - * Copyright (C) 2012 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import static java.util.Collections.unmodifiableList; - -import androidx.annotation.Nullable; - -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; -import java.lang.reflect.Type; -import java.net.URL; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Deque; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; - -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; -import retrofit2.http.GET; -import retrofit2.http.HTTP; -import retrofit2.http.Header; -import retrofit2.http.Url; - -/** - * Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to - * define how requests are made. Create instances using {@linkplain Builder the builder} and pass - * your interface to {@link #create} to generate an implementation. - * - *

For example, - * - *


- * Retrofit retrofit = new Retrofit.Builder()
- *     .baseUrl("https://api.example.com/")
- *     .addConverterFactory(GsonConverterFactory.create())
- *     .build();
- *
- * MyApi api = retrofit.create(MyApi.class);
- * Response<User> user = api.getUser().execute();
- * 
- * - * @author Bob Lee (bob@squareup.com) - * @author Jake Wharton (jw@squareup.com) - */ -public final class Retrofit { - final OkHttpClient callFactory; - final HttpUrl baseUrl; - final List converterFactories; - final int defaultConverterFactoriesSize; - final List callAdapterFactories; - final int defaultCallAdapterFactoriesSize; - final @Nullable - Executor callbackExecutor; - final boolean validateEagerly; - private final Map> serviceMethodCache = new ConcurrentHashMap<>(); - - Retrofit( - OkHttpClient callFactory, - HttpUrl baseUrl, - List converterFactories, - int defaultConverterFactoriesSize, - List callAdapterFactories, - int defaultCallAdapterFactoriesSize, - @Nullable Executor callbackExecutor, - boolean validateEagerly) { - this.callFactory = callFactory; - this.baseUrl = baseUrl; - this.converterFactories = converterFactories; // Copy+unmodifiable at call site. - this.defaultConverterFactoriesSize = defaultConverterFactoriesSize; - this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site. - this.defaultCallAdapterFactoriesSize = defaultCallAdapterFactoriesSize; - this.callbackExecutor = callbackExecutor; - this.validateEagerly = validateEagerly; - } - - /** - * Create an implementation of the API endpoints defined by the {@code service} interface. - * - *

The relative path for a given method is obtained from an annotation on the method describing - * the request type. The built-in methods are {@link retrofit2.http.GET GET}, {@link - * retrofit2.http.PUT PUT}, {@link retrofit2.http.POST POST}, {@link retrofit2.http.PATCH PATCH}, - * {@link retrofit2.http.HEAD HEAD}, {@link retrofit2.http.DELETE DELETE} and {@link - * retrofit2.http.OPTIONS OPTIONS}. You can use a custom HTTP method with {@link HTTP @HTTP}. For - * a dynamic URL, omit the path on the annotation and annotate the first parameter with {@link - * Url @Url}. - * - *

Method parameters can be used to replace parts of the URL by annotating them with {@link - * retrofit2.http.Path @Path}. Replacement sections are denoted by an identifier surrounded by - * curly braces (e.g., "{foo}"). To add items to the query string of a URL use {@link - * retrofit2.http.Query @Query}. - * - *

The body of a request is denoted by the {@link retrofit2.http.Body @Body} annotation. The - * object will be converted to request representation by one of the {@link Converter.Factory} - * instances. A {@link RequestBody} can also be used for a raw representation. - * - *

Alternative request body formats are supported by method annotations and corresponding - * parameter annotations: - * - *

    - *
  • {@link retrofit2.http.FormUrlEncoded @FormUrlEncoded} - Form-encoded data with key-value - * pairs specified by the {@link retrofit2.http.Field @Field} parameter annotation. - *
  • {@link retrofit2.http.Multipart @Multipart} - RFC 2388-compliant multipart data with - * parts specified by the {@link retrofit2.http.Part @Part} parameter annotation. - *
- * - *

Additional static headers can be added for an endpoint using the {@link - * retrofit2.http.Headers @Headers} method annotation. For per-request control over a header - * annotate a parameter with {@link Header @Header}. - * - *

By default, methods return a {@link Call} which represents the HTTP request. The generic - * parameter of the call is the response body type and will be converted by one of the {@link - * Converter.Factory} instances. {@link ResponseBody} can also be used for a raw representation. - * {@link Void} can be used if you do not care about the body contents. - * - *

For example: - * - *

-     * public interface CategoryService {
-     *   @POST("category/{cat}/")
-     *   Call<List<Item>> categoryList(@Path("cat") String a, @Query("page") int b);
-     * }
-     * 
- */ - @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety. - public T create(Class service) { - validateServiceInterface(service); - return (T) - Proxy.newProxyInstance( - service.getClassLoader(), - new Class[]{service}, - new InvocationHandler() { - private final Object[] emptyArgs = new Object[0]; - - @Override - public @Nullable - Object invoke(Object proxy, Method method, @Nullable Object[] args) - throws Throwable { - // If the method is a method from Object then defer to normal invocation. - if (method.getDeclaringClass() == Object.class) { - return method.invoke(this, args); - } - args = args != null ? args : emptyArgs; - Platform platform = Platform.get(); - return platform.isDefaultMethod(method) - ? platform.invokeDefaultMethod(method, service, proxy, args) - : loadServiceMethod(method).invoke(args); - } - }); - } - - private void validateServiceInterface(Class service) { - if (!service.isInterface()) { - throw new IllegalArgumentException("API declarations must be interfaces."); - } - - Deque> check = new ArrayDeque<>(1); - check.add(service); - while (!check.isEmpty()) { - Class candidate = check.removeFirst(); - if (candidate.getTypeParameters().length != 0) { - StringBuilder message = - new StringBuilder("Type parameters are unsupported on ").append(candidate.getName()); - if (candidate != service) { - message.append(" which is an interface of ").append(service.getName()); - } - throw new IllegalArgumentException(message.toString()); - } - Collections.addAll(check, candidate.getInterfaces()); - } - - if (validateEagerly) { - Platform platform = Platform.get(); - for (Method method : service.getDeclaredMethods()) { - if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { - loadServiceMethod(method); - } - } - } - } - - ServiceMethod loadServiceMethod(Method method) { - ServiceMethod result = serviceMethodCache.get(method); - if (result != null) return result; - - synchronized (serviceMethodCache) { - result = serviceMethodCache.get(method); - if (result == null) { - result = ServiceMethod.parseAnnotations(this, method); - serviceMethodCache.put(method, result); - } - } - return result; - } - - /** - * The factory used to create {@linkplain okhttp3.Call OkHttp calls} for sending a HTTP requests. - * Typically an instance of {@link OkHttpClient}. - */ - public OkHttpClient callFactory() { - return callFactory; - } - - /** - * The API base URL. - */ - public HttpUrl baseUrl() { - return baseUrl; - } - - /** - * Returns a list of the factories tried when creating a {@linkplain #callAdapter(Type, - * Annotation[])} call adapter}. - */ - public List callAdapterFactories() { - return callAdapterFactories; - } - - /** - * Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain - * #callAdapterFactories() factories}. - * - * @throws IllegalArgumentException if no call adapter available for {@code type}. - */ - public CallAdapter callAdapter(Type returnType, Annotation[] annotations) { - return nextCallAdapter(null, returnType, annotations); - } - - /** - * Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain - * #callAdapterFactories() factories} except {@code skipPast}. - * - * @throws IllegalArgumentException if no call adapter available for {@code type}. - */ - public CallAdapter nextCallAdapter( - @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { - Objects.requireNonNull(returnType, "returnType == null"); - Objects.requireNonNull(annotations, "annotations == null"); - - int start = callAdapterFactories.indexOf(skipPast) + 1; - for (int i = start, count = callAdapterFactories.size(); i < count; i++) { - CallAdapter adapter = callAdapterFactories.get(i).get(returnType, annotations, this); - if (adapter != null) { - return adapter; - } - } - - StringBuilder builder = - new StringBuilder("Could not locate call adapter for ").append(returnType).append(".\n"); - if (skipPast != null) { - builder.append(" Skipped:"); - for (int i = 0; i < start; i++) { - builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName()); - } - builder.append('\n'); - } - builder.append(" Tried:"); - for (int i = start, count = callAdapterFactories.size(); i < count; i++) { - builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName()); - } - throw new IllegalArgumentException(builder.toString()); - } - - /** - * Returns an unmodifiable list of the factories tried when creating a {@linkplain - * #requestBodyConverter(Type, Annotation[], Annotation[]) request body converter}, a {@linkplain - * #responseBodyConverter(Type, Annotation[]) response body converter}, or a {@linkplain - * #stringConverter(Type, Annotation[]) string converter}. - */ - public List converterFactories() { - return converterFactories; - } - - /** - * Returns a {@link Converter} for {@code type} to {@link RequestBody} from the available - * {@linkplain #converterFactories() factories}. - * - * @throws IllegalArgumentException if no converter available for {@code type}. - */ - public Converter requestBodyConverter( - Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations) { - return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations); - } - - /** - * Returns a {@link Converter} for {@code type} to {@link RequestBody} from the available - * {@linkplain #converterFactories() factories} except {@code skipPast}. - * - * @throws IllegalArgumentException if no converter available for {@code type}. - */ - public Converter nextRequestBodyConverter( - @Nullable Converter.Factory skipPast, - Type type, - Annotation[] parameterAnnotations, - Annotation[] methodAnnotations) { - Objects.requireNonNull(type, "type == null"); - Objects.requireNonNull(parameterAnnotations, "parameterAnnotations == null"); - Objects.requireNonNull(methodAnnotations, "methodAnnotations == null"); - - int start = converterFactories.indexOf(skipPast) + 1; - for (int i = start, count = converterFactories.size(); i < count; i++) { - Converter.Factory factory = converterFactories.get(i); - Converter converter = - factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this); - if (converter != null) { - //noinspection unchecked - return (Converter) converter; - } - } - - StringBuilder builder = - new StringBuilder("Could not locate RequestBody converter for ").append(type).append(".\n"); - if (skipPast != null) { - builder.append(" Skipped:"); - for (int i = 0; i < start; i++) { - builder.append("\n * ").append(converterFactories.get(i).getClass().getName()); - } - builder.append('\n'); - } - builder.append(" Tried:"); - for (int i = start, count = converterFactories.size(); i < count; i++) { - builder.append("\n * ").append(converterFactories.get(i).getClass().getName()); - } - throw new IllegalArgumentException(builder.toString()); - } - - /** - * Returns a {@link Converter} for {@link ResponseBody} to {@code type} from the available - * {@linkplain #converterFactories() factories}. - * - * @throws IllegalArgumentException if no converter available for {@code type}. - */ - public Converter responseBodyConverter(Type type, Annotation[] annotations) { - return nextResponseBodyConverter(null, type, annotations); - } - - /** - * Returns a {@link Converter} for {@link ResponseBody} to {@code type} from the available - * {@linkplain #converterFactories() factories} except {@code skipPast}. - * - * @throws IllegalArgumentException if no converter available for {@code type}. - */ - public Converter nextResponseBodyConverter( - @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { - Objects.requireNonNull(type, "type == null"); - Objects.requireNonNull(annotations, "annotations == null"); - - int start = converterFactories.indexOf(skipPast) + 1; - for (int i = start, count = converterFactories.size(); i < count; i++) { - Converter converter = - converterFactories.get(i).responseBodyConverter(type, annotations, this); - if (converter != null) { - //noinspection unchecked - return (Converter) converter; - } - } - - StringBuilder builder = - new StringBuilder("Could not locate ResponseBody converter for ") - .append(type) - .append(".\n"); - if (skipPast != null) { - builder.append(" Skipped:"); - for (int i = 0; i < start; i++) { - builder.append("\n * ").append(converterFactories.get(i).getClass().getName()); - } - builder.append('\n'); - } - builder.append(" Tried:"); - for (int i = start, count = converterFactories.size(); i < count; i++) { - builder.append("\n * ").append(converterFactories.get(i).getClass().getName()); - } - throw new IllegalArgumentException(builder.toString()); - } - - /** - * Returns a {@link Converter} for {@code type} to {@link String} from the available {@linkplain - * #converterFactories() factories}. - */ - public Converter stringConverter(Type type, Annotation[] annotations) { - Objects.requireNonNull(type, "type == null"); - Objects.requireNonNull(annotations, "annotations == null"); - - for (int i = 0, count = converterFactories.size(); i < count; i++) { - Converter converter = - converterFactories.get(i).stringConverter(type, annotations, this); - if (converter != null) { - //noinspection unchecked - return (Converter) converter; - } - } - - // Nothing matched. Resort to default converter which just calls toString(). - //noinspection unchecked - return (Converter) BuiltInConverters.ToStringConverter.INSTANCE; - } - - /** - * The executor used for {@link Callback} methods on a {@link Call}. This may be {@code null}, in - * which case callbacks should be made synchronously on the background thread. - */ - public @Nullable - Executor callbackExecutor() { - return callbackExecutor; - } - - public Builder newBuilder() { - return new Builder(this); - } - - /** - * Build a new {@link Retrofit}. - * - *

Calling {@link #baseUrl} is required before calling {@link #build()}. All other methods are - * optional. - */ - public static final class Builder { - private final List converterFactories = new ArrayList<>(); - private final List callAdapterFactories = new ArrayList<>(); - private @Nullable - OkHttpClient callFactory; - private @Nullable - HttpUrl baseUrl; - private @Nullable - Executor callbackExecutor; - private boolean validateEagerly; - - public Builder() { - } - - Builder(Retrofit retrofit) { - callFactory = retrofit.callFactory; - baseUrl = retrofit.baseUrl; - - // Do not add the default BuiltIntConverters and platform-aware converters added by build(). - for (int i = 1, - size = retrofit.converterFactories.size() - retrofit.defaultConverterFactoriesSize; - i < size; - i++) { - converterFactories.add(retrofit.converterFactories.get(i)); - } - - // Do not add the default, platform-aware call adapters added by build(). - for (int i = 0, - size = - retrofit.callAdapterFactories.size() - retrofit.defaultCallAdapterFactoriesSize; - i < size; - i++) { - callAdapterFactories.add(retrofit.callAdapterFactories.get(i)); - } - - callbackExecutor = retrofit.callbackExecutor; - validateEagerly = retrofit.validateEagerly; - } - - /** - * The HTTP client used for requests. - * - *

This is a convenience method for calling {@link #callFactory}. - */ - public Builder client(OkHttpClient client) { - callFactory = (Objects.requireNonNull(client, "client == null")); - return this; - } - - - /** - * Set the API base URL. - * - * @see #baseUrl(HttpUrl) - */ - public Builder baseUrl(URL baseUrl) { - Objects.requireNonNull(baseUrl, "baseUrl == null"); - return baseUrl(HttpUrl.get(baseUrl.toString())); - } - - /** - * Set the API base URL. - * - * @see #baseUrl(HttpUrl) - */ - public Builder baseUrl(String baseUrl) { - Objects.requireNonNull(baseUrl, "baseUrl == null"); - return baseUrl(HttpUrl.get(baseUrl)); - } - - /** - * Set the API base URL. - * - *

The specified endpoint values (such as with {@link GET @GET}) are resolved against this - * value using {@link HttpUrl#resolve(String)}. The behavior of this matches that of an {@code - * } link on a website resolving on the current URL. - * - *

Base URLs should always end in {@code /}. - * - *

A trailing {@code /} ensures that endpoints values which are relative paths will correctly - * append themselves to a base which has path components. - * - *

Correct:
- * Base URL: http://example.com/api/
- * Endpoint: foo/bar/
- * Result: http://example.com/api/foo/bar/ - * - *

Incorrect:
- * Base URL: http://example.com/api
- * Endpoint: foo/bar/
- * Result: http://example.com/foo/bar/ - * - *

This method enforces that {@code baseUrl} has a trailing {@code /}. - * - *

Endpoint values which contain a leading {@code /} are absolute. - * - *

Absolute values retain only the host from {@code baseUrl} and ignore any specified path - * components. - * - *

Base URL: http://example.com/api/
- * Endpoint: /foo/bar/
- * Result: http://example.com/foo/bar/ - * - *

Base URL: http://example.com/
- * Endpoint: /foo/bar/
- * Result: http://example.com/foo/bar/ - * - *

Endpoint values may be a full URL. - * - *

Values which have a host replace the host of {@code baseUrl} and values also with a scheme - * replace the scheme of {@code baseUrl}. - * - *

Base URL: http://example.com/
- * Endpoint: https://github.com/square/retrofit/
- * Result: https://github.com/square/retrofit/ - * - *

Base URL: http://example.com
- * Endpoint: //github.com/square/retrofit/
- * Result: http://github.com/square/retrofit/ (note the scheme stays 'http') - */ - public Builder baseUrl(HttpUrl baseUrl) { - Objects.requireNonNull(baseUrl, "baseUrl == null"); - List pathSegments = baseUrl.pathSegments(); - if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { - throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl); - } - this.baseUrl = baseUrl; - return this; - } - - /** - * Add converter factory for serialization and deserialization of objects. - */ - public Builder addConverterFactory(Converter.Factory factory) { - converterFactories.add(Objects.requireNonNull(factory, "factory == null")); - return this; - } - - /** - * Add a call adapter factory for supporting service method return types other than {@link - * Call}. - */ - public Builder addCallAdapterFactory(CallAdapter.Factory factory) { - callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null")); - return this; - } - - /** - * The executor on which {@link Callback} methods are invoked when returning {@link Call} from - * your service method. - * - *

Note: {@code executor} is not used for {@linkplain #addCallAdapterFactory custom method - * return types}. - */ - public Builder callbackExecutor(Executor executor) { - callbackExecutor = Objects.requireNonNull(executor, "executor == null"); - return this; - } - - /** - * Returns a modifiable list of call adapter factories. - */ - public List callAdapterFactories() { - return callAdapterFactories; - } - - /** - * Returns a modifiable list of converter factories. - */ - public List converterFactories() { - return converterFactories; - } - - /** - * When calling {@link #create} on the resulting {@link Retrofit} instance, eagerly validate the - * configuration of all methods in the supplied interface. - */ - public Builder validateEagerly(boolean validateEagerly) { - this.validateEagerly = validateEagerly; - return this; - } - - /** - * Create the {@link Retrofit} instance using the configured values. - * - *

Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link - * OkHttpClient} will be created and used. - */ - public Retrofit build() { - if (baseUrl == null) { - throw new IllegalStateException("Base URL required."); - } - - Platform platform = Platform.get(); - - OkHttpClient callFactory = this.callFactory; - if (callFactory == null) { - callFactory = new OkHttpClient(); - } - - Executor callbackExecutor = this.callbackExecutor; - if (callbackExecutor == null) { - callbackExecutor = platform.defaultCallbackExecutor(); - } - - // Make a defensive copy of the adapters and add the default Call adapter. - List callAdapterFactories = new ArrayList<>(this.callAdapterFactories); - List defaultCallAdapterFactories = - platform.createDefaultCallAdapterFactories(callbackExecutor); - callAdapterFactories.addAll(defaultCallAdapterFactories); - - // Make a defensive copy of the converters. - List defaultConverterFactories = - platform.createDefaultConverterFactories(); - int defaultConverterFactoriesSize = defaultConverterFactories.size(); - List converterFactories = - new ArrayList<>(1 + this.converterFactories.size() + defaultConverterFactoriesSize); - - // Add the built-in converter factory first. This prevents overriding its behavior but also - // ensures correct behavior when using converters that consume all types. - converterFactories.add(new BuiltInConverters()); - converterFactories.addAll(this.converterFactories); - converterFactories.addAll(defaultConverterFactories); - - return new Retrofit( - callFactory, - baseUrl, - unmodifiableList(converterFactories), - defaultConverterFactoriesSize, - unmodifiableList(callAdapterFactories), - defaultCallAdapterFactories.size(), - callbackExecutor, - validateEagerly); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/ServiceMethod.java b/retrofit/src/main/java/retrofit2/ServiceMethod.java deleted file mode 100644 index 9de8d6231..000000000 --- a/retrofit/src/main/java/retrofit2/ServiceMethod.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import static retrofit2.Utils.methodError; - -import androidx.annotation.Nullable; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -abstract class ServiceMethod { - static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) { - RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); - - Type returnType = method.getGenericReturnType(); - if (Utils.hasUnresolvableType(returnType)) { - throw methodError( - method, - "Method return type must not include a type variable or wildcard: %s", - returnType); - } - if (returnType == void.class) { - throw methodError(method, "Service methods cannot return void."); - } - - return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); - } - - abstract @Nullable - T invoke(Object[] args); -} diff --git a/retrofit/src/main/java/retrofit2/SkipCallbackExecutor.java b/retrofit/src/main/java/retrofit2/SkipCallbackExecutor.java deleted file mode 100644 index da01cedfb..000000000 --- a/retrofit/src/main/java/retrofit2/SkipCallbackExecutor.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.lang.reflect.Type; - -/** - * Change the behavior of a {@code Call} return type to not use the {@linkplain - * Retrofit#callbackExecutor() callback executor} for invoking the {@link Callback#onResponse(Call, - * Response) onResponse} or {@link Callback#onFailure(Call, Throwable) onFailure} methods. - * - *


- * @SkipCallbackExecutor
- * @GET("user/{id}/token")
- * Call<String> getToken(@Path("id") long id);
- * 
- *

- * This annotation can also be used when a {@link CallAdapter.Factory} explicitly delegates - * to the built-in factory for {@link Call} via {@link Retrofit#nextCallAdapter(CallAdapter.Factory, - * Type, Annotation[])} in order for the returned {@link Call} to skip the executor. (Note: by - * default, a {@link Call} supplied directly to a {@link CallAdapter} will already skip the callback - * executor. The annotation is only useful when looking up the built-in adapter.) - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface SkipCallbackExecutor { -} diff --git a/retrofit/src/main/java/retrofit2/SkipCallbackExecutorImpl.java b/retrofit/src/main/java/retrofit2/SkipCallbackExecutorImpl.java deleted file mode 100644 index d1895ea01..000000000 --- a/retrofit/src/main/java/retrofit2/SkipCallbackExecutorImpl.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.NonNull; - -import java.lang.annotation.Annotation; - -// This class conforms to the annotation requirements documented on Annotation. -final class SkipCallbackExecutorImpl implements SkipCallbackExecutor { - private static final SkipCallbackExecutor INSTANCE = new SkipCallbackExecutorImpl(); - - static Annotation[] ensurePresent(Annotation[] annotations) { - if (Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)) { - return annotations; - } - - Annotation[] newAnnotations = new Annotation[annotations.length + 1]; - // Place the skip annotation first since we're guaranteed to check for it in the call adapter. - newAnnotations[0] = INSTANCE; - System.arraycopy(annotations, 0, newAnnotations, 1, annotations.length); - return newAnnotations; - } - - @Override - public Class annotationType() { - return SkipCallbackExecutor.class; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof SkipCallbackExecutor; - } - - @Override - public int hashCode() { - return 0; - } - - @NonNull - @Override - public String toString() { - return "@" + SkipCallbackExecutor.class.getName() + "()"; - } -} diff --git a/retrofit/src/main/java/retrofit2/Utils.java b/retrofit/src/main/java/retrofit2/Utils.java deleted file mode 100644 index f16e5d11a..000000000 --- a/retrofit/src/main/java/retrofit2/Utils.java +++ /dev/null @@ -1,572 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.GenericDeclaration; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; -import java.util.Arrays; -import java.util.NoSuchElementException; -import java.util.Objects; - -import kotlin.Unit; -import okhttp3.ResponseBody; -import okio.Buffer; - -final class Utils { - static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; - /** - * Not volatile because we don't mind multiple threads discovering this. - */ - private static boolean checkForKotlinUnit = true; - - private Utils() { - // No instances. - } - - static RuntimeException methodError(Method method, String message, Object... args) { - return methodError(method, null, message, args); - } - - @SuppressWarnings("AnnotateFormatMethod") - static RuntimeException methodError( - Method method, @Nullable Throwable cause, String message, Object... args) { - message = String.format(message, args); - return new IllegalArgumentException( - message - + "\n for method " - + method.getDeclaringClass().getSimpleName() - + "." - + method.getName(), - cause); - } - - static RuntimeException parameterError( - Method method, Throwable cause, int p, String message, Object... args) { - return methodError(method, cause, message + " (parameter #" + (p + 1) + ")", args); - } - - static RuntimeException parameterError(Method method, int p, String message, Object... args) { - return methodError(method, message + " (parameter #" + (p + 1) + ")", args); - } - - static Class getRawType(Type type) { - Objects.requireNonNull(type, "type == null"); - - if (type instanceof Class) { - // Type is a normal class. - return (Class) type; - } - if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - - // I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but - // suspects some pathological case related to nested classes exists. - Type rawType = parameterizedType.getRawType(); - if (!(rawType instanceof Class)) throw new IllegalArgumentException(); - return (Class) rawType; - } - if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType) type).getGenericComponentType(); - return Array.newInstance(getRawType(componentType), 0).getClass(); - } - if (type instanceof TypeVariable) { - // We could use the variable's bounds, but that won't work if there are multiple. Having a raw - // type that's more general than necessary is okay. - return Object.class; - } - if (type instanceof WildcardType) { - return getRawType(((WildcardType) type).getUpperBounds()[0]); - } - - throw new IllegalArgumentException( - "Expected a Class, ParameterizedType, or " - + "GenericArrayType, but <" - + type - + "> is of type " - + type.getClass().getName()); - } - - /** - * Returns true if {@code a} and {@code b} are equal. - */ - static boolean equals(Type a, Type b) { - if (a == b) { - return true; // Also handles (a == null && b == null). - - } else if (a instanceof Class) { - return a.equals(b); // Class already specifies equals(). - - } else if (a instanceof ParameterizedType) { - if (!(b instanceof ParameterizedType)) return false; - ParameterizedType pa = (ParameterizedType) a; - ParameterizedType pb = (ParameterizedType) b; - Object ownerA = pa.getOwnerType(); - Object ownerB = pb.getOwnerType(); - return (Objects.equals(ownerA, ownerB)) - && pa.getRawType().equals(pb.getRawType()) - && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); - - } else if (a instanceof GenericArrayType) { - if (!(b instanceof GenericArrayType)) return false; - GenericArrayType ga = (GenericArrayType) a; - GenericArrayType gb = (GenericArrayType) b; - return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); - - } else if (a instanceof WildcardType) { - if (!(b instanceof WildcardType)) return false; - WildcardType wa = (WildcardType) a; - WildcardType wb = (WildcardType) b; - return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) - && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); - - } else if (a instanceof TypeVariable) { - if (!(b instanceof TypeVariable)) return false; - TypeVariable va = (TypeVariable) a; - TypeVariable vb = (TypeVariable) b; - return va.getGenericDeclaration() == vb.getGenericDeclaration() - && va.getName().equals(vb.getName()); - - } else { - return false; // This isn't a type we support! - } - } - - /** - * Returns the generic supertype for {@code supertype}. For example, given a class {@code - * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set} and the - * result when the supertype is {@code Collection.class} is {@code Collection}. - */ - static Type getGenericSupertype(Type context, Class rawType, Class toResolve) { - if (toResolve == rawType) return context; - - // We skip searching through interfaces if unknown is an interface. - if (toResolve.isInterface()) { - Class[] interfaces = rawType.getInterfaces(); - for (int i = 0, length = interfaces.length; i < length; i++) { - if (interfaces[i] == toResolve) { - return rawType.getGenericInterfaces()[i]; - } else if (toResolve.isAssignableFrom(interfaces[i])) { - return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve); - } - } - } - - // Check our supertypes. - if (!rawType.isInterface()) { - while (rawType != Object.class) { - Class rawSupertype = rawType.getSuperclass(); - if (rawSupertype == toResolve) { - return rawType.getGenericSuperclass(); - } else if (toResolve.isAssignableFrom(rawSupertype)) { - return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve); - } - rawType = rawSupertype; - } - } - - // We can't resolve this further. - return toResolve; - } - - private static int indexOf(Object[] array, Object toFind) { - for (int i = 0; i < array.length; i++) { - if (toFind.equals(array[i])) return i; - } - throw new NoSuchElementException(); - } - - static String typeToString(Type type) { - return type instanceof Class ? ((Class) type).getName() : type.toString(); - } - - /** - * Returns the generic form of {@code supertype}. For example, if this is {@code - * ArrayList}, this returns {@code Iterable} given the input {@code - * Iterable.class}. - * - * @param supertype a superclass of, or interface implemented by, this. - */ - static Type getSupertype(Type context, Class contextRawType, Class supertype) { - if (!supertype.isAssignableFrom(contextRawType)) throw new IllegalArgumentException(); - return resolve( - context, contextRawType, getGenericSupertype(context, contextRawType, supertype)); - } - - static Type resolve(Type context, Class contextRawType, Type toResolve) { - // This implementation is made a little more complicated in an attempt to avoid object-creation. - while (true) { - if (toResolve instanceof TypeVariable) { - TypeVariable typeVariable = (TypeVariable) toResolve; - toResolve = resolveTypeVariable(context, contextRawType, typeVariable); - if (toResolve == typeVariable) { - return toResolve; - } - - } else if (toResolve instanceof Class && ((Class) toResolve).isArray()) { - Class original = (Class) toResolve; - Type componentType = original.getComponentType(); - Type newComponentType = resolve(context, contextRawType, componentType); - return componentType == newComponentType - ? original - : new GenericArrayTypeImpl(newComponentType); - - } else if (toResolve instanceof GenericArrayType) { - GenericArrayType original = (GenericArrayType) toResolve; - Type componentType = original.getGenericComponentType(); - Type newComponentType = resolve(context, contextRawType, componentType); - return componentType == newComponentType - ? original - : new GenericArrayTypeImpl(newComponentType); - - } else if (toResolve instanceof ParameterizedType) { - ParameterizedType original = (ParameterizedType) toResolve; - Type ownerType = original.getOwnerType(); - Type newOwnerType = resolve(context, contextRawType, ownerType); - boolean changed = newOwnerType != ownerType; - - Type[] args = original.getActualTypeArguments(); - for (int t = 0, length = args.length; t < length; t++) { - Type resolvedTypeArgument = resolve(context, contextRawType, args[t]); - if (resolvedTypeArgument != args[t]) { - if (!changed) { - args = args.clone(); - changed = true; - } - args[t] = resolvedTypeArgument; - } - } - - return changed - ? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args) - : original; - - } else if (toResolve instanceof WildcardType) { - WildcardType original = (WildcardType) toResolve; - Type[] originalLowerBound = original.getLowerBounds(); - Type[] originalUpperBound = original.getUpperBounds(); - - if (originalLowerBound.length == 1) { - Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]); - if (lowerBound != originalLowerBound[0]) { - return new WildcardTypeImpl(new Type[]{Object.class}, new Type[]{lowerBound}); - } - } else if (originalUpperBound.length == 1) { - Type upperBound = resolve(context, contextRawType, originalUpperBound[0]); - if (upperBound != originalUpperBound[0]) { - return new WildcardTypeImpl(new Type[]{upperBound}, EMPTY_TYPE_ARRAY); - } - } - return original; - - } else { - return toResolve; - } - } - } - - private static Type resolveTypeVariable( - Type context, Class contextRawType, TypeVariable unknown) { - Class declaredByRaw = declaringClassOf(unknown); - - // We can't reduce this further. - if (declaredByRaw == null) return unknown; - - Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw); - if (declaredBy instanceof ParameterizedType) { - int index = indexOf(declaredByRaw.getTypeParameters(), unknown); - return ((ParameterizedType) declaredBy).getActualTypeArguments()[index]; - } - - return unknown; - } - - /** - * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by - * a class. - */ - private static @Nullable - Class declaringClassOf(TypeVariable typeVariable) { - GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); - return genericDeclaration instanceof Class ? (Class) genericDeclaration : null; - } - - static void checkNotPrimitive(Type type) { - if (type instanceof Class && ((Class) type).isPrimitive()) { - throw new IllegalArgumentException(); - } - } - - /** - * Returns true if {@code annotations} contains an instance of {@code cls}. - */ - static boolean isAnnotationPresent(Annotation[] annotations, Class cls) { - for (Annotation annotation : annotations) { - if (cls.isInstance(annotation)) { - return true; - } - } - return false; - } - - static ResponseBody buffer(ResponseBody body) throws IOException { - Buffer buffer = new Buffer(); - body.source().readAll(buffer); - return ResponseBody.create(buffer, body.contentType(), body.contentLength()); - } - - static Type getParameterUpperBound(int index, ParameterizedType type) { - Type[] types = type.getActualTypeArguments(); - if (index < 0 || index >= types.length) { - throw new IllegalArgumentException( - "Index " + index + " not in range [0," + types.length + ") for " + type); - } - Type paramType = types[index]; - if (paramType instanceof WildcardType) { - return ((WildcardType) paramType).getUpperBounds()[0]; - } - return paramType; - } - - static Type getParameterLowerBound(int index, ParameterizedType type) { - Type paramType = type.getActualTypeArguments()[index]; - if (paramType instanceof WildcardType) { - return ((WildcardType) paramType).getLowerBounds()[0]; - } - return paramType; - } - - static boolean hasUnresolvableType(@Nullable Type type) { - if (type instanceof Class) { - return false; - } - if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - for (Type typeArgument : parameterizedType.getActualTypeArguments()) { - if (hasUnresolvableType(typeArgument)) { - return true; - } - } - return false; - } - if (type instanceof GenericArrayType) { - return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType()); - } - if (type instanceof TypeVariable) { - return true; - } - if (type instanceof WildcardType) { - return true; - } - String className = type == null ? "null" : type.getClass().getName(); - throw new IllegalArgumentException( - "Expected a Class, ParameterizedType, or " - + "GenericArrayType, but <" - + type - + "> is of type " - + className); - } - - // https://github.com/ReactiveX/RxJava/blob/6a44e5d0543a48f1c378dc833a155f3f71333bc2/ - // src/main/java/io/reactivex/exceptions/Exceptions.java#L66 - static void throwIfFatal(Throwable t) { - if (t instanceof VirtualMachineError) { - throw (VirtualMachineError) t; - } else if (t instanceof ThreadDeath) { - throw (ThreadDeath) t; - } else if (t instanceof LinkageError) { - throw (LinkageError) t; - } - } - - static boolean isUnit(Type type) { - if (checkForKotlinUnit) { - try { - return type == Unit.class; - } catch (NoClassDefFoundError ignored) { - checkForKotlinUnit = false; - } - } - return false; - } - - static final class ParameterizedTypeImpl implements ParameterizedType { - private final @Nullable - Type ownerType; - private final Type rawType; - private final Type[] typeArguments; - - ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) { - // Require an owner type if the raw type needs it. - if (rawType instanceof Class - && (ownerType == null) != (((Class) rawType).getEnclosingClass() == null)) { - throw new IllegalArgumentException(); - } - - for (Type typeArgument : typeArguments) { - Objects.requireNonNull(typeArgument, "typeArgument == null"); - checkNotPrimitive(typeArgument); - } - - this.ownerType = ownerType; - this.rawType = rawType; - this.typeArguments = typeArguments.clone(); - } - - @NonNull - @Override - public Type[] getActualTypeArguments() { - return typeArguments.clone(); - } - - @NonNull - @Override - public Type getRawType() { - return rawType; - } - - @Override - public @Nullable - Type getOwnerType() { - return ownerType; - } - - @Override - public boolean equals(Object other) { - return other instanceof ParameterizedType && Utils.equals(this, (ParameterizedType) other); - } - - @Override - public int hashCode() { - return Arrays.hashCode(typeArguments) - ^ rawType.hashCode() - ^ (ownerType != null ? ownerType.hashCode() : 0); - } - - @NonNull - @Override - public String toString() { - if (typeArguments.length == 0) return typeToString(rawType); - StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1)); - result.append(typeToString(rawType)); - result.append("<").append(typeToString(typeArguments[0])); - for (int i = 1; i < typeArguments.length; i++) { - result.append(", ").append(typeToString(typeArguments[i])); - } - return result.append(">").toString(); - } - } - - private static final class GenericArrayTypeImpl implements GenericArrayType { - private final Type componentType; - - GenericArrayTypeImpl(Type componentType) { - this.componentType = componentType; - } - - @NonNull - @Override - public Type getGenericComponentType() { - return componentType; - } - - @Override - public boolean equals(Object o) { - return o instanceof GenericArrayType && Utils.equals(this, (GenericArrayType) o); - } - - @Override - public int hashCode() { - return componentType.hashCode(); - } - - @NonNull - @Override - public String toString() { - return typeToString(componentType) + "[]"; - } - } - - /** - * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only - * support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper - * bound must be Object.class. - */ - private static final class WildcardTypeImpl implements WildcardType { - private final Type upperBound; - private final @Nullable - Type lowerBound; - - WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { - if (lowerBounds.length > 1) throw new IllegalArgumentException(); - if (upperBounds.length != 1) throw new IllegalArgumentException(); - - if (lowerBounds.length == 1) { - if (lowerBounds[0] == null) throw new NullPointerException(); - checkNotPrimitive(lowerBounds[0]); - if (upperBounds[0] != Object.class) throw new IllegalArgumentException(); - lowerBound = lowerBounds[0]; - upperBound = Object.class; - } else { - if (upperBounds[0] == null) throw new NullPointerException(); - checkNotPrimitive(upperBounds[0]); - lowerBound = null; - upperBound = upperBounds[0]; - } - } - - @NonNull - @Override - public Type[] getUpperBounds() { - return new Type[]{upperBound}; - } - - @NonNull - @Override - public Type[] getLowerBounds() { - return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY; - } - - @Override - public boolean equals(Object other) { - return other instanceof WildcardType && Utils.equals(this, (WildcardType) other); - } - - @Override - public int hashCode() { - // This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()). - return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode()); - } - - @NonNull - @Override - public String toString() { - if (lowerBound != null) return "? super " + typeToString(lowerBound); - if (upperBound == Object.class) return "?"; - return "? extends " + typeToString(upperBound); - } - } -} diff --git a/retrofit/src/main/java/retrofit2/http/Body.java b/retrofit/src/main/java/retrofit2/http/Body.java deleted file mode 100644 index 1c1851ccf..000000000 --- a/retrofit/src/main/java/retrofit2/http/Body.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2011 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import retrofit2.Converter; -import retrofit2.Retrofit; - -/** - * Use this annotation on a service method param when you want to directly control the request body - * of a POST/PUT request (instead of sending in as request parameters or form-style request body). - * The object will be serialized using the {@link Retrofit Retrofit} instance {@link Converter - * Converter} and the result will be set directly as the request body. - * - *

Body parameters may not be {@code null}. - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface Body { -} diff --git a/retrofit/src/main/java/retrofit2/http/DELETE.java b/retrofit/src/main/java/retrofit2/http/DELETE.java deleted file mode 100644 index 01d92cd19..000000000 --- a/retrofit/src/main/java/retrofit2/http/DELETE.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; - -/** - * Make a DELETE request. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface DELETE { - /** - * A relative or absolute path, or full URL of the endpoint. This value is optional if the first - * parameter of the method is annotated with {@link Url @Url}. - * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how - * this is resolved against a base URL to create the full endpoint URL. - */ - String value() default ""; -} diff --git a/retrofit/src/main/java/retrofit2/http/Field.java b/retrofit/src/main/java/retrofit2/http/Field.java deleted file mode 100644 index 4cc61c1d6..000000000 --- a/retrofit/src/main/java/retrofit2/http/Field.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.lang.reflect.Type; - -import retrofit2.Retrofit; - -/** - * Named pair for a form-encoded request. - * - *

Values are converted to strings using {@link Retrofit#stringConverter(Type, Annotation[])} (or - * {@link Object#toString()}, if no matching string converter is installed) and then form URL - * encoded. {@code null} values are ignored. Passing a {@link java.util.List List} or array will - * result in a field pair for each non-{@code null} item. - * - *

Simple Example: - * - *


- * @FormUrlEncoded
- * @POST("/")
- * Call<ResponseBody> example(
- *     @Field("name") String name,
- *     @Field("occupation") String occupation);
- * 
- *

- * Calling with {@code foo.example("Bob Smith", "President")} yields a request body of {@code - * name=Bob+Smith&occupation=President}. - * - *

Array/Varargs Example: - * - *


- * @FormUrlEncoded
- * @POST("/list")
- * Call<ResponseBody> example(@Field("name") String... names);
- * 
- *

- * Calling with {@code foo.example("Bob Smith", "Jane Doe")} yields a request body of {@code - * name=Bob+Smith&name=Jane+Doe}. - * - * @see FormUrlEncoded - * @see FieldMap - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface Field { - String value(); - - /** - * Specifies whether the {@linkplain #value() name} and value are already URL encoded. - */ - boolean encoded() default false; -} diff --git a/retrofit/src/main/java/retrofit2/http/FieldMap.java b/retrofit/src/main/java/retrofit2/http/FieldMap.java deleted file mode 100644 index 44e976550..000000000 --- a/retrofit/src/main/java/retrofit2/http/FieldMap.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Named key/value pairs for a form-encoded request. - * - *

Simple Example: - * - *


- * @FormUrlEncoded
- * @POST("/things")
- * Call<ResponseBody> things(@FieldMap Map<String, String> fields);
- * 
- *

- * Calling with {@code foo.things(ImmutableMap.of("foo", "bar", "kit", "kat")} yields a request body - * of {@code foo=bar&kit=kat}. - * - *

A {@code null} value for the map, as a key, or as a value is not allowed. - * - * @see FormUrlEncoded - * @see Field - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface FieldMap { - /** - * Specifies whether the names and values are already URL encoded. - */ - boolean encoded() default false; -} diff --git a/retrofit/src/main/java/retrofit2/http/FormUrlEncoded.java b/retrofit/src/main/java/retrofit2/http/FormUrlEncoded.java deleted file mode 100644 index 48ae0885b..000000000 --- a/retrofit/src/main/java/retrofit2/http/FormUrlEncoded.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Denotes that the request body will use form URL encoding. Fields should be declared as parameters - * and annotated with {@link Field @Field}. - * - *

Requests made with this annotation will have {@code application/x-www-form-urlencoded} MIME - * type. Field names and values will be UTF-8 encoded before being URI-encoded in accordance to RFC-3986. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface FormUrlEncoded { -} diff --git a/retrofit/src/main/java/retrofit2/http/GET.java b/retrofit/src/main/java/retrofit2/http/GET.java deleted file mode 100644 index 2e667fe7d..000000000 --- a/retrofit/src/main/java/retrofit2/http/GET.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; - -/** - * Make a GET request. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface GET { - /** - * A relative or absolute path, or full URL of the endpoint. This value is optional if the first - * parameter of the method is annotated with {@link Url @Url}. - * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how - * this is resolved against a base URL to create the full endpoint URL. - */ - String value() default ""; -} diff --git a/retrofit/src/main/java/retrofit2/http/HEAD.java b/retrofit/src/main/java/retrofit2/http/HEAD.java deleted file mode 100644 index 77d3f384e..000000000 --- a/retrofit/src/main/java/retrofit2/http/HEAD.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; - -/** - * Make a HEAD request. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface HEAD { - /** - * A relative or absolute path, or full URL of the endpoint. This value is optional if the first - * parameter of the method is annotated with {@link Url @Url}. - * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how - * this is resolved against a base URL to create the full endpoint URL. - */ - String value() default ""; -} diff --git a/retrofit/src/main/java/retrofit2/http/HTTP.java b/retrofit/src/main/java/retrofit2/http/HTTP.java deleted file mode 100644 index b249c856c..000000000 --- a/retrofit/src/main/java/retrofit2/http/HTTP.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; - -/** - * Use a custom HTTP verb for a request. - * - *


- * interface Service {
- *   @HTTP(method = "CUSTOM", path = "custom/endpoint/")
- *   Call<ResponseBody> customEndpoint();
- * }
- * 
- *

- * This annotation can also used for sending {@code DELETE} with a request body: - * - *


- * interface Service {
- *   @HTTP(method = "DELETE", path = "remove/", hasBody = true)
- *   Call<ResponseBody> deleteObject(@Body RequestBody object);
- * }
- * 
- */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface HTTP { - String method(); - - /** - * A relative or absolute path, or full URL of the endpoint. This value is optional if the first - * parameter of the method is annotated with {@link Url @Url}. - * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how - * this is resolved against a base URL to create the full endpoint URL. - */ - String path() default ""; - - boolean hasBody() default false; -} diff --git a/retrofit/src/main/java/retrofit2/http/Header.java b/retrofit/src/main/java/retrofit2/http/Header.java deleted file mode 100644 index b4286c77a..000000000 --- a/retrofit/src/main/java/retrofit2/http/Header.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Replaces the header with the value of its target. - * - *


- * @GET("/")
- * Call<ResponseBody> foo(@Header("Accept-Language") String lang);
- * 
- *

- * Header parameters may be {@code null} which will omit them from the request. Passing a {@link - * java.util.List List} or array will result in a header for each non-{@code null} item. - * - *

Parameter keys and values only allows ascii values by default. Specify {@link - * #allowUnsafeNonAsciiValues() allowUnsafeNonAsciiValues=true} to change this behavior. - * - *


- * @GET("/")
- * Call<ResponseBody> foo(@Header("Accept-Language", allowUnsafeNonAsciiValues=true) String lang);
- * 
- * - *

Note: Headers do not overwrite each other. All headers with the same name - * will be included in the request. - * - * @see Headers - * @see HeaderMap - */ -@Documented -@Retention(RUNTIME) -@Target(PARAMETER) -public @interface Header { - - /** - * The query parameter name. - */ - String value(); - - /** - * Specifies whether the parameter {@linkplain #value() name} and value are already URL encoded. - */ - boolean allowUnsafeNonAsciiValues() default false; -} \ No newline at end of file diff --git a/retrofit/src/main/java/retrofit2/http/HeaderMap.java b/retrofit/src/main/java/retrofit2/http/HeaderMap.java deleted file mode 100644 index 0e71dd34c..000000000 --- a/retrofit/src/main/java/retrofit2/http/HeaderMap.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2016 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.lang.reflect.Type; -import java.util.Map; - -import retrofit2.Retrofit; - -/** - * Adds headers specified in the {@link Map} or {@link okhttp3.Headers}. - * - *

Values in the map are converted to strings using {@link Retrofit#stringConverter(Type, - * Annotation[])} (or {@link Object#toString()}, if no matching string converter is installed). - * - *

Simple Example: - * - *

- * @GET("/search")
- * void list(@HeaderMap Map<String, String> headers);
- *
- * ...
- *
- * // The following call yields /search with headers
- * // Accept: text/plain and Accept-Charset: utf-8
- * foo.list(ImmutableMap.of("Accept", "text/plain", "Accept-Charset", "utf-8"));
- * 
- * - *

Map keys and values representing parameter values allow only ascii values by default. - * Specify {@link #allowUnsafeNonAsciiValues() allowUnsafeNonAsciiValues=true} to change this behavior. - * - *

- * @GET("/search")
- * void list(@HeaderMap(allowUnsafeNonAsciiValues=true) Map<String, String> headers);
- *
- * @see Header
- * @see Headers
- */
-@Documented
-@Target(PARAMETER)
-@Retention(RUNTIME)
-public @interface HeaderMap {
-    /**
-     * Specifies whether the parameter values are allowed with unsafe non ascii values.
-     */
-    boolean allowUnsafeNonAsciiValues() default false;
-}
\ No newline at end of file
diff --git a/retrofit/src/main/java/retrofit2/http/Headers.java b/retrofit/src/main/java/retrofit2/http/Headers.java
deleted file mode 100644
index 20fc6fe4a..000000000
--- a/retrofit/src/main/java/retrofit2/http/Headers.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2013 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package retrofit2.http;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Adds headers literally supplied in the {@code value}.
- *
- * 

- * @Headers("Cache-Control: max-age=640000")
- * @GET("/")
- * ...
- *
- * @Headers({
- *   "X-Foo: Bar",
- *   "X-Ping: Pong"
- * })
- * @GET("/")
- * ...
- * 
- * - *

Parameter keys and values only allows ascii values by default. Specify {@link - * #allowUnsafeNonAsciiValues() allowUnsafeNonAsciiValues=true} to change this behavior. - * - *

@Headers({ "X-Foo: Bar", "X-Ping: Pong" }, allowUnsafeNonAsciiValues=true) @GET("/") - * - *

Note: Headers do not overwrite each other. All headers with the same name - * will be included in the request. - * - * @see Header - * @see HeaderMap - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface Headers { - - /** - * The query parameter name. - */ - String[] value(); - - /** - * Specifies whether the parameter {@linkplain #value() name} and value are already URL encoded. - */ - boolean allowUnsafeNonAsciiValues() default false; -} \ No newline at end of file diff --git a/retrofit/src/main/java/retrofit2/http/Multipart.java b/retrofit/src/main/java/retrofit2/http/Multipart.java deleted file mode 100644 index fcd2a8d09..000000000 --- a/retrofit/src/main/java/retrofit2/http/Multipart.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Denotes that the request body is multi-part. Parts should be declared as parameters and annotated - * with {@link Part @Part}. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface Multipart { -} diff --git a/retrofit/src/main/java/retrofit2/http/OPTIONS.java b/retrofit/src/main/java/retrofit2/http/OPTIONS.java deleted file mode 100644 index 5361a5e1f..000000000 --- a/retrofit/src/main/java/retrofit2/http/OPTIONS.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; - -/** - * Make an OPTIONS request. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface OPTIONS { - /** - * A relative or absolute path, or full URL of the endpoint. This value is optional if the first - * parameter of the method is annotated with {@link Url @Url}. - * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how - * this is resolved against a base URL to create the full endpoint URL. - */ - String value() default ""; -} diff --git a/retrofit/src/main/java/retrofit2/http/PATCH.java b/retrofit/src/main/java/retrofit2/http/PATCH.java deleted file mode 100644 index 85da64f6f..000000000 --- a/retrofit/src/main/java/retrofit2/http/PATCH.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; - -/** - * Make a PATCH request. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface PATCH { - /** - * A relative or absolute path, or full URL of the endpoint. This value is optional if the first - * parameter of the method is annotated with {@link Url @Url}. - * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how - * this is resolved against a base URL to create the full endpoint URL. - */ - String value() default ""; -} diff --git a/retrofit/src/main/java/retrofit2/http/POST.java b/retrofit/src/main/java/retrofit2/http/POST.java deleted file mode 100644 index 69d3bcad2..000000000 --- a/retrofit/src/main/java/retrofit2/http/POST.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; - -/** - * Make a POST request. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface POST { - /** - * A relative or absolute path, or full URL of the endpoint. This value is optional if the first - * parameter of the method is annotated with {@link Url @Url}. - * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how - * this is resolved against a base URL to create the full endpoint URL. - */ - String value() default ""; -} diff --git a/retrofit/src/main/java/retrofit2/http/PUT.java b/retrofit/src/main/java/retrofit2/http/PUT.java deleted file mode 100644 index 66e8a69f4..000000000 --- a/retrofit/src/main/java/retrofit2/http/PUT.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; - -/** - * Make a PUT request. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface PUT { - /** - * A relative or absolute path, or full URL of the endpoint. This value is optional if the first - * parameter of the method is annotated with {@link Url @Url}. - * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how - * this is resolved against a base URL to create the full endpoint URL. - */ - String value() default ""; -} diff --git a/retrofit/src/main/java/retrofit2/http/Part.java b/retrofit/src/main/java/retrofit2/http/Part.java deleted file mode 100644 index aa557c782..000000000 --- a/retrofit/src/main/java/retrofit2/http/Part.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import retrofit2.Converter; - -/** - * Denotes a single part of a multi-part request. - * - *

The parameter type on which this annotation exists will be processed in one of three ways: - * - *

    - *
  • If the type is {@link okhttp3.MultipartBody.Part} the contents will be used directly. Omit - * the name from the annotation (i.e., {@code @Part MultipartBody.Part part}). - *
  • If the type is {@link okhttp3.RequestBody RequestBody} the value will be used directly with - * its content type. Supply the part name in the annotation (e.g., {@code @Part("foo") - * RequestBody foo}). - *
  • Other object types will be converted to an appropriate representation by using {@linkplain - * Converter a converter}. Supply the part name in the annotation (e.g., {@code @Part("foo") - * Image photo}). - *
- * - *

Values may be {@code null} which will omit them from the request body. - * - *

- * - *


- * @Multipart
- * @POST("/")
- * Call<ResponseBody> example(
- *     @Part("description") String description,
- *     @Part(value = "image", encoding = "8-bit") RequestBody image);
- * 
- * - *

Part parameters may not be {@code null}. - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface Part { - /** - * The name of the part. Required for all parameter types except {@link - * okhttp3.MultipartBody.Part}. - */ - String value() default ""; - - /** - * The {@code Content-Transfer-Encoding} of this part. - */ - String encoding() default "binary"; -} diff --git a/retrofit/src/main/java/retrofit2/http/PartMap.java b/retrofit/src/main/java/retrofit2/http/PartMap.java deleted file mode 100644 index 0470f89de..000000000 --- a/retrofit/src/main/java/retrofit2/http/PartMap.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import retrofit2.Converter; - -/** - * Denotes name and value parts of a multi-part request. - * - *

Values of the map on which this annotation exists will be processed in one of two ways: - * - *

    - *
  • If the type is {@link okhttp3.RequestBody RequestBody} the value will be used directly with - * its content type. - *
  • Other object types will be converted to an appropriate representation by using {@linkplain - * Converter a converter}. - *
- * - *

- * - *


- * @Multipart
- * @POST("/upload")
- * Call<ResponseBody> upload(
- *     @Part("file") RequestBody file,
- *     @PartMap Map<String, RequestBody> params);
- * 
- * - *

A {@code null} value for the map, as a key, or as a value is not allowed. - * - * @see Multipart - * @see Part - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface PartMap { - /** - * The {@code Content-Transfer-Encoding} of the parts. - */ - String encoding() default "binary"; -} diff --git a/retrofit/src/main/java/retrofit2/http/Path.java b/retrofit/src/main/java/retrofit2/http/Path.java deleted file mode 100644 index 1cc5c72c7..000000000 --- a/retrofit/src/main/java/retrofit2/http/Path.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.lang.reflect.Type; - -import retrofit2.Retrofit; - -/** - * Named replacement in a URL path segment. Values are converted to strings using {@link - * Retrofit#stringConverter(Type, Annotation[])} (or {@link Object#toString()}, if no matching - * string converter is installed) and then URL encoded. - * - *

Simple example: - * - *


- * @GET("/image/{id}")
- * Call<ResponseBody> example(@Path("id") int id);
- * 
- *

- * Calling with {@code foo.example(1)} yields {@code /image/1}. - * - *

Values are URL encoded by default. Disable with {@code encoded=true}. - * - *


- * @GET("/user/{name}")
- * Call<ResponseBody> encoded(@Path("name") String name);
- *
- * @GET("/user/{name}")
- * Call<ResponseBody> notEncoded(@Path(value="name", encoded=true) String name);
- * 
- *

- * Calling {@code foo.encoded("John+Doe")} yields {@code /user/John%2BDoe} whereas {@code - * foo.notEncoded("John+Doe")} yields {@code /user/John+Doe}. - * - *

Path parameters may not be {@code null}. - */ -@Documented -@Retention(RUNTIME) -@Target(PARAMETER) -public @interface Path { - String value(); - - /** - * Specifies whether the argument value to the annotated method parameter is already URL encoded. - */ - boolean encoded() default false; -} diff --git a/retrofit/src/main/java/retrofit2/http/Query.java b/retrofit/src/main/java/retrofit2/http/Query.java deleted file mode 100644 index 30bd594ab..000000000 --- a/retrofit/src/main/java/retrofit2/http/Query.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.lang.reflect.Type; - -import retrofit2.Retrofit; - -/** - * Query parameter appended to the URL. - * - *

Values are converted to strings using {@link Retrofit#stringConverter(Type, Annotation[])} (or - * {@link Object#toString()}, if no matching string converter is installed) and then URL encoded. - * {@code null} values are ignored. Passing a {@link java.util.List List} or array will result in a - * query parameter for each non-{@code null} item. - * - *

Simple Example: - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@Query("page") int page);
- * 
- *

- * Calling with {@code foo.friends(1)} yields {@code /friends?page=1}. - * - *

Example with {@code null}: - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@Query("group") String group);
- * 
- *

- * Calling with {@code foo.friends(null)} yields {@code /friends}. - * - *

Array/Varargs Example: - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@Query("group") String... groups);
- * 
- *

- * Calling with {@code foo.friends("coworker", "bowling")} yields {@code - * /friends?group=coworker&group=bowling}. - * - *

Parameter names and values are URL encoded by default. Specify {@link #encoded() encoded=true} - * to change this behavior. - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@Query(value="group", encoded=true) String group);
- * 
- *

- * Calling with {@code foo.friends("foo+bar"))} yields {@code /friends?group=foo+bar}. - * - * @see QueryMap - * @see QueryName - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface Query { - /** - * The query parameter name. - */ - String value(); - - /** - * Specifies whether the parameter {@linkplain #value() name} and value are already URL encoded. - */ - boolean encoded() default false; -} diff --git a/retrofit/src/main/java/retrofit2/http/QueryMap.java b/retrofit/src/main/java/retrofit2/http/QueryMap.java deleted file mode 100644 index bd38143da..000000000 --- a/retrofit/src/main/java/retrofit2/http/QueryMap.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.lang.reflect.Type; - -import retrofit2.Retrofit; - -/** - * Query parameter keys and values appended to the URL. - * - *

Values are converted to strings using {@link Retrofit#stringConverter(Type, Annotation[])} (or - * {@link Object#toString()}, if no matching string converter is installed). - * - *

Simple Example: - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@QueryMap Map<String, String> filters);
- * 
- *

- * Calling with {@code foo.friends(ImmutableMap.of("group", "coworker", "age", "42"))} yields {@code - * /friends?group=coworker&age=42}. - * - *

Map keys and values representing parameter values are URL encoded by default. Specify {@link - * #encoded() encoded=true} to change this behavior. - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@QueryMap(encoded=true) Map<String, String> filters);
- * 
- *

- * Calling with {@code foo.list(ImmutableMap.of("group", "coworker+bowling"))} yields {@code - * /friends?group=coworker+bowling}. - * - *

A {@code null} value for the map, as a key, or as a value is not allowed. - * - * @see Query - * @see QueryName - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface QueryMap { - /** - * Specifies whether parameter names and values are already URL encoded. - */ - boolean encoded() default false; -} diff --git a/retrofit/src/main/java/retrofit2/http/QueryName.java b/retrofit/src/main/java/retrofit2/http/QueryName.java deleted file mode 100644 index da7e28681..000000000 --- a/retrofit/src/main/java/retrofit2/http/QueryName.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2013 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Query parameter appended to the URL that has no value. - * - *

Passing a {@link java.util.List List} or array will result in a query parameter for each - * non-{@code null} item. - * - *

Simple Example: - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@QueryName String filter);
- * 
- *

- * Calling with {@code foo.friends("contains(Bob)")} yields {@code /friends?contains(Bob)}. - * - *

Array/Varargs Example: - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@QueryName String... filters);
- * 
- *

- * Calling with {@code foo.friends("contains(Bob)", "age(42)")} yields {@code - * /friends?contains(Bob)&age(42)}. - * - *

Parameter names are URL encoded by default. Specify {@link #encoded() encoded=true} to change - * this behavior. - * - *


- * @GET("/friends")
- * Call<ResponseBody> friends(@QueryName(encoded=true) String filter);
- * 
- *

- * Calling with {@code foo.friends("name+age"))} yields {@code /friends?name+age}. - * - * @see Query - * @see QueryMap - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface QueryName { - /** - * Specifies whether the parameter is already URL encoded. - */ - boolean encoded() default false; -} diff --git a/retrofit/src/main/java/retrofit2/http/Streaming.java b/retrofit/src/main/java/retrofit2/http/Streaming.java deleted file mode 100644 index c957ded72..000000000 --- a/retrofit/src/main/java/retrofit2/http/Streaming.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2014 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.ResponseBody; - -/** - * Treat the response body on methods returning {@link ResponseBody ResponseBody} as is, i.e. - * without converting the body to {@code byte[]}. - */ -@Documented -@Target(METHOD) -@Retention(RUNTIME) -public @interface Streaming { -} diff --git a/retrofit/src/main/java/retrofit2/http/Tag.java b/retrofit/src/main/java/retrofit2/http/Tag.java deleted file mode 100644 index 6cd5b63b8..000000000 --- a/retrofit/src/main/java/retrofit2/http/Tag.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Adds the argument instance as a request tag using the type as the key. - * - *


- * @GET("/")
- * Call<ResponseBody> foo(@Tag String tag);
- * 
- *

- * Tag arguments may be {@code null} which will omit them from the request. Passing a parameterized - * type such as {@code List} will use the raw type (i.e., {@code List.class}) as the key. - * Duplicate tag types are not allowed. - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface Tag { -} diff --git a/retrofit/src/main/java/retrofit2/http/Url.java b/retrofit/src/main/java/retrofit2/http/Url.java deleted file mode 100644 index 9d735189b..000000000 --- a/retrofit/src/main/java/retrofit2/http/Url.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package retrofit2.http; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import okhttp3.HttpUrl; -import retrofit2.Retrofit; - -/** - * URL resolved against the {@linkplain Retrofit#baseUrl() base URL}. - * - *


- * @GET
- * Call<ResponseBody> list(@Url String url);
- * 
- * - *

See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how the - * value will be resolved against a base URL to create the full endpoint URL. - */ -@Documented -@Target(PARAMETER) -@Retention(RUNTIME) -public @interface Url { -} diff --git a/settings.gradle b/settings.gradle index f242c7e45..19c80f3f9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -include ":app_fenrir", ":app_filegallery", ":fenrir_common", ":picasso3", ":firebase-installations", ":image", ":retrofit", ":material", ":preference", ":viewpager2" +include ":app_fenrir", ":app_filegallery", ":fenrir_common", ":picasso3", ":firebase-installations", ":image", ":material", ":preference", ":viewpager2" include ":camera2" //include ":libfenrir" \ No newline at end of file