Skip to content

Commit 19894ca

Browse files
author
Alexnaldo Santos
committed
init retrofit api with koin
1 parent ac37077 commit 19894ca

File tree

19 files changed

+304
-4
lines changed

19 files changed

+304
-4
lines changed

Diff for: app/build.gradle

+9
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ dependencies {
5656
implementation "androidx.navigation:navigation-compose:$nav_version"
5757
implementation project(path: ':challenge_coreui')
5858
implementation project(path: ':challenge_home')
59+
// Koin
60+
implementation "io.insert-koin:koin-core:$koin_version"
61+
implementation "io.insert-koin:koin-android:$koin_version"
62+
implementation "io.insert-koin:koin-android-compat:$koin_version"
63+
implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
64+
implementation "io.insert-koin:koin-androidx-navigation:$koin_version"
65+
implementation "io.insert-koin:koin-androidx-compose:$koin_version"
66+
67+
implementation project(path: ':challenge_model')
5968
testImplementation 'junit:junit:4.13.2'
6069
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
6170
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

Diff for: app/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package="com.amedigital.challenge_android">
44
<uses-permission android:name="android.permission.INTERNET" />
55
<application
6+
android:name=".CustomApp"
67
android:allowBackup="true"
78
android:icon="@mipmap/ic_launcher"
89
android:label="@string/app_name"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.amedigital.challenge_android
2+
3+
import com.amedigital.challenge_home.HomeViewModel
4+
import org.koin.dsl.module
5+
import com.amedigital.challenge_model.api.NetworkUtils
6+
import com.amedigital.challenge_model.repositories.BannerRepository
7+
import com.amedigital.challenge_model.repositories.BannerRepositoryImpl
8+
import org.koin.androidx.viewmodel.dsl.viewModel
9+
10+
val appModules = module {
11+
single<BannerRepository> { BannerRepositoryImpl(get()) }
12+
single { NetworkUtils.createApiService() }
13+
viewModel { HomeViewModel(get()) }
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.amedigital.challenge_android
2+
3+
import android.app.Application
4+
import org.koin.android.ext.koin.androidContext
5+
import org.koin.core.context.startKoin
6+
7+
class CustomApp : Application() {
8+
9+
override fun onCreate() {
10+
super.onCreate()
11+
setupKoin()
12+
}
13+
14+
private fun setupKoin() {
15+
startKoin {
16+
androidContext(this@CustomApp)
17+
modules(listOf(appModules))
18+
}
19+
}
20+
}

Diff for: build.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ buildscript {
33
compose_version = '1.2.0-alpha08'
44
nav_version = '2.4.2'
55
kotlin_version = '1.6.20'
6+
koin_version= '3.2.0-beta-1'
7+
lifecycle_version = '2.4.1'
68
}
79
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
810
plugins {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.amedigital.coreui.widgets
2+
3+
import androidx.compose.foundation.layout.fillMaxSize
4+
import androidx.compose.foundation.layout.wrapContentSize
5+
import androidx.compose.material.CircularProgressIndicator
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.Alignment
8+
import androidx.compose.ui.Modifier
9+
10+
@Composable
11+
fun WaitingIndicator(){
12+
CircularProgressIndicator(
13+
modifier = Modifier
14+
.fillMaxSize()
15+
.wrapContentSize(
16+
Alignment.Center
17+
)
18+
)
19+
}

Diff for: challenge_home/build.gradle

+16
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ dependencies {
5454
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
5555
implementation 'androidx.activity:activity-compose:1.4.0'
5656
implementation("io.coil-kt:coil-compose:2.0.0-rc03")
57+
// // LiveData
58+
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
59+
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
60+
// ViewModel
61+
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
62+
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
63+
implementation 'androidx.fragment:fragment-ktx:1.4.1'
64+
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
65+
// Koin
66+
implementation "io.insert-koin:koin-core:$koin_version"
67+
implementation "io.insert-koin:koin-android:$koin_version"
68+
implementation "io.insert-koin:koin-android-compat:$koin_version"
69+
implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
70+
implementation "io.insert-koin:koin-androidx-navigation:$koin_version"
71+
implementation "io.insert-koin:koin-androidx-compose:$koin_version"
72+
5773
implementation project(path: ':challenge_coreui')
5874
implementation project(path: ':challenge_model')
5975
implementation project(path: ':challenge_produto')

Diff for: challenge_home/src/main/java/com/amedigital/challenge_home/Home.kt

+24-3
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,54 @@ import android.content.Intent
55
import android.util.Log
66
import androidx.compose.foundation.layout.Column
77
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.wrapContentSize
89
import androidx.compose.foundation.rememberScrollState
910
import androidx.compose.foundation.verticalScroll
11+
import androidx.compose.material.CircularProgressIndicator
12+
import androidx.compose.material.Text
1013
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.livedata.observeAsState
15+
import androidx.compose.ui.Alignment
1116
import androidx.compose.ui.Modifier
1217
import androidx.compose.ui.platform.LocalContext
18+
import com.amedigital.challenge_model.Banner
1319
import com.amedigital.challenge_model.Categoria
20+
import com.amedigital.challenge_model.api.Resource
1421
import com.amedigital.challenge_model.fakeBanners
1522
import com.amedigital.challenge_model.fakeCategorias
1623
import com.amedigital.challenge_model.fakeProdutos
1724
import com.amedigital.challenge_produto.CategoriaActivity
1825
import com.amedigital.challenge_produto.ListaCategorias
1926
import com.amedigital.challenge_produto.ListaProdutos
2027
import com.amedigital.challenge_produto.ProdutoActivity
28+
import com.amedigital.challenge_produto.widgets.LogAndShowErrorPanel
2129
import com.amedigital.coreui.widgets.Banner
30+
import com.amedigital.coreui.widgets.WaitingIndicator
31+
import org.koin.androidx.compose.getViewModel
2232

2333
@Composable
2434
fun Home() {
35+
val viewModel = getViewModel<HomeViewModel>()
36+
val bannerState = viewModel.banner.observeAsState()
37+
38+
when (val banner = bannerState.value) {
39+
is Resource.Requesting -> WaitingIndicator()
40+
is Resource.Failure -> LogAndShowErrorPanel(banner.throwable)
41+
is Resource.Success -> HomeView(banner.value)
42+
}
43+
}
44+
45+
@Composable
46+
private fun HomeView(images: List<Banner>) {
2547
val context = LocalContext.current
26-
val homeTag = "HOME"
2748
val scrollState = rememberScrollState()
2849
Column(
2950
modifier = Modifier
3051
.fillMaxSize()
3152
.verticalScroll(scrollState)
3253
) {
33-
Banner(fakeBanners.map { it.linkUrl }, onImageClick = { image ->
34-
Log.d(homeTag, image)
54+
Banner(images.map { it.linkUrl }, onImageClick = { image ->
55+
Log.d("HOME", image)
3556
})
3657
ListaCategorias(fakeCategorias, onCategoriaClick = { categoria ->
3758
CategoriaActivity.gotoCategoria(context, categoria)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.amedigital.challenge_home
2+
3+
import androidx.lifecycle.LiveData
4+
import androidx.lifecycle.MutableLiveData
5+
import androidx.lifecycle.ViewModel
6+
import androidx.lifecycle.viewModelScope
7+
import com.amedigital.challenge_model.Banner
8+
import com.amedigital.challenge_model.api.Resource
9+
import com.amedigital.challenge_model.repositories.BannerRepository
10+
import kotlinx.coroutines.launch
11+
12+
class HomeViewModel(private val bannerRepository: BannerRepository) : ViewModel() {
13+
private val _banner: MutableLiveData<Resource<List<Banner>>> = MutableLiveData()
14+
val banner: LiveData<Resource<List<Banner>>>
15+
get() = _banner
16+
17+
init {
18+
loadBanner()
19+
}
20+
21+
fun loadBanner() = viewModelScope.launch {
22+
_banner.value = Resource.Requesting
23+
_banner.value = bannerRepository.getBanner()
24+
}
25+
}

Diff for: challenge_model/build.gradle

+15-1
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,24 @@ android {
3131
}
3232

3333
dependencies {
34-
3534
implementation 'androidx.core:core-ktx:1.7.0'
3635
implementation 'androidx.appcompat:appcompat:1.4.1'
3736
implementation 'com.google.android.material:material:1.5.0'
37+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
38+
// Koin
39+
implementation "io.insert-koin:koin-core:$koin_version"
40+
implementation "io.insert-koin:koin-android:$koin_version"
41+
implementation "io.insert-koin:koin-android-compat:$koin_version"
42+
implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
43+
implementation "io.insert-koin:koin-androidx-navigation:$koin_version"
44+
implementation "io.insert-koin:koin-androidx-compose:$koin_version"
45+
// retrofit
46+
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
47+
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
48+
// okhttp
49+
implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"
50+
implementation "com.squareup.okhttp3:okhttp:3.14.9"
51+
// tests
3852
testImplementation 'junit:junit:4.13.2'
3953
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
4054
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.amedigital.challenge_model.api
2+
3+
import android.os.Parcelable
4+
5+
data class ApiResponse<T>(val data: T)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.amedigital.challenge_model.api
2+
3+
import com.amedigital.challenge_model.Banner
4+
import retrofit2.http.GET
5+
6+
interface LodjinhaApi {
7+
@GET("/banner")
8+
suspend fun getBanner(
9+
): ApiResponse<List<Banner>>
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.amedigital.challenge_model.api
2+
3+
import okhttp3.OkHttpClient
4+
import retrofit2.Retrofit
5+
import retrofit2.converter.gson.GsonConverterFactory
6+
import okhttp3.OkHttpClient.Builder
7+
import okhttp3.logging.HttpLoggingInterceptor
8+
import java.util.concurrent.TimeUnit
9+
10+
object NetworkUtils {
11+
fun createApiService(): LodjinhaApi {
12+
13+
val retrofit = Retrofit.Builder()
14+
.baseUrl("https://alodjinha.herokuapp.com/")
15+
.addConverterFactory(GsonConverterFactory.create())
16+
.client(okHttpClientLog())
17+
.build()
18+
19+
return retrofit.create(LodjinhaApi::class.java)
20+
}
21+
22+
private fun okHttpClientLog(): OkHttpClient {
23+
val client = Builder()
24+
client.addInterceptor(createInterceptor())
25+
.connectTimeout(10, TimeUnit.SECONDS)
26+
.writeTimeout(10, TimeUnit.SECONDS)
27+
.readTimeout(10, TimeUnit.SECONDS)
28+
return client.build()
29+
}
30+
31+
private fun createInterceptor(): HttpLoggingInterceptor {
32+
val interceptor = HttpLoggingInterceptor()
33+
interceptor.level = HttpLoggingInterceptor.Level.BODY
34+
return interceptor
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.amedigital.challenge_model.api
2+
3+
sealed class Resource<out T> {
4+
data class Success<out T>(val value: T) : Resource<T>()
5+
data class Failure(
6+
val throwable: Throwable
7+
) : Resource<Nothing>()
8+
object Requesting : Resource<Nothing>()
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.amedigital.challenge_model.api
2+
3+
import kotlinx.coroutines.Dispatchers
4+
import kotlinx.coroutines.withContext
5+
6+
object SafeApi {
7+
suspend fun <T> safeCall(
8+
apiCall: suspend () -> T
9+
): Resource<T> {
10+
return withContext(Dispatchers.IO) {
11+
try {
12+
Resource.Success(apiCall.invoke())
13+
} catch (throwable: Throwable) {
14+
Resource.Failure(throwable)
15+
}
16+
}
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.amedigital.challenge_model.repositories
2+
3+
import com.amedigital.challenge_model.Banner
4+
import com.amedigital.challenge_model.api.Resource
5+
6+
interface BannerRepository {
7+
suspend fun getBanner(): Resource<List<Banner>>
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.amedigital.challenge_model.repositories
2+
3+
import com.amedigital.challenge_model.Banner
4+
import com.amedigital.challenge_model.api.LodjinhaApi
5+
import com.amedigital.challenge_model.api.Resource
6+
import com.amedigital.challenge_model.api.SafeApi
7+
8+
open class BannerRepositoryImpl(val api: LodjinhaApi) : BannerRepository {
9+
10+
override suspend fun getBanner(): Resource<List<Banner>> {
11+
val call = SafeApi.safeCall {
12+
api.getBanner()
13+
}
14+
return when (call) {
15+
is Resource.Success -> Resource.Success(call.value.data)
16+
is Resource.Failure -> call
17+
else -> Resource.Requesting
18+
}
19+
}
20+
21+
}

Diff for: challenge_produto/build.gradle

+8
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ dependencies {
5454
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
5555
implementation 'androidx.activity:activity-compose:1.4.0'
5656
implementation "androidx.navigation:navigation-compose:$nav_version"
57+
// Koin
58+
implementation "io.insert-koin:koin-core:$koin_version"
59+
implementation "io.insert-koin:koin-android:$koin_version"
60+
implementation "io.insert-koin:koin-android-compat:$koin_version"
61+
implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
62+
implementation "io.insert-koin:koin-androidx-navigation:$koin_version"
63+
implementation "io.insert-koin:koin-androidx-compose:$koin_version"
64+
5765
implementation("io.coil-kt:coil-compose:2.0.0-rc03")
5866
implementation project(path: ':challenge_coreui')
5967
implementation project(path: ':challenge_model')

0 commit comments

Comments
 (0)