fix schedule request. Add Moshi and network logger

This commit is contained in:
Shcherbatykh Oleg
2026-02-12 14:55:50 +03:00
parent 04b8164eba
commit 5dac9438fd
27 changed files with 303 additions and 156 deletions

View File

@@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application <application
android:name=".App"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"

View File

@@ -0,0 +1,7 @@
package ru.fincode.tsudesk
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class App : Application()

View File

@@ -1,21 +1,33 @@
package ru.fincode.tsudesk package ru.fincode.tsudesk
import android.os.Bundle import android.os.Bundle
import androidx.activity.enableEdgeToEdge import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.activity.ComponentActivity
import androidx.core.view.ViewCompat import androidx.lifecycle.lifecycleScope
import androidx.core.view.WindowInsetsCompat import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import ru.fincode.tsudesk.feature.schedule.domain.usecase.GetScheduleUseCase
import ru.fincode.tsudesk.feature.schedule.domain.usecase.GetScheduleUseCase.ScheduleType
import javax.inject.Inject
import ru.fincode.tsudesk.core.network.NetworkConstants @AndroidEntryPoint
import ru.fincode.tsudesk.core.network.RetrofitProvider class MainActivity : ComponentActivity() {
import ru.fincode.tsudesk.core.network.NetworkModule @Inject
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleApi lateinit var getScheduleUseCase: GetScheduleUseCase
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main) lifecycleScope.launch {
try {
val result = getScheduleUseCase(
ScheduleType.Group("220631")
)
Log.d("TSUDesk", result.isSuccess.toString())
} catch (e: Exception) {
Log.e("TSUDesk", "Error loading schedule", e)
}
}
} }
} }

View File

@@ -42,5 +42,12 @@ dependencies {
api(libs.retrofit) api(libs.retrofit)
api(libs.okhttp) api(libs.okhttp)
implementation(libs.retrofit.simplexml) implementation(libs.okhttp.logging)
implementation(libs.converter.gson)
api(libs.moshi)
api(libs.moshiKotlin)
api(libs.retrofitMoshi)
api(libs.retrofit.simplexml)
} }

View File

@@ -0,0 +1,18 @@
package ru.fincode.tsudesk.core.network
import okhttp3.Interceptor
import okhttp3.Response
import android.util.Log
class DebugInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
Log.d("NETWORK_DEBUG", "URL: ${request.url}")
Log.d("NETWORK_DEBUG", "Method: ${request.method}")
Log.d("NETWORK_DEBUG", "Headers: ${request.headers}")
return chain.proceed(request)
}
}

View File

@@ -1,5 +1,5 @@
package ru.fincode.tsudesk.core.network package ru.fincode.tsudesk.core.network
object NetworkConstants { object NetworkConstants {
const val BASE_URL = "https://api.tsu.tula.ru/" const val BASE_URL = "https://tulsu.ru/schedule/queries/"
} }

View File

@@ -8,12 +8,16 @@ import okhttp3.OkHttpClient
import retrofit2.Retrofit import retrofit2.Retrofit
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Singleton import javax.inject.Singleton
import okhttp3.logging.HttpLoggingInterceptor
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
object NetworkModule { object NetworkModule {
private const val TIMEOUT_SEC = 30L private const val TIMEOUT_SEC = 30L
val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
@Provides @Provides
@Singleton @Singleton
@@ -23,6 +27,7 @@ object NetworkModule {
.readTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) .readTimeout(TIMEOUT_SEC, TimeUnit.SECONDS)
.writeTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) .writeTimeout(TIMEOUT_SEC, TimeUnit.SECONDS)
.retryOnConnectionFailure(true) .retryOnConnectionFailure(true)
.addInterceptor(DebugInterceptor())
.build() .build()
@Provides @Provides

View File

@@ -1,14 +1,24 @@
package ru.fincode.tsudesk.core.network package ru.fincode.tsudesk.core.network
import com.squareup.moshi.Moshi
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.simplexml.SimpleXmlConverterFactory import retrofit2.converter.moshi.MoshiConverterFactory
import javax.inject.Inject
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import javax.inject.Singleton
@Singleton
class RetrofitProvider @Inject constructor() {
private val moshi: Moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
class RetrofitProvider {
fun process(baseUrl: String, client: OkHttpClient): Retrofit = fun process(baseUrl: String, client: OkHttpClient): Retrofit =
Retrofit.Builder() Retrofit.Builder()
.baseUrl(baseUrl) .baseUrl(baseUrl)
.client(client) .client(client)
.addConverterFactory(SimpleXmlConverterFactory.create()) .addConverterFactory(MoshiConverterFactory.create(moshi))
.build() .build()
} }

View File

@@ -2,7 +2,7 @@ package ru.fincode.tsudesk.feature.schedule.data
import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleRemoteDataSource import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleRemoteDataSource
import ru.fincode.tsudesk.feature.schedule.data.mapper.ScheduleDtoToDomainMapper import ru.fincode.tsudesk.feature.schedule.data.mapper.ScheduleDtoToDomainMapper
import ru.fincode.tsudesk.feature.schedule.domain.model.Schedule import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository
import javax.inject.Inject import javax.inject.Inject
@@ -10,12 +10,11 @@ class ScheduleRepositoryImpl @Inject constructor(
private val remote: ScheduleRemoteDataSource, private val mapper: ScheduleDtoToDomainMapper private val remote: ScheduleRemoteDataSource, private val mapper: ScheduleDtoToDomainMapper
) : ScheduleRepository { ) : ScheduleRepository {
override suspend fun loadScheduleByGroup(groupNumber: String): Schedule { override suspend fun loadScheduleByGroup(
return mapper.map(remote.loadScheduleByGroup(groupNumber)) groupNumber: String
} ): Result<ScheduleEntity> = remote.loadScheduleByGroup(groupNumber).map(mapper::invoke)
override suspend fun loadScheduleByTeacher(teacherName: String): Schedule {
return mapper.map(remote.loadScheduleByTeacher(teacherName))
}
override suspend fun loadScheduleByTeacher(
name: String
): Result<ScheduleEntity> = remote.loadScheduleByTeacher(name).map(mapper::invoke)
} }

View File

@@ -1,10 +1,10 @@
package ru.fincode.tsudesk.feature.schedule.data.datasource package ru.fincode.tsudesk.feature.schedule.data.datasource
import ru.fincode.tsudesk.feature.schedule.data.local.ScheduleEntity import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleDto
interface ScheduleLocalDataSource { interface ScheduleLocalDataSource {
suspend fun getScheduleByGroup(groupNumber: String): ScheduleEntity? suspend fun getScheduleByGroup(groupNumber: String): ScheduleEntity?
suspend fun getScheduleByTeacherName(teacherName: String): ScheduleEntity? suspend fun getScheduleByTeacher(teacherName: String): ScheduleEntity?
suspend fun saveSchedule(entity: ScheduleEntity) suspend fun saveSchedule(entity: ScheduleEntity)
} }

View File

@@ -1,20 +1,17 @@
package ru.fincode.tsudesk.feature.schedule.data.datasource package ru.fincode.tsudesk.feature.schedule.data.datasource
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleApi import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleApi
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleDto import ru.fincode.tsudesk.feature.schedule.data.remote.model.LessonDto.ScheduleSearchField.GROUP_P
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleXmlParser import ru.fincode.tsudesk.feature.schedule.data.remote.model.LessonDto.ScheduleSearchField.PREP
import java.io.IOException import ru.fincode.tsudesk.feature.schedule.data.remote.model.ScheduleDto
import javax.inject.Inject
class ScheduleRemoteDataSource( class ScheduleRemoteDataSource @Inject constructor(
private val api: ScheduleApi, private val xmlParser: ScheduleXmlParser private val api: ScheduleApi
) { ) {
suspend fun loadScheduleByGroup(groupNumber: String): ScheduleDto { suspend fun loadScheduleByGroup(groupNumber: String): Result<ScheduleDto> =
val response = api.getScheduleByGroup(groupNumber) runCatching { api.getSchedule(GROUP_P, groupNumber) }
return xmlParser.parse(response.body() ?: throw IOException("Response body is null"))
}
suspend fun loadScheduleByTeacher(name: String): ScheduleDto { suspend fun loadScheduleByTeacher(name: String): Result<ScheduleDto> =
val response = api.getScheduleByTeacherName(name) runCatching { api.getSchedule(PREP, name) }
return xmlParser.parse(response.body() ?: throw IOException("Response body is null"))
}
} }

View File

@@ -1,20 +1,22 @@
package ru.fincode.tsudesk.feature.schedule.data.datasource package ru.fincode.tsudesk.feature.schedule.data.datasource
import ru.fincode.tsudesk.feature.schedule.data.mapper.ScheduleDtoToDomainMapper import ru.fincode.tsudesk.feature.schedule.data.mapper.ScheduleDtoToDomainMapper
import ru.fincode.tsudesk.feature.schedule.domain.model.Schedule import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository
import javax.inject.Inject import javax.inject.Inject
class ScheduleRepositoryImpl @Inject constructor( class ScheduleRemoteDataSourceImpl @Inject constructor(
private val remote: ScheduleRemoteDataSource, private val remote: ScheduleRemoteDataSource,
private val mapper: ScheduleDtoToDomainMapper private val mapper: ScheduleDtoToDomainMapper
) : ScheduleRepository { ) : ScheduleRepository {
override suspend fun loadScheduleByGroup(groupNumber: String): Schedule { override suspend fun loadScheduleByGroup(groupNumber: String): Result<ScheduleEntity> =
return mapper.map(remote.loadScheduleByGroup(groupNumber)); remote
} .loadScheduleByGroup(groupNumber) // Result<ScheduleDto>
.map(mapper::invoke) // Result<ScheduleEntity>
override suspend fun loadScheduleByTeacher(teacherName: String): Schedule { override suspend fun loadScheduleByTeacher(name: String): Result<ScheduleEntity> =
return mapper.map(remote.loadScheduleByTeacher(teacherName)); remote
} .loadScheduleByTeacher(name) // Result<ScheduleDto>
.map(mapper::invoke) // Result<ScheduleEntity>
} }

View File

@@ -1,4 +0,0 @@
package ru.fincode.tsudesk.feature.schedule.data.local;
public class ScheduleEntity {
}

View File

@@ -1,23 +1,25 @@
package ru.fincode.tsudesk.feature.schedule.data.mapper package ru.fincode.tsudesk.feature.schedule.data.mapper
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleDto import ru.fincode.tsudesk.feature.schedule.data.remote.model.LessonDto
import ru.fincode.tsudesk.feature.schedule.data.remote.model.ScheduleDto
import ru.fincode.tsudesk.feature.schedule.domain.model.Lesson import ru.fincode.tsudesk.feature.schedule.domain.model.Lesson
import ru.fincode.tsudesk.feature.schedule.domain.model.Schedule import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
import javax.inject.Inject
class ScheduleDtoToDomainMapper { class ScheduleDtoToDomainMapper @Inject constructor() {
fun map(dto: ScheduleDto): Schedule = operator fun invoke(dto: ScheduleDto): ScheduleEntity =
Schedule( ScheduleEntity(lessons = dto.map(::mapLesson))
lessons = dto.lessons.map { l ->
Lesson( private fun mapLesson(item: LessonDto): Lesson =
dayOfWeek = l.dayOfWeek, Lesson(
dayName = l.dayName, date = item.date.trim(),
weekType = l.weekType, time = item.time.trim(),
time = l.time, subject = item.discipline.trim(),
room = l.room, typeName = item.typeName.trim(),
subjectName = l.subjectName, room = item.room.trim(),
teacherName = l.teacherName teacher = item.teacher.trim(),
) groupId = item.groups.firstOrNull()?.groupCode.orEmpty(),
} type = item.type.trim()
) )
} }

View File

@@ -1,18 +1,28 @@
package ru.fincode.tsudesk.feature.schedule.data.remote package ru.fincode.tsudesk.feature.schedule.data.remote
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Query import retrofit2.http.Query
import ru.fincode.tsudesk.feature.schedule.data.remote.model.LessonDto
import ru.fincode.tsudesk.feature.schedule.data.remote.model.LessonDto.*
import ru.fincode.tsudesk.feature.schedule.data.remote.model.ScheduleDto
interface ScheduleApi { interface ScheduleApi {
@GET("schedule")
suspend fun getScheduleByGroup(
@Query("group") groupNumber: String
): Response<ResponseBody>
@GET("schedule") /**
suspend fun getScheduleByTeacherName( * Расписание по номеру группы.
@Query("fio") teacherName: String * Пример: search_field=GROUP_P&search_value=220631
): Response<ResponseBody> * https://tulsu.ru/schedule/queries/GetSchedule.php?search_field=GROUP_P&search_value=220631
*
*
* Расписание по ФИО преподавателя (строкой).
* Пример: search_field=PREP&search_value=Набродова Ирина Николаевна
*
* Важно: Retrofit сам URL-энкодит параметр search_value
* https://tulsu.ru/schedule/queries/GetDates.php?search_value=%D0%9D%D0%B0%D0%B1%D1%80%D0%BE%D0%B4%D0%BE%D0%B2%D0%B0%20%D0%98%D1%80%D0%B8%D0%BD%D0%B0%20%D0%9D%D0%B8%D0%BA%D0%BE%D0%BB%D0%B0%D0%B5%D0%B2%D0%BD%D0%B0
*/
@GET("GetSchedule.php")
suspend fun getSchedule(
@Query("search_field") searchField: String,
@Query("search_value") searchValue: String
): ScheduleDto
} }

