diff --git a/app/build.gradle.kts b/app/build.gradle.kts index eeb1291..50dfae5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -70,7 +70,7 @@ dependencies { implementation(libs.firebase.crashlytics) implementation(libs.okhttp) implementation(libs.retrofit) - implementation(libs.retrofit.gson) + implementation(libs.converter.moshi) implementation(libs.glide) implementation(libs.media3.exoplayer) implementation(libs.media3.exoplayer.dash) @@ -78,6 +78,7 @@ dependencies { implementation(libs.media3.ui.compose) implementation(libs.taptargetview) ksp(libs.androidx.room.compiler) + ksp(libs.moshi.kotlin.codegen) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index e4fa445..9881634 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -44,10 +44,6 @@ -keep class org.cssnr.zipline.api.ZiplineApi$* { *; } -keep class org.cssnr.zipline.api.FeedbackApi$* { *; } --keepclassmembers class * { - @com.google.gson.annotations.SerializedName ; -} - ## https://github.com/square/retrofit/blob/trunk/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro # Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and diff --git a/app/src/main/java/org/cssnr/zipline/api/FeedbackApi.kt b/app/src/main/java/org/cssnr/zipline/api/FeedbackApi.kt index 07cc226..6f2be2e 100644 --- a/app/src/main/java/org/cssnr/zipline/api/FeedbackApi.kt +++ b/app/src/main/java/org/cssnr/zipline/api/FeedbackApi.kt @@ -1,15 +1,18 @@ package org.cssnr.zipline.api import android.content.Context +import android.os.Build import android.util.Log -import com.google.gson.GsonBuilder +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.squareup.moshi.Moshi import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.ResponseBody.Companion.toResponseBody import org.cssnr.zipline.R import retrofit2.Response import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.http.Body import retrofit2.http.POST @@ -30,7 +33,7 @@ class FeedbackApi(val context: Context) { suspend fun sendFeedback(messageText: String): Response { Log.d("sendFeedback", "messageText: $messageText") val feedbackText = - "**${context.getString(R.string.app_name)}** `v${versionName}`\n```\n${messageText}\n```" + "**${context.getString(R.string.app_name)}** `v${versionName}` API **${Build.VERSION.SDK_INT}**\n```\n${messageText}\n```" Log.d("sendFeedback", "feedbackText: $feedbackText") val message = Message(content = feedbackText) Log.d("sendFeedback", "message: $message") @@ -42,7 +45,9 @@ class FeedbackApi(val context: Context) { } } + @JsonClass(generateAdapter = true) data class Message( + @Json(name = "content") val content: String ) @@ -62,10 +67,10 @@ class FeedbackApi(val context: Context) { chain.proceed(request) } .build() - val gson = GsonBuilder().create() + val moshi = Moshi.Builder().build() return Retrofit.Builder() .baseUrl(RELAY_URL) - .addConverterFactory(GsonConverterFactory.create(gson)) + .addConverterFactory(MoshiConverterFactory.create(moshi)) .client(client) .build() } diff --git a/app/src/main/java/org/cssnr/zipline/api/ZiplineApi.kt b/app/src/main/java/org/cssnr/zipline/api/ZiplineApi.kt index afd048d..9200546 100644 --- a/app/src/main/java/org/cssnr/zipline/api/ZiplineApi.kt +++ b/app/src/main/java/org/cssnr/zipline/api/ZiplineApi.kt @@ -5,7 +5,9 @@ import android.util.Log import android.webkit.CookieManager import androidx.core.content.edit import androidx.preference.PreferenceManager -import com.google.gson.annotations.SerializedName +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.squareup.moshi.Moshi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.Cookie @@ -20,7 +22,7 @@ import okhttp3.RequestBody.Companion.toRequestBody import org.cssnr.zipline.R import retrofit2.Response import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Header @@ -31,14 +33,14 @@ import java.io.InputStream import java.net.URLConnection class ZiplineApi(private val context: Context, url: String? = null) { + val api: ApiService private var ziplineUrl: String private var ziplineToken: String - private val preferences by lazy { PreferenceManager.getDefaultSharedPreferences(context) } - private lateinit var cookieJar: SimpleCookieJar - private lateinit var client: OkHttpClient + + private val preferences by lazy { PreferenceManager.getDefaultSharedPreferences(context) } init { ziplineUrl = url ?: preferences.getString("ziplineUrl", null) ?: "" @@ -164,7 +166,7 @@ class ZiplineApi(private val context: Context, url: String? = null) { val userAgent = "${context.getString(R.string.app_name)}/${versionName}" Log.d("createRetrofit", "versionName: $versionName") cookieJar = SimpleCookieJar() - client = OkHttpClient.Builder() + val client = OkHttpClient.Builder() .cookieJar(cookieJar) .addInterceptor { chain -> val request = chain.request().newBuilder() @@ -174,16 +176,19 @@ class ZiplineApi(private val context: Context, url: String? = null) { chain.proceed(request) } .build() + val moshi = Moshi.Builder().build() return Retrofit.Builder() .baseUrl(baseUrl) - .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(MoshiConverterFactory.create(moshi)) .client(client) .build() } interface ApiService { @POST("auth/login") - suspend fun postLogin(@Body request: LoginRequest): Response + suspend fun postLogin( + @Body request: LoginRequest, + ): Response @GET("user/token") suspend fun getToken(): TokenResponse @@ -204,54 +209,61 @@ class ZiplineApi(private val context: Context, url: String? = null) { ): Response } + @JsonClass(generateAdapter = true) data class LoginRequest( val username: String, val password: String, ) + @JsonClass(generateAdapter = true) data class TokenResponse( - val token: String + val token: String, ) + @JsonClass(generateAdapter = true) data class FileResponse( - val files: List + val files: List, ) + @JsonClass(generateAdapter = true) data class UploadedFile( val id: String, val type: String, val url: String, ) + @JsonClass(generateAdapter = true) data class ShortRequest( val destination: String, val vanity: String?, val enabled: Boolean, ) + @JsonClass(generateAdapter = true) data class ShortResponse( val id: String, val createdAt: String, val updatedAt: String, val code: String, - val vanity: String, + val vanity: String?, val destination: String, val views: Int, val maxViews: Int?, val enabled: Boolean, val userId: String, - val url: String + val url: String, ) + @JsonClass(generateAdapter = true) data class StatsResponse( - @SerializedName("filesUploaded") val filesUploaded: Int, - @SerializedName("favoriteFiles") val favoriteFiles: Int, - @SerializedName("views") val views: Int, - @SerializedName("avgViews") val avgViews: Double, - @SerializedName("storageUsed") val storageUsed: Long, - @SerializedName("avgStorageUsed") val avgStorageUsed: Double, - @SerializedName("urlsCreated") val urlsCreated: Int, - @SerializedName("urlViews") val urlViews: Int, + @Json(name = "filesUploaded") val filesUploaded: Int, + @Json(name = "favoriteFiles") val favoriteFiles: Int, + @Json(name = "views") val views: Int, + @Json(name = "avgViews") val avgViews: Double, + @Json(name = "storageUsed") val storageUsed: Long, + @Json(name = "avgStorageUsed") val avgStorageUsed: Double, + @Json(name = "urlsCreated") val urlsCreated: Int, + @Json(name = "urlViews") val urlViews: Int, ) @@ -276,27 +288,3 @@ class ZiplineApi(private val context: Context, url: String? = null) { //} } } - - -//data class LoginResponse( -// val user: TokenUser -//) - -//data class TokenUser( -// val id: String, -// val username: String, -// val token: String, -//) - -//fun getFileNameFromUri(context: Context, uri: Uri): String? { -// var fileName: String? = null -// context.contentResolver.query(uri, null, null, null, null).use { cursor -> -// if (cursor != null && cursor.moveToFirst()) { -// val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) -// if (nameIndex != -1) { -// fileName = cursor.getString(nameIndex) -// } -// } -// } -// return fileName -//} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8fb3f4b..9dc165f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,6 +24,7 @@ crashlyticsPlugin = "3.0.4" okhttp = "4.12.0" retrofit = "3.0.0" +moshiKotlinCodegen = "1.15.2" glide = "5.0.0-rc01" media3 = "1.7.1" taptargetview = "1.15.0" @@ -53,7 +54,8 @@ firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashly okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } -retrofit-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" } +converter-moshi = { group = "com.squareup.retrofit2", name = "converter-moshi", version.ref = "retrofit" } +moshi-kotlin-codegen = { group = "com.squareup.moshi", name = "moshi-kotlin-codegen", version.ref = "moshiKotlinCodegen" } glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" } #okhttp3-integration = { group = "com.github.bumptech.glide", name = "okhttp3-integration", version.ref = "glide" } media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }