Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Retrofit to Ktor #13

Merged
merged 3 commits into from
May 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ A minimalist Android Launcher built with Jetpack Compose
- [Hilt-ViewModel](https://developer.android.com/training/dependency-injection/hilt-jetpack) - DI for injecting ViewModel.
- [Jetpack Compose UI Toolkit](https://developer.android.com/jetpack/compose) - Modern UI development toolkit.
- [Accompanist](https://chrisbanes.github.io/accompanist/) - A collection of extension libraries for Jetpack Compose.
- [Retrofit](https://square.github.io/retrofit/) - A type-safe HTTP client for Android and Java.
- [Ktor](https://ktor.io/) - Lightweight & simple asynchronous HTTP Client
- [ktlint](https://ktlint.github.io/) - An anti-bikeshedding Kotlin linter with built-in formatter.


Expand Down
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ android {

buildTypes {
getByName("debug") {
applicationIdSuffix = ".debug"
isMinifyEnabled = false
isShrinkResources = false
isTestCoverageEnabled = true
Expand Down
11 changes: 1 addition & 10 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,11 @@

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

# Firebase
#-keep class com.google.firebase.** { *; }

# --------------- Begin: Retrofit ---------------
-keepclassmembers class dev.mslalith.focuslauncher.data.network.entities.QuotesApiResponse {
public protected private *;
# Keep default members & functions
!public !protected !private *;
}
# --------------- End: Retrofit ---------------

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class WidgetsViewModel @Inject constructor(

init {
launch {
quotesRepo.nextRandomQuote()
quotesRepo.addInitialQuotesIfNeeded()
}
launch {
clockRepo.currentInstantStateFlow.collectLatest { instant ->
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ buildscript {
classpath(Libs.buildToolsGradle)
classpath(Libs.buildToolsKotlinGradlePlugin)
classpath(Libs.buildToolsHiltAndroidGradlePlugin)
classpath(Libs.buildToolsKotlinSerialization)
classpath(Libs.buildToolsKotlinxKover)
}
}
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/ConfigData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ object ConfigData {
const val TARGET_SDK = 31
const val BUILD_TOOLS = "30.0.3"

const val VERSION_CODE = 2
const val VERSION_CODE = 3
const val VERSION_NAME = "0.1.0"
}
16 changes: 8 additions & 8 deletions buildSrc/src/main/kotlin/Libs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ object Libs {
const val buildToolsKotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.KOTLIN}"
const val buildToolsHiltAndroidGradlePlugin = "com.google.dagger:hilt-android-gradle-plugin:${Versions.HILT}"
const val buildToolsKotlinxKover = "org.jetbrains.kotlinx:kover:${Versions.KOTLINX_KOVER}"
const val buildToolsKotlinSerialization = "org.jetbrains.kotlin:kotlin-serialization:${Versions.KOTLIN_SERIALIZATION}"

const val coreKtx = "androidx.core:core-ktx:${Versions.KOTLIN_CORE_KTX}"
const val kotlinxDateTime = "org.jetbrains.kotlinx:kotlinx-datetime:${Versions.KOTLINX_DATETIME}"
const val kotlinxCoroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.KOTLIN_COROUTINES}"
const val ktorClientAndroid = "io.ktor:ktor-client-android:${Versions.KTOR_CLIENT}"
const val ktorClientSerialization = "io.ktor:ktor-client-serialization:${Versions.KTOR_CLIENT}"

const val googleMaterial = "com.google.android.material:material:${Versions.GOOGLE_MATERIAL}"
const val googlePlayAppUpdate = "com.google.android.play:app-update:${Versions.GOOGLE_PLAY_APP_UPDATE}"
Expand Down Expand Up @@ -42,9 +45,6 @@ object Libs {
const val accompanistInsets = "com.google.accompanist:accompanist-insets:${Versions.ACCOMPANIST}"
const val accompanistFlowLayout = "com.google.accompanist:accompanist-flowlayout:${Versions.ACCOMPANIST}"

const val retrofit = "com.squareup.retrofit2:retrofit:${Versions.RETROFIT}"
const val retrofitGsonConverter = "com.squareup.retrofit2:converter-gson:${Versions.RETROFIT}"

const val thirdSunCalc = "org.shredzone.commons:commons-suncalc:${Versions.THIRD_SUNCALC}"

const val testJUnit = "junit:junit:${Versions.TEST_JUNIT}"
Expand All @@ -66,6 +66,11 @@ fun DependencyHandler.kotlin() {
implementation(Libs.kotlinxDateTime)
}

fun DependencyHandler.ktorClient() {
implementation(Libs.ktorClientAndroid)
implementation(Libs.ktorClientSerialization)
}

fun DependencyHandler.compose() {
implementation(Libs.composeMaterial)
implementation(Libs.composeUi)
Expand Down Expand Up @@ -128,11 +133,6 @@ fun DependencyHandler.accompanist() {
implementation(Libs.accompanistFlowLayout)
}

fun DependencyHandler.retrofit() {
implementation(Libs.retrofit)
implementation(Libs.retrofitGsonConverter)
}

fun DependencyHandler.thirdPartyLibs() {
implementation(Libs.thirdSunCalc)
}
Expand Down
3 changes: 2 additions & 1 deletion buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ object Versions {
const val KOTLIN_CORE_KTX = "1.8.0-alpha07"
const val KOTLINX_DATETIME = "0.3.2"
const val KOTLIN_COROUTINES = "1.6.1"
const val KOTLIN_SERIALIZATION = "1.6.10"
const val KTOR_CLIENT = "1.6.4"

const val GOOGLE_MATERIAL = "1.7.0-alpha01"
const val GOOGLE_PLAY_APP_UPDATE = "2.0.0"
Expand All @@ -24,7 +26,6 @@ object Versions {

const val DATASTORE = "1.0.0"
const val ROOM = "2.5.0-alpha01"
const val RETROFIT = "2.9.0"
const val ACCOMPANIST = "0.24.6-alpha"

const val THIRD_SUNCALC = "3.5"
Expand Down
5 changes: 3 additions & 2 deletions data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id("com.android.library")
kotlin("android")
kotlin("kapt")
id("kotlinx-serialization")
id("dagger.hilt.android.plugin")
}

Expand Down Expand Up @@ -34,7 +35,7 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
release {
isMinifyEnabled = false
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
Expand Down Expand Up @@ -64,9 +65,9 @@ dependencies {
hiltAndroid()
room()
dataStore()
ktorClient()
playInAppUpdate()

implementation(Libs.retrofitGsonConverter)
implementation(Libs.kotlinxDateTime)
implementation(Libs.thirdSunCalc)

Expand Down
41 changes: 41 additions & 0 deletions data/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# --------------- Begin: Kotlinx Serialization ---------------
# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}

# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <2>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}

# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
# If you have any, uncomment and replace classes with those containing named companion objects.
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
#-if @kotlinx.serialization.Serializable class
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
#com.example.myapplication.HasNamedCompanion2
#{
# static **$* *;
#}
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
# static <1>$$serializer INSTANCE;
#}
# --------------- End: Kotlinx Serialization ---------------
2 changes: 1 addition & 1 deletion data/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import dev.mslalith.focuslauncher.data.database.entities.AppRoom
import dev.mslalith.focuslauncher.data.database.entities.FavoriteAppRoom
import dev.mslalith.focuslauncher.data.database.entities.HiddenAppRoom
import dev.mslalith.focuslauncher.data.database.entities.QuoteRoom
import dev.mslalith.focuslauncher.data.utils.Converters
import dev.mslalith.focuslauncher.data.database.typeconverter.Converters

@Database(
entities = [
Expand All @@ -23,7 +23,6 @@ import dev.mslalith.focuslauncher.data.utils.Converters
version = 1,
exportSchema = false
)
// TODO: @ms: are these Converters really necessary
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.mslalith.focuslauncher.data.database.typeconverter

import androidx.room.TypeConverter
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json

class Converters {

@TypeConverter
fun jsonToStringList(json: String): List<String> {
return Json.decodeFromString(ListSerializer(String.serializer()), json)
}

@TypeConverter
fun stringListToJson(list: List<String>): String {
return Json.encodeToString(ListSerializer(String.serializer()), list)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,41 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dev.mslalith.focuslauncher.data.network.api.QuotesApi
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import dev.mslalith.focuslauncher.data.network.api.QuotesApiKtorImpl
import io.ktor.client.HttpClient
import io.ktor.client.engine.android.Android
import io.ktor.client.features.HttpTimeout
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

private const val QUOTES_BASE_URL = "https://api.quotable.io"
@Provides
@Singleton
fun provideKtorClient(): HttpClient = HttpClient(Android) {
install(JsonFeature) {
serializer = KotlinxSerializer(
Json {
ignoreUnknownKeys = true
isLenient = true
encodeDefaults = false
}
)
}

val timeout = 15_000L
install(HttpTimeout) {
requestTimeoutMillis = timeout
connectTimeoutMillis = timeout
socketTimeoutMillis = timeout
}
}

@Provides
@Singleton
fun provideQuoteApi(): QuotesApi = Retrofit.Builder().baseUrl(QUOTES_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(QuotesApi::class.java)
fun provideQuoteApi(httpClient: HttpClient): QuotesApi = QuotesApiKtorImpl(httpClient)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class QuoteResponseToRoomMapper : Mapper<QuoteResponse, QuoteRoom> {
override fun fromEntity(data: QuoteResponse) = QuoteRoom(
id = data.id,
quote = data.quote,
author = data.quote,
author = data.author,
authorSlug = data.authorSlug,
length = data.length,
tags = data.tags
Expand All @@ -16,7 +16,7 @@ class QuoteResponseToRoomMapper : Mapper<QuoteResponse, QuoteRoom> {
override fun toEntity(data: QuoteRoom) = QuoteResponse(
id = data.id,
quote = data.quote,
author = data.quote,
author = data.author,
authorSlug = data.authorSlug,
length = data.length,
tags = data.tags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class QuoteToRoomMapper @Inject constructor(
override fun fromEntity(data: QuoteRoom) = Quote(
id = data.id,
quote = data.quote,
author = data.quote
author = data.author
)

override fun toEntity(data: Quote): QuoteRoom {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,27 @@ package dev.mslalith.focuslauncher.data.network.api

import dev.mslalith.focuslauncher.data.network.entities.QuotesApiResponse
import dev.mslalith.focuslauncher.data.utils.Constants
import retrofit2.http.GET
import retrofit2.http.Query
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.request.url
import javax.inject.Inject

interface QuotesApi {
@GET("/quotes")
suspend fun getQuotes(
@Query("page") page: Int = 1,
@Query("limit") limit: Int = Constants.Defaults.QUOTES_LIMIT_PER_PAGE,
page: Int = 1,
limit: Int = Constants.Defaults.QUOTES_LIMIT_PER_PAGE,
): QuotesApiResponse
}

internal class QuotesApiKtorImpl @Inject constructor(
private val httpClient: HttpClient,
) : QuotesApi {

private val baseUrl = "https://api.quotable.io"

override suspend fun getQuotes(page: Int, limit: Int): QuotesApiResponse {
return httpClient.get {
url("$baseUrl/quotes?page=$page&limit=$limit")
}
}
}