View File

@@ -1,15 +1,38 @@
package ru.fincode.tsudesk.feature.schedule.data.remote package ru.fincode.tsudesk.feature.schedule.data.remote.model
data class ScheduleDto( import com.squareup.moshi.Json
val lessons: List<LessonDto> import com.squareup.moshi.JsonClass
)
typealias ScheduleDto = List<LessonDto>
@JsonClass(generateAdapter = false) // reflection-адаптер через moshi-kotlin
data class LessonDto( data class LessonDto(
val dayOfWeek: String, @Json(name = "DATE_Z")
val dayName: String, val date: String, // "12.01.2026"
val time: String, @Json(name = "TIME_Z")
val room: String, val time: String, // "11:00 - 13:55"
val subjectName: String, @Json(name = "DISCIP")
val teacherName: String, val discipline: String, // предмет
val weekType: Int @Json(name = "KOW")
) val typeName: String, // "Лекции", "Лабораторные занятия", "Э", "зч", "КР", "ДЗ" и т.п.
@Json(name = "AUD")
val room: String, // можно назвать audience, но ключ в JSON "AUD"
@Json(name = "PREP")
val teacher: String,
@Json(name = "GROUPS")
val groups: List<GroupDto>,
@Json(name = "CLASS")
val type: String // "lecture" / "lab" / "practice" / "default"
) {
data class GroupDto(
@Json(name = "GROUP_P")
val groupCode: String
)
object ScheduleSearchField {
const val GROUP_P = "GROUP_P"
const val PREP = "PREP"
}
}

View File

@@ -0,0 +1,23 @@
package ru.fincode.tsudesk.feature.schedule.data.remote
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import ru.fincode.tsudesk.feature.schedule.data.remote.model.LessonDto
class ScheduleJsonParser {
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val type = Types.newParameterizedType(
List::class.java,
LessonDto::class.java
)
private val adapter = moshi.adapter<List<LessonDto>>(type)
fun parse(json: String): List<LessonDto> =
adapter.fromJson(json)
?: throw IllegalStateException("Schedule JSON is null/invalid")
}

View File

@@ -1,15 +0,0 @@
package ru.fincode.tsudesk.feature.schedule.data.remote
import okhttp3.ResponseBody
interface ScheduleXmlParser {
fun parse(body: ResponseBody): ScheduleDto
}
class ScheduleXmlParserImpl : ScheduleXmlParser {
override fun parse(body: ResponseBody): ScheduleDto {
val xml = body.string()
return ScheduleDto(lessons = emptyList())
}
}

View File

@@ -0,0 +1,19 @@
package ru.fincode.tsudesk.feature.schedule.di
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleApi
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object ScheduleNetworkModule {
@Provides
@Singleton
fun provideScheduleApi(retrofit: Retrofit): ScheduleApi =
retrofit.create(ScheduleApi::class.java)
}

View File

@@ -0,0 +1,20 @@
package ru.fincode.tsudesk.feature.schedule.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import ru.fincode.tsudesk.feature.schedule.data.ScheduleRepositoryImpl
import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
abstract class ScheduleRepositoryModule {
@Binds
@Singleton
abstract fun bindScheduleRepository(
impl: ScheduleRepositoryImpl
): ScheduleRepository
}

View File

@@ -1,17 +0,0 @@
package ru.fincode.tsudesk.feature.schedule.domain.model
data class Schedule(
val lessons: List<Lesson>
)
data class Lesson(
val dayOfWeek: String,
val dayName: String,
val time: String,
val room: String,
val subjectName: String,
val teacherName: String,
val weekType: Int
)

View File

