diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d1786fc..96cd765 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,6 +9,7 @@ plugins { alias(libs.plugins.hilt) alias(libs.plugins.protobuf) alias(libs.plugins.kotlinxSerialization) + alias(libs.plugins.secrets) } android { @@ -58,6 +59,7 @@ android { } buildFeatures { compose = true + buildConfig = true } composeOptions { kotlinCompilerExtensionVersion = "1.5.12" @@ -113,10 +115,15 @@ afterEvaluate { tasks.findByName("kspReleaseKotlin")?.dependsOn(tasks.findByName("generateReleaseProto")) } +secrets { + defaultPropertiesFileName = "secrets.defaults.properties" +} + dependencies { coreLibraryDesugaring(libs.desugar.jdk.libs) testImplementation(libs.junit) + testImplementation(libs.paging.common) androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(libs.androidx.ui.test.junit4) @@ -156,4 +163,9 @@ dependencies { implementation(libs.androidx.navigation.compose) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.kotlinx.serialization.json) + implementation(libs.retrofit.core) + implementation(libs.retrofit.kotlin.serialization) + implementation(libs.okhttp.logging) + implementation(libs.paging.runtime) + implementation(libs.paging.compose) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dfc821f..094cc34 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> + > + + suspend fun detail( + username: String, + toolId: String, + ver: String = "latest", + platform: ToolBaseVo.Platform = ToolBaseVo.Platform.ANDROID + ): Flow> +} \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/data/tool/ToolDataSource.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/data/tool/ToolDataSource.kt index b42835b..7766eb1 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/data/tool/ToolDataSource.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/data/tool/ToolDataSource.kt @@ -1,36 +1,7 @@ package top.fatweb.oxygen.toolbox.data.tool -import kotlinx.coroutines.flow.flowOf -import top.fatweb.oxygen.toolbox.icon.OxygenIcons -import top.fatweb.oxygen.toolbox.model.tool.Tool -import top.fatweb.oxygen.toolbox.model.tool.ToolGroup import javax.inject.Inject -import kotlin.random.Random class ToolDataSource @Inject constructor() { - val tool = flowOf( - (0..100).map { index -> - ToolGroup( - id = "local-base-$index", - title = "${generateRandomString()}-$index", - icon = OxygenIcons.Tool, - tools = (0..20).map { - Tool( - id = "local-base-$index-time-screen-$it", - icon = OxygenIcons.Time, - name = "${generateRandomString()}-$index-$it" - ) - } - ) - } - ) - private fun generateRandomString(length: Int = (1..10).random()): String { - val words = ('a'..'z') + ('A'..'Z') - - return (1..length) - .map { Random.nextInt(0, words.size) } - .map(words::get) - .joinToString("") - } } \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/di/DataModule.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/di/DataModule.kt index b1caa1a..621594a 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/di/DataModule.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/di/DataModule.kt @@ -9,10 +9,10 @@ import top.fatweb.oxygen.toolbox.monitor.NetworkMonitor import top.fatweb.oxygen.toolbox.monitor.TimeZoneBroadcastMonitor import top.fatweb.oxygen.toolbox.monitor.TimeZoneMonitor import top.fatweb.oxygen.toolbox.repository.lib.DepRepository -import top.fatweb.oxygen.toolbox.repository.lib.LocalDepRepository -import top.fatweb.oxygen.toolbox.repository.tool.LocalToolRepository +import top.fatweb.oxygen.toolbox.repository.lib.impl.LocalDepRepository +import top.fatweb.oxygen.toolbox.repository.tool.impl.NetworkToolRepository import top.fatweb.oxygen.toolbox.repository.tool.ToolRepository -import top.fatweb.oxygen.toolbox.repository.userdata.LocalUserDataRepository +import top.fatweb.oxygen.toolbox.repository.userdata.impl.LocalUserDataRepository import top.fatweb.oxygen.toolbox.repository.userdata.UserDataRepository @Module @@ -28,7 +28,7 @@ abstract class DataModule { internal abstract fun bindsUserDataRepository(userDataRepository: LocalUserDataRepository): UserDataRepository @Binds - internal abstract fun bindsToolRepository(toolRepository: LocalToolRepository): ToolRepository + internal abstract fun bindsToolRepository(toolRepository: NetworkToolRepository): ToolRepository @Binds internal abstract fun bindsDepRepository(depRepository: LocalDepRepository): DepRepository diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/di/NetworkModule.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/di/NetworkModule.kt new file mode 100644 index 0000000..16ca519 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/di/NetworkModule.kt @@ -0,0 +1,45 @@ +package top.fatweb.oxygen.toolbox.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.serialization.json.Json +import okhttp3.Call +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import top.fatweb.oxygen.toolbox.BuildConfig +import top.fatweb.oxygen.toolbox.data.network.OxygenNetworkDataSource +import top.fatweb.oxygen.toolbox.network.retrofit.RetrofitOxygenNetwork +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object NetworkModule { + @Provides + @Singleton + fun providesNetworkJson(): Json = Json { + ignoreUnknownKeys = true + } + + @Provides + @Singleton + fun okHttpCallFactory(): Call.Factory = + OkHttpClient.Builder() + .addInterceptor( + HttpLoggingInterceptor() + .apply { + if (BuildConfig.DEBUG) { + setLevel(HttpLoggingInterceptor.Level.BODY) + } + } + ) + .build() + + @Provides + @Singleton + fun providesOxygenNetworkDataSource( + networkJson: Json, + okhttpCallFactory: dagger.Lazy + ): OxygenNetworkDataSource = RetrofitOxygenNetwork(networkJson, okhttpCallFactory) +} \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/Page.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/Page.kt new file mode 100644 index 0000000..2f1fa98 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/Page.kt @@ -0,0 +1,13 @@ +package top.fatweb.oxygen.toolbox.model + +data class Page( + val total: Long, + + val pages: Long, + + val size: Long, + + val current: Long, + + val records: List +) diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/Result.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/Result.kt new file mode 100644 index 0000000..52c2048 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/Result.kt @@ -0,0 +1,41 @@ +package top.fatweb.oxygen.toolbox.model + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import top.fatweb.oxygen.toolbox.network.model.ResponseResult + +sealed interface Result { + data class Success(val data: T) : Result + data class Fail(val message: String): Result + data class Error(val exception: Throwable) : Result + data object Loading : Result +} + +fun Flow>.asResult(): Flow> = map, Result> { + if (it.success) { + Result.Success(it.data!!) + } else { + Result.Fail(it.msg) + } +} + .onStart { emit(Result.Loading) } + .catch { emit(Result.Error(it)) } + +fun Result.asExternalModel(block: (T) -> R): Result = + when (this) { + is Result.Success -> { + Result.Success(block(data)) + } + + is Result.Fail -> { + Result.Fail(message) + } + + is Result.Error -> { + Result.Error(exception) + } + + Result.Loading -> Result.Loading + } \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/tool/Tool.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/tool/Tool.kt index d081007..86318e8 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/tool/Tool.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/tool/Tool.kt @@ -1,12 +1,53 @@ package top.fatweb.oxygen.toolbox.model.tool -import androidx.compose.ui.graphics.vector.ImageVector -import top.fatweb.oxygen.toolbox.icon.OxygenIcons +import kotlinx.datetime.LocalDateTime data class Tool( - val id: String, + val id: Long, - val icon: ImageVector = OxygenIcons.Tool, + val name: String, - val name: String -) \ No newline at end of file + val toolId: String, + + val icon: String, + + val platform: Platform, + + val description: String, + + val base: String? = null, + + val author: Author, + + val ver: String, + + val keywords: List, + + val categories: List, + + val source: String? = null, + + val dist: String? = null, + + val entryPoint: String, + + val createTime: LocalDateTime, + + val updateTime: LocalDateTime +) { + enum class Platform { + WEB, + + DESKTOP, + + ANDROID + } + + data class Author( + val username: String, + + val nickname: String, + + val avatar: String + ) +} diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/PageVo.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/PageVo.kt new file mode 100644 index 0000000..9ae9a78 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/PageVo.kt @@ -0,0 +1,25 @@ +package top.fatweb.oxygen.toolbox.network.model + +import kotlinx.serialization.Serializable +import top.fatweb.oxygen.toolbox.model.Page + +@Serializable +data class PageVo( + val total: Long, + + val pages: Long, + + val size: Long, + + val current: Long, + + val records: List +) + +fun PageVo.asExternalModel(block: (T) -> R): Page = Page( + total = total, + pages = pages, + size = size, + current = current, + records = records.map(block) +) diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ResponseResult.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ResponseResult.kt new file mode 100644 index 0000000..72c9326 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ResponseResult.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.toolbox.network.model + +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseResult( + val code: Long, + + val success: Boolean, + + val msg: String, + + val data: T? = null +) diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolBaseVo.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolBaseVo.kt new file mode 100644 index 0000000..6796cfb --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolBaseVo.kt @@ -0,0 +1,42 @@ +package top.fatweb.oxygen.toolbox.network.model + +import kotlinx.datetime.LocalDateTime +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import top.fatweb.oxygen.toolbox.model.tool.Tool +import top.fatweb.oxygen.toolbox.network.serializer.LocalDateTimeSerializer + +@Serializable +data class ToolBaseVo( + val id: Long, + + val name: String, + + val source: ToolDataVo? = null, + + val dist: ToolDataVo, + + val platform: Platform? = null, + + val compiled: Boolean? = null, + + @Serializable(LocalDateTimeSerializer::class) + val createTime: LocalDateTime? = null, + + @Serializable(LocalDateTimeSerializer::class) + val updateTime: LocalDateTime? = null +) { + @Serializable + enum class Platform { + @SerialName("WEB") + WEB, + + @SerialName("DESKTOP") + DESKTOP, + + @SerialName("ANDROID") + ANDROID + } +} + +fun ToolBaseVo.Platform.asExternalModel() = Tool.Platform.valueOf(this.name) diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolCategoryVo.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolCategoryVo.kt new file mode 100644 index 0000000..df054e8 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolCategoryVo.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.toolbox.network.model + +import kotlinx.datetime.LocalDateTime +import kotlinx.serialization.Serializable +import top.fatweb.oxygen.toolbox.network.serializer.LocalDateTimeSerializer + +@Serializable +data class ToolCategoryVo( + val id: Long, + + val name: String, + + val enable: Boolean, + + @Serializable(LocalDateTimeSerializer::class) + val createTime: LocalDateTime, + + @Serializable(LocalDateTimeSerializer::class) + val updateTime: LocalDateTime +) \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolDataVo.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolDataVo.kt new file mode 100644 index 0000000..1508780 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolDataVo.kt @@ -0,0 +1,18 @@ +package top.fatweb.oxygen.toolbox.network.model + +import kotlinx.datetime.LocalDateTime +import kotlinx.serialization.Serializable +import top.fatweb.oxygen.toolbox.network.serializer.LocalDateTimeSerializer + +@Serializable +data class ToolDataVo( + val id: Long, + + val data: String, + + @Serializable(LocalDateTimeSerializer::class) + val createTime: LocalDateTime, + + @Serializable(LocalDateTimeSerializer::class) + val updateTime: LocalDateTime +) diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolVo.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolVo.kt new file mode 100644 index 0000000..2271770 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/ToolVo.kt @@ -0,0 +1,82 @@ +package top.fatweb.oxygen.toolbox.network.model + +import kotlinx.datetime.LocalDateTime +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import top.fatweb.oxygen.toolbox.model.tool.Tool +import top.fatweb.oxygen.toolbox.network.serializer.LocalDateTimeSerializer + +@Serializable +data class ToolVo( + val id: Long, + + val name: String, + + val toolId: String, + + val icon: String, + + val platform: ToolBaseVo.Platform, + + val description: String, + + val base: ToolBaseVo? = null, + + val author: UserWithInfoVo, + + val ver: String, + + val keywords: List, + + val categories: List, + + val source: ToolDataVo? = null, + + val dist: ToolDataVo? = null, + + val entryPoint: String, + + val publish: Long, + + val review: ReviewType, + + @Serializable(LocalDateTimeSerializer::class) + val createTime: LocalDateTime, + + @Serializable(LocalDateTimeSerializer::class) + val updateTime: LocalDateTime +) { + @Serializable + enum class ReviewType { + @SerialName("NONE") + NONE, + + @SerialName("PROCESSING") + PROCESSING, + + @SerialName("PASS") + PASS, + + @SerialName("REJECT") + REJECT + } +} + +fun ToolVo.asExternalModel() = Tool( + id = id, + name = name, + toolId = toolId, + icon = icon, + platform = platform.asExternalModel(), + description = description, + base = base?.dist?.data, + author = author.asExternalModel(), + ver = ver, + keywords = keywords, + categories = categories.map { it.name }, + source = source?.data, + dist = dist?.data, + entryPoint = entryPoint, + createTime = createTime, + updateTime = updateTime +) diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/UserWithInfoVo.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/UserWithInfoVo.kt new file mode 100644 index 0000000..c2368e3 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/model/UserWithInfoVo.kt @@ -0,0 +1,28 @@ +package top.fatweb.oxygen.toolbox.network.model + +import kotlinx.serialization.Serializable +import top.fatweb.oxygen.toolbox.model.tool.Tool + +@Serializable +data class UserWithInfoVo( + val id: Long, + + val username: String, + + val userInfo: UserInfoVo +) { + @Serializable + data class UserInfoVo( + val id: Long, + + val nickname: String, + + val avatar: String + ) +} + +fun UserWithInfoVo.asExternalModel() = Tool.Author( + username = username, + nickname = userInfo.nickname, + avatar = userInfo.avatar +) diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/paging/ToolStorePagingSource.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/paging/ToolStorePagingSource.kt new file mode 100644 index 0000000..20dbec8 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/paging/ToolStorePagingSource.kt @@ -0,0 +1,37 @@ +package top.fatweb.oxygen.toolbox.network.paging + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import top.fatweb.oxygen.toolbox.data.network.OxygenNetworkDataSource +import top.fatweb.oxygen.toolbox.model.tool.Tool +import top.fatweb.oxygen.toolbox.network.model.ToolVo +import top.fatweb.oxygen.toolbox.network.model.asExternalModel + +internal class ToolStorePagingSource( + private val oxygenNetworkDataSource: OxygenNetworkDataSource, + private val searchValue: String +) : PagingSource() { + override fun getRefreshKey(state: PagingState): Int? = null + + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 1 + val (_, success, msg, data) = oxygenNetworkDataSource.getStore(searchValue, currentPage) + + if (!success) { + return LoadResult.Error(RuntimeException(msg)) + } + val (_, pages, _, _, records) = data!! + + val nextPage = if (currentPage < pages) currentPage + 1 else null + LoadResult.Page( + data = records.map(ToolVo::asExternalModel), + prevKey = null, + nextKey = nextPage + ) + } catch (e: Throwable) { + LoadResult.Error(e) + } + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/retrofit/RetrofitOxygenNetwork.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/retrofit/RetrofitOxygenNetwork.kt new file mode 100644 index 0000000..cf4a27b --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/retrofit/RetrofitOxygenNetwork.kt @@ -0,0 +1,76 @@ +package top.fatweb.oxygen.toolbox.network.retrofit + +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.serialization.json.Json +import okhttp3.Call +import okhttp3.MediaType.Companion.toMediaType +import retrofit2.Retrofit +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query +import top.fatweb.oxygen.toolbox.BuildConfig +import top.fatweb.oxygen.toolbox.data.network.OxygenNetworkDataSource +import top.fatweb.oxygen.toolbox.model.Result +import top.fatweb.oxygen.toolbox.model.asResult +import top.fatweb.oxygen.toolbox.network.model.PageVo +import top.fatweb.oxygen.toolbox.network.model.ResponseResult +import top.fatweb.oxygen.toolbox.network.model.ToolBaseVo +import top.fatweb.oxygen.toolbox.network.model.ToolVo +import javax.inject.Inject + +private interface RetrofitOxygenNetworkApi { + @GET(value = "/tool/store") + suspend fun getStore( + @Query("currentPage") currentPage: Int, + @Query("searchValue") searchValue: String, + @Query("platform") platform: ToolBaseVo.Platform? = ToolBaseVo.Platform.ANDROID + ): ResponseResult> + + @GET(value = "/tool/detail/{username}/{toolId}/{ver}") + suspend fun detail( + @Path("username") username: String, + @Path("toolId") toolId: String, + @Path("ver") ver: String, + @Query("platform") platform: String + ): ResponseResult +} + +private const val API_BASE_URL = BuildConfig.API_URL + +internal class RetrofitOxygenNetwork @Inject constructor( + networkJson: Json, + okhttpCallFactory: dagger.Lazy +) : OxygenNetworkDataSource { + private val networkApi = Retrofit.Builder() + .baseUrl(API_BASE_URL) + .callFactory { okhttpCallFactory.get().newCall(it) } + .addConverterFactory( + networkJson.asConverterFactory("application/json".toMediaType()) + ) + .build() + .create(RetrofitOxygenNetworkApi::class.java) + + override suspend fun getStore( + searchValue: String, + currentPage: Int + ): ResponseResult> = + networkApi.getStore(searchValue = searchValue, currentPage = currentPage) + + override suspend fun detail( + username: String, + toolId: String, + ver: String, + platform: ToolBaseVo.Platform + ): Flow> = flow { + emit( + networkApi.detail( + username = username, + toolId = toolId, + ver = ver, + platform = platform.name + ) + ) + }.asResult() +} diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/serializer/LocalDateTimeSerializer.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/serializer/LocalDateTimeSerializer.kt new file mode 100644 index 0000000..7f7de88 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/network/serializer/LocalDateTimeSerializer.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.toolbox.network.serializer + +import kotlinx.datetime.LocalDateTime +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +object LocalDateTimeSerializer : KSerializer { + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): LocalDateTime = + LocalDateTime.parse(decoder.decodeString().removeSuffix("Z")) + + override fun serialize(encoder: Encoder, value: LocalDateTime) { + encoder.encodeString(value.toString().padEnd(24, 'Z')) + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/lib/LocalDepRepository.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/lib/impl/LocalDepRepository.kt similarity index 89% rename from app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/lib/LocalDepRepository.kt rename to app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/lib/impl/LocalDepRepository.kt index ad8b795..a2dd943 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/lib/LocalDepRepository.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/lib/impl/LocalDepRepository.kt @@ -1,4 +1,4 @@ -package top.fatweb.oxygen.toolbox.repository.lib +package top.fatweb.oxygen.toolbox.repository.lib.impl import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import top.fatweb.oxygen.toolbox.data.lib.DepDataSource import top.fatweb.oxygen.toolbox.model.lib.Dependencies +import top.fatweb.oxygen.toolbox.repository.lib.DepRepository import javax.inject.Inject class LocalDepRepository @Inject constructor( diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/LocalToolRepository.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/LocalToolRepository.kt deleted file mode 100644 index a023841..0000000 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/LocalToolRepository.kt +++ /dev/null @@ -1,13 +0,0 @@ -package top.fatweb.oxygen.toolbox.repository.tool - -import kotlinx.coroutines.flow.Flow -import top.fatweb.oxygen.toolbox.data.tool.ToolDataSource -import top.fatweb.oxygen.toolbox.model.tool.ToolGroup -import javax.inject.Inject - -internal class LocalToolRepository @Inject constructor( - toolDataSource: ToolDataSource -) : ToolRepository { - override val toolGroups: Flow> = - toolDataSource.tool -} \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/ToolRepository.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/ToolRepository.kt index 1f95461..1d32b4a 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/ToolRepository.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/ToolRepository.kt @@ -1,8 +1,17 @@ package top.fatweb.oxygen.toolbox.repository.tool +import androidx.paging.PagingData import kotlinx.coroutines.flow.Flow -import top.fatweb.oxygen.toolbox.model.tool.ToolGroup +import top.fatweb.oxygen.toolbox.model.Result +import top.fatweb.oxygen.toolbox.model.tool.Tool interface ToolRepository { - val toolGroups: Flow> + suspend fun getStore(searchValue: String, currentPage: Int): Flow> + + suspend fun detail( + username: String, + toolId: String, + ver: String = "latest", + platform: Tool.Platform = Tool.Platform.ANDROID + ): Flow> } \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/impl/NetworkToolRepository.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/impl/NetworkToolRepository.kt new file mode 100644 index 0000000..dbfd6a1 --- /dev/null +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/tool/impl/NetworkToolRepository.kt @@ -0,0 +1,45 @@ +package top.fatweb.oxygen.toolbox.repository.tool.impl + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import top.fatweb.oxygen.toolbox.data.network.OxygenNetworkDataSource +import top.fatweb.oxygen.toolbox.model.Result +import top.fatweb.oxygen.toolbox.model.asExternalModel +import top.fatweb.oxygen.toolbox.model.tool.Tool +import top.fatweb.oxygen.toolbox.network.model.ToolBaseVo +import top.fatweb.oxygen.toolbox.network.model.ToolVo +import top.fatweb.oxygen.toolbox.network.model.asExternalModel +import top.fatweb.oxygen.toolbox.network.paging.ToolStorePagingSource +import top.fatweb.oxygen.toolbox.repository.tool.ToolRepository +import javax.inject.Inject + +private const val PAGE_SIZE = 20 + +internal class NetworkToolRepository @Inject constructor( + private val oxygenNetworkDataSource: OxygenNetworkDataSource +) : ToolRepository { + + override suspend fun getStore(searchValue: String, currentPage: Int): Flow> = + Pager( + config = PagingConfig(PAGE_SIZE), + pagingSourceFactory = { ToolStorePagingSource(oxygenNetworkDataSource, searchValue) } + ).flow + + override suspend fun detail( + username: String, + toolId: String, + ver: String, + platform: Tool.Platform + ): Flow> = + oxygenNetworkDataSource.detail( + username, + toolId, + ver, + ToolBaseVo.Platform.valueOf(platform.name) + ).map { + it.asExternalModel(ToolVo::asExternalModel) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/userdata/LocalUserDataRepository.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/userdata/impl/LocalUserDataRepository.kt similarity index 91% rename from app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/userdata/LocalUserDataRepository.kt rename to app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/userdata/impl/LocalUserDataRepository.kt index 25726c2..8de15d0 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/userdata/LocalUserDataRepository.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/repository/userdata/impl/LocalUserDataRepository.kt @@ -1,4 +1,4 @@ -package top.fatweb.oxygen.toolbox.repository.userdata +package top.fatweb.oxygen.toolbox.repository.userdata.impl import kotlinx.coroutines.flow.Flow import top.fatweb.oxygen.toolbox.data.userdata.OxygenPreferencesDataSource @@ -7,6 +7,7 @@ import top.fatweb.oxygen.toolbox.model.userdata.LanguageConfig import top.fatweb.oxygen.toolbox.model.userdata.LaunchPageConfig import top.fatweb.oxygen.toolbox.model.userdata.ThemeBrandConfig import top.fatweb.oxygen.toolbox.model.userdata.UserData +import top.fatweb.oxygen.toolbox.repository.userdata.UserDataRepository import javax.inject.Inject internal class LocalUserDataRepository @Inject constructor( diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ToolGroupCard.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ToolGroupCard.kt index b636f8d..b20e288 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ToolGroupCard.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ToolGroupCard.kt @@ -1,3 +1,4 @@ +/* package top.fatweb.oxygen.toolbox.ui.component import androidx.compose.animation.AnimatedVisibility @@ -202,4 +203,4 @@ fun ToolGroupContentPreview() { ToolDataSource().tool.first().map { it.tools }.flatten() }) } -} \ No newline at end of file +}*/ diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsPanel.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsPanel.kt index 0fe9596..6b57aa9 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsPanel.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsPanel.kt @@ -2,21 +2,17 @@ package top.fatweb.oxygen.toolbox.ui.tool import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope import androidx.compose.foundation.lazy.staggeredgrid.items -import top.fatweb.oxygen.toolbox.ui.component.ToolGroupCard +import androidx.compose.material3.Text +import androidx.paging.compose.LazyPagingItems +import top.fatweb.oxygen.toolbox.model.tool.Tool fun LazyStaggeredGridScope.toolsPanel( - toolsScreenUiState: ToolsScreenUiState + toolStorePagingItems: LazyPagingItems ) { - when (toolsScreenUiState) { - ToolsScreenUiState.Loading -> Unit - - is ToolsScreenUiState.Success -> { - items( - items = toolsScreenUiState.toolGroups, - key = { it.id }, - ) { - ToolGroupCard(toolGroup = it) - } - } + items( + items = toolStorePagingItems.itemSnapshotList, + key = { it!!.id }, + ) { + Text(text = it!!.name) } } \ No newline at end of file diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsScreen.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsScreen.kt index f86e173..86ea983 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsScreen.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsScreen.kt @@ -1,5 +1,6 @@ package top.fatweb.oxygen.toolbox.ui.tool +import android.util.Log import androidx.activity.compose.ReportDrawnWhen import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement @@ -19,48 +20,44 @@ import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import top.fatweb.oxygen.toolbox.R -import top.fatweb.oxygen.toolbox.data.tool.ToolDataSource +import androidx.paging.LoadState +import androidx.paging.compose.LazyPagingItems +import androidx.paging.compose.collectAsLazyPagingItems +import top.fatweb.oxygen.toolbox.model.tool.Tool import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar import top.fatweb.oxygen.toolbox.ui.component.scrollbar.rememberDraggableScroller import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState -import top.fatweb.oxygen.toolbox.ui.theme.OxygenPreviews -import top.fatweb.oxygen.toolbox.ui.theme.OxygenTheme @Composable internal fun ToolsRoute( modifier: Modifier = Modifier, viewModel: ToolsScreenViewModel = hiltViewModel() ) { - val toolsScreenUiState by viewModel.toolsScreenUiState.collectAsStateWithLifecycle() + val toolStorePagingItems = viewModel.getStoreData().collectAsLazyPagingItems() ToolsScreen( modifier = modifier, - toolsScreenUiState = toolsScreenUiState + toolStorePagingItems = toolStorePagingItems ) } @Composable internal fun ToolsScreen( modifier: Modifier = Modifier, - toolsScreenUiState: ToolsScreenUiState + toolStorePagingItems: LazyPagingItems ) { - val isToolLoading = toolsScreenUiState is ToolsScreenUiState.Loading + val isToolLoading = toolStorePagingItems.loadState.refresh is LoadState.Loading + Log.d("TAG", "ToolsScreen: ${toolStorePagingItems.loadState}") + ReportDrawnWhen { !isToolLoading } - val itemsAvailable = howManyItems(toolsScreenUiState) + val itemsAvailable = toolStorePagingItems.itemCount val state = rememberLazyStaggeredGridState() val scrollbarState = state.scrollbarState(itemsAvailable = itemsAvailable) @@ -68,49 +65,35 @@ internal fun ToolsScreen( Box( modifier.fillMaxSize() ) { - when (toolsScreenUiState) { - ToolsScreenUiState.Loading -> { - Text(text = stringResource(R.string.feature_settings_loading)) - } + LazyVerticalStaggeredGrid( + columns = StaggeredGridCells.Adaptive(300.dp), + contentPadding = PaddingValues(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalItemSpacing = 24.dp, + state = state + ) { - is ToolsScreenUiState.Success -> { - LazyVerticalStaggeredGrid( - columns = StaggeredGridCells.Adaptive(300.dp), - contentPadding = PaddingValues(16.dp), - horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalItemSpacing = 24.dp, - state = state - ) { + toolsPanel(toolStorePagingItems = toolStorePagingItems) - toolsPanel(toolsScreenUiState = toolsScreenUiState) - - item(span = StaggeredGridItemSpan.FullLine) { - Spacer(modifier = Modifier.height(8.dp)) - Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing)) - } - } - - state.DraggableScrollbar( - modifier = Modifier - .fillMaxHeight() - .windowInsetsPadding(WindowInsets.systemBars) - .padding(horizontal = 2.dp) - .align(Alignment.CenterEnd), - state = scrollbarState, orientation = Orientation.Vertical, - onThumbMoved = state.rememberDraggableScroller(itemsAvailable = itemsAvailable) - ) + item(span = StaggeredGridItemSpan.FullLine) { + Spacer(modifier = Modifier.height(8.dp)) + Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing)) } } + + state.DraggableScrollbar( + modifier = Modifier + .fillMaxHeight() + .windowInsetsPadding(WindowInsets.systemBars) + .padding(horizontal = 2.dp) + .align(Alignment.CenterEnd), + state = scrollbarState, orientation = Orientation.Vertical, + onThumbMoved = state.rememberDraggableScroller(itemsAvailable = itemsAvailable) + ) } } -fun howManyItems(toolScreenUiState: ToolsScreenUiState) = - when (toolScreenUiState) { - ToolsScreenUiState.Loading -> 0 - - is ToolsScreenUiState.Success -> toolScreenUiState.toolGroups.size - } - +/* @OxygenPreviews @Composable fun ToolsScreenLoadingPreview() { @@ -130,4 +113,4 @@ fun ToolsScreenPreview() { }) ) } -} \ No newline at end of file +}*/ diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsScreenViewModel.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsScreenViewModel.kt index 75a8f1a..1faf51a 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsScreenViewModel.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolsScreenViewModel.kt @@ -1,34 +1,44 @@ package top.fatweb.oxygen.toolbox.ui.tool +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import top.fatweb.oxygen.toolbox.model.tool.ToolGroup +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import top.fatweb.oxygen.toolbox.model.Page +import top.fatweb.oxygen.toolbox.model.tool.Tool import top.fatweb.oxygen.toolbox.repository.tool.ToolRepository import javax.inject.Inject -import kotlin.time.Duration.Companion.seconds @HiltViewModel class ToolsScreenViewModel @Inject constructor( - toolRepository: ToolRepository + private val toolRepository: ToolRepository, + savedStateHandle: SavedStateHandle ) : ViewModel() { - val toolsScreenUiState: StateFlow = - toolRepository.toolGroups - .map { - ToolsScreenUiState.Success(it) - } - .stateIn( - scope = viewModelScope, - initialValue = ToolsScreenUiState.Loading, - started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds) - ) + private val searchValue = savedStateHandle.getStateFlow(SEARCH_VALUE, "") + private val currentPage = savedStateHandle.getStateFlow(CURRENT_PAGE, 1) + + @OptIn(ExperimentalCoroutinesApi::class) + fun getStoreData(): Flow> { + return combine( + searchValue, + currentPage, + ::Pair + ).flatMapLatest { (searchValue, currentPage) -> + toolRepository.getStore(searchValue, currentPage).cachedIn(viewModelScope) + } + } } sealed interface ToolsScreenUiState { data object Loading : ToolsScreenUiState - data class Success(val toolGroups: List) : ToolsScreenUiState -} \ No newline at end of file + data class Success(val tools: Page) : ToolsScreenUiState +} + +private const val SEARCH_VALUE = "searchValue" +private const val CURRENT_PAGE = "currentPage" \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 0ad3fa5..eec1b59 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,4 +7,5 @@ plugins { alias(libs.plugins.hilt) apply false alias(libs.plugins.protobuf) apply false alias(libs.plugins.kotlinxSerialization) apply false + alias(libs.plugins.secrets) apply false } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6947c74..00d6322 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,8 @@ ksp = "1.9.23-1.0.20" aboutlibraries = "11.1.0" protobufPlugin = "0.9.4" kotlinxSerialization = "1.9.23" +secrets = "2.0.1" +paging = "3.2.1" desugarJdkLibs = "2.0.4" composeBom = "2024.05.00" @@ -24,20 +26,25 @@ protobuf = "3.25.2" androidxNavigation = "2.7.7" androidxHiltNavigationCompose = "1.2.0" kotlinxSerializationJson = "1.6.3" +retrofit = "2.9.0" +retrofitKotlinxSerializationJson = "1.0.0" +okhttp = "4.12.0" [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -aboutlibraries = {id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutlibraries"} +aboutlibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutlibraries" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } kotlinxSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinxSerialization" } +secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } [libraries] -desugar-jdk-libs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugarJdkLibs"} +desugar-jdk-libs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugarJdkLibs" } junit = { group = "junit", name = "junit", version.ref = "junit" } +paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "paging" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } @@ -51,19 +58,19 @@ androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } -material-icons-core = { group = "androidx.compose.material", name = "material-icons-core"} -material-icons-extended = {group = "androidx.compose.material", name = "material-icons-extended"} -material3-window-size = {group = "androidx.compose.material3", name = "material3-window-size-class"} +material-icons-core = { group = "androidx.compose.material", name = "material-icons-core" } +material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" } +material3-window-size = { group = "androidx.compose.material3", name = "material3-window-size-class" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } lifecycle-runtime-testing = { group = "androidx.lifecycle", name = "lifecycle-runtime-testing", version.ref = "androidxLifecycle" } -lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle"} +lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } dagger-compiler = { group = "com.google.dagger", name = "dagger-compiler", version.ref = "hilt" } -hilt-android = {group = "com.google.dagger", name = "hilt-android", version.ref = "hilt"} +hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" } hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil" } @@ -76,4 +83,9 @@ protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" } androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } -kotlinx-serialization-json = {group="org.jetbrains.kotlinx", name="kotlinx-serialization-json", version.ref = "kotlinxSerializationJson"} +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } +retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } +retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofitKotlinxSerializationJson" } +okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } +paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "paging" } +paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "paging" } diff --git a/secrets.defaults.properties b/secrets.defaults.properties new file mode 100644 index 0000000..86aa308 --- /dev/null +++ b/secrets.defaults.properties @@ -0,0 +1,4 @@ +## This file provides default values to modules using the secrets-gradle-plugin. It is necessary +# because the secrets properties file is not under source control so CI builds will fail without +# default values. +API_URL="https://example.com" \ No newline at end of file