Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2025 The Android Open Source Project
*
* 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
*
* https://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 com.google.samples.apps.nowinandroid.core.network.api

import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.network.retrofit.NetworkResponse
import retrofit2.http.GET
import retrofit2.http.Query

/**
* Retrofit API declaration for News Resource API
*/
internal interface NewsResourceApi {
@GET(value = "newsresources")
suspend fun getNewsResources(
@Query("id") ids: List<String>?,
): NetworkResponse<List<NetworkNewsResource>>

@GET(value = "changelists/newsresources")
suspend fun getNewsResourcesChangeList(
@Query("after") after: Int?,
): List<NetworkChangeList>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2025 The Android Open Source Project
*
* 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
*
* https://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 com.google.samples.apps.nowinandroid.core.network.api

import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import com.google.samples.apps.nowinandroid.core.network.retrofit.NetworkResponse
import retrofit2.http.GET
import retrofit2.http.Query

/**
* Retrofit API declaration for Topic API
*/
internal interface TopicApi {
@GET(value = "topics")
suspend fun getTopics(
@Query("id") ids: List<String>?,
): NetworkResponse<List<NetworkTopic>>

@GET(value = "changelists/topics")
suspend fun getTopicChangeList(
@Query("after") after: Int?,
): List<NetworkChangeList>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2025 The Android Open Source Project
*
* 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
*
* https://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 com.google.samples.apps.nowinandroid.core.network.di

import androidx.tracing.trace
import com.google.samples.apps.nowinandroid.core.network.api.NewsResourceApi
import com.google.samples.apps.nowinandroid.core.network.api.TopicApi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import javax.inject.Singleton
import kotlin.jvm.java

@Module
@InstallIn(SingletonComponent::class)
internal object ApiModule {

@Provides
@Singleton
internal fun providesTopicApi(retrofit: Retrofit): TopicApi {
trace("RetrofitNiaNetwork") {
return retrofit.create(TopicApi::class.java)
}
}

@Provides
@Singleton
internal fun providesNewsResourceApi(retrofit: Retrofit): NewsResourceApi {
trace("RetrofitNiaNetwork") {
return retrofit.create(NewsResourceApi::class.java)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@ import coil.decode.SvgDecoder
import coil.util.DebugLogger
import com.google.samples.apps.nowinandroid.core.network.BuildConfig
import com.google.samples.apps.nowinandroid.core.network.demo.DemoAssetManager
import com.google.samples.apps.nowinandroid.core.network.retrofit.NIA_BASE_URL
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.Call
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.kotlinx.serialization.asConverterFactory
import javax.inject.Singleton

@Module
Expand Down Expand Up @@ -92,4 +96,23 @@ internal object NetworkModule {
}
.build()
}

@Provides
@Singleton
fun provideRetrofit(
networkJson: Json,
okhttpCallFactory: dagger.Lazy<Call.Factory>,
): Retrofit {
trace("RetrofitNiaNetwork") {
return Retrofit.Builder()
.baseUrl(NIA_BASE_URL)
// We use callFactory lambda here with dagger.Lazy<Call.Factory>
// to prevent initializing OkHttp on the main thread.
.callFactory { okhttpCallFactory.get().newCall(it) }
.addConverterFactory(
networkJson.asConverterFactory("application/json".toMediaType()),
)
.build()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,25 @@

package com.google.samples.apps.nowinandroid.core.network.retrofit

import androidx.tracing.trace
import com.google.samples.apps.nowinandroid.core.network.BuildConfig
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.api.NewsResourceApi
import com.google.samples.apps.nowinandroid.core.network.api.TopicApi
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okhttp3.Call
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit
import retrofit2.converter.kotlinx.serialization.asConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
import javax.inject.Inject
import javax.inject.Singleton

/**
* Retrofit API declaration for NIA Network API
*/
private interface RetrofitNiaNetworkApi {
@GET(value = "topics")
suspend fun getTopics(
@Query("id") ids: List<String>?,
): NetworkResponse<List<NetworkTopic>>

@GET(value = "newsresources")
suspend fun getNewsResources(
@Query("id") ids: List<String>?,
): NetworkResponse<List<NetworkNewsResource>>

@GET(value = "changelists/topics")
suspend fun getTopicChangeList(
@Query("after") after: Int?,
): List<NetworkChangeList>

@GET(value = "changelists/newsresources")
suspend fun getNewsResourcesChangeList(
@Query("after") after: Int?,
): List<NetworkChangeList>
}

private const val NIA_BASE_URL = BuildConfig.BACKEND_URL
internal const val NIA_BASE_URL = BuildConfig.BACKEND_URL

/**
* Wrapper for data provided from the [NIA_BASE_URL]
*/
@Serializable
private data class NetworkResponse<T>(
internal data class NetworkResponse<T>(
val data: T,
)

Expand All @@ -73,32 +43,19 @@ private data class NetworkResponse<T>(
*/
@Singleton
internal class RetrofitNiaNetwork @Inject constructor(
networkJson: Json,
okhttpCallFactory: dagger.Lazy<Call.Factory>,
val newsResourceApi: NewsResourceApi,
val topicApi: TopicApi,
) : NiaNetworkDataSource {

private val networkApi = trace("RetrofitNiaNetwork") {
Retrofit.Builder()
.baseUrl(NIA_BASE_URL)
// We use callFactory lambda here with dagger.Lazy<Call.Factory>
// to prevent initializing OkHttp on the main thread.
.callFactory { okhttpCallFactory.get().newCall(it) }
.addConverterFactory(
networkJson.asConverterFactory("application/json".toMediaType()),
)
.build()
.create(RetrofitNiaNetworkApi::class.java)
}

override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> =
networkApi.getTopics(ids = ids).data
topicApi.getTopics(ids = ids).data

override suspend fun getNewsResources(ids: List<String>?): List<NetworkNewsResource> =
networkApi.getNewsResources(ids = ids).data
newsResourceApi.getNewsResources(ids = ids).data

override suspend fun getTopicChangeList(after: Int?): List<NetworkChangeList> =
networkApi.getTopicChangeList(after = after)
topicApi.getTopicChangeList(after = after)

override suspend fun getNewsResourceChangeList(after: Int?): List<NetworkChangeList> =
networkApi.getNewsResourcesChangeList(after = after)
newsResourceApi.getNewsResourcesChangeList(after = after)
}
Loading