@@ -0,0 +1,19 @@
package ru.fincode.tsudesk.feature.schedule.domain.model
data class ScheduleEntity(
val lessons: List<Lesson>
)
data class Lesson(
val date: String, // "12.01.2026"
val time: String, // "11:00 - 13:55"
val subject: String, // discipline
val typeName: String, // "Лекции", "Лабораторные занятия" и т.д.
val room: String,
val teacher: String,
val groupId: String,
val type: String // "lecture" / "lab" / "practice" / "default"
)

View File

@@ -1,8 +1,8 @@
package ru.fincode.tsudesk.feature.schedule.domain.repository package ru.fincode.tsudesk.feature.schedule.domain.repository
import ru.fincode.tsudesk.feature.schedule.domain.model.Schedule import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
interface ScheduleRepository { interface ScheduleRepository {
suspend fun loadScheduleByGroup(groupNumber: String): Schedule suspend fun loadScheduleByGroup(groupNumber: String): Result<ScheduleEntity>
suspend fun loadScheduleByTeacher(teacherName: String): Schedule suspend fun loadScheduleByTeacher(name: String): Result<ScheduleEntity>
} }

View File

@@ -1,24 +0,0 @@
package ru.fincode.tsudesk.feature.schedule.domain.repository
import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleLocalDataSource
import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleRemoteDataSource
import ru.fincode.tsudesk.feature.schedule.data.mapper.ScheduleDtoToDomainMapper
import ru.fincode.tsudesk.feature.schedule.domain.model.Schedule
import javax.inject.Inject
class ScheduleRepositoryImpl @Inject constructor(
private val remoteDataSource: ScheduleRemoteDataSource,
private val localDataSource: ScheduleLocalDataSource,
private val mapper: ScheduleDtoToDomainMapper
) : ScheduleRepository {
override suspend fun loadScheduleByGroup(groupNumber: String): Schedule {
val dto = remoteDataSource.loadScheduleByGroup(groupNumber)
return mapper.map(dto)
}
override suspend fun loadScheduleByTeacher(teacherName: String): Schedule {
val dto = remoteDataSource.loadScheduleByTeacher(teacherName)
return mapper.map(dto)
}
}

View File

@@ -1,4 +1,20 @@
package ru.fincode.tsudesk.feature.schedule.domain.usecase package ru.fincode.tsudesk.feature.schedule.domain.usecase
class GetScheduleUseCase { import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository
import javax.inject.Inject
class GetScheduleUseCase @Inject constructor(
private val repository: ScheduleRepository
) {
sealed interface ScheduleType {
data class Group(val groupNumber: String) : ScheduleType
data class Teacher(val teacherName: String) : ScheduleType
}
suspend operator fun invoke(type: ScheduleType): Result<ScheduleEntity> =
when (type) {
is ScheduleType.Group -> repository.loadScheduleByGroup(type.groupNumber)
is ScheduleType.Teacher -> repository.loadScheduleByTeacher(type.teacherName)
}
} }

View File

@@ -6,7 +6,7 @@ versionName = "1.0"
versionCode = "1" versionCode = "1"
agp = "8.12.0" agp = "8.12.0"
kotlin = "2.0.21" kotlin = "1.9.24"
jvmTarget = "17" jvmTarget = "17"
coreKtx = "1.10.1" coreKtx = "1.10.1"
@@ -19,6 +19,10 @@ hilt = "2.50"
retrofit = "2.11.0" retrofit = "2.11.0"
okhttp = "4.12.0" okhttp = "4.12.0"
moshi = "1.15.1"
lifecycle = "2.7.0"
coroutines = "1.8.1"
[libraries] [libraries]
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }
@@ -34,6 +38,18 @@ hiltandroid = { module = "com.google.dagger:hilt-android", version.ref = "hilt"
hiltcompiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } hiltcompiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
moshi = { group="com.squareup.moshi", name="moshi", version.ref="moshi" }
moshiKotlin = { group="com.squareup.moshi", name="moshi-kotlin", version.ref="moshi" }
retrofitMoshi = { group="com.squareup.retrofit2", name="converter-moshi", version.ref="retrofit" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
[plugins] [plugins]
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }

View File

@@ -13,12 +13,13 @@ pluginManagement {
} }
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories(fun RepositoryHandler.() { repositories {
google() google()
mavenCentral() mavenCentral()
}) }
} }
rootProject.name = "TSUDesk" rootProject.name = "TSUDesk"
include(":app") include(":app")