Практическое задание
Нужно разработать android приложение для просмотра GitHub репозиториев.
Во время работы над практическим заданием настоятельно рекомендуем обращаться к разделу Памятки для разработчика
Кликабельный прототип
Функциональные требования
- Авторизация пользователя (personal access token)
- Просмотр списка репозиториев пользователя (первые 10)
- Просмотр детальной информации выбранного репозитория
- статистика (forks, stars, watchers)
- ссылка на web страницу репозитория
- лицензия
- readme
Технические требования
- Реализация на Kotlin
- Использовать XML Layouts для UI
- Использовать Kotlin Gradle DSL
- Использовать Retrofit для работы с REST API
- Использовать RecyclerView для отображения списка
- Использовать ConstraintLayout для экрана детальной информации
- Использовать Android Navigation Component для переходов между экранами
- Использовать View Binding для связывания верстки с кодом
- Экраны делать с помощью Fragment (подход Single Activity)
- Использовать Coroutines для асинхронности и многопоточности
- Использовать Kotlinx.Serialization для парсинга json
- Использовать ViewModel для реализации логики экранов
- Использовать LiveData / StateFlow для обновления данных на UI
- Использовать Dagger Hilt для внедрения зависимостей
- Сохранять токен авторизации в хранилище устройства - SharedPreferences
- Корректно обрабатывать ситуации "загрузка данных", "ошибка загрузки", "пустой список"
- Корректно обрабатывать смену конфигурации
- При перезапуске приложения авторизация должна сохраняться
- Использовать локализацию для всех строк, показываемых пользователю
- Использовать векторную графику везде, где это возможно
- Обеспечить поддержку Android API 21
Классы Android-приложения
class MainActivity: AppCompatActivity {
// TODO:
}
class AuthFragment: Fragment {
// TODO:
}
class RepositoriesListFragment: Fragment {
// TODO:
}
class DetailInfoFragment: Fragment {
// TODO:
}
class AuthViewModel {
val token: MutableLiveData<String>
val state: LiveData<State>
val actions: Flow<Action>
fun onSignButtonPressed() {
// TODO:
}
sealed interface State {
object Idle : State
object Loading : State
data class InvalidInput(val reason: String) : State
}
sealed interface Action {
data class ShowError(val message: String) : Action
object RouteToMain : Action
}
// TODO:
}
class RepositoryInfoViewModel {
val state: LiveData<State>
sealed interface State {
object Loading : State
data class Error(val error: String) : State
data class Loaded(
val githubRepo: Repo,
val readmeState: ReadmeState
) : State
}
sealed interface ReadmeState {
object Loading : ReadmeState
object Empty : ReadmeState
data class Error(val error: String) : ReadmeState
data class Loaded(val markdown: String) : ReadmeState
}
// TODO:
}
class RepositoriesListViewModel {
val state: LiveData<State>
sealed interface State {
object Loading : State
data class Loaded(val repos: List<Repo>) : State
data class Error(val error: String) : State
object Empty : State
}
// TODO:
}
class AppRepository {
suspend fun getRepositories(): List<Repo> {
// TODO:
}
suspend fun getRepository(repoId: String): RepoDetails {
// TODO:
}
suspend fun getRepositoryReadme(ownerName: String, repositoryName: String, branchName: String): String {
// TODO:
}
suspend fun signIn(token: String): UserInfo {
// TODO:
}
// TODO:
}
class KeyValueStorage {
var authToken: String?
}
Диаграмма классов
При реализации нужно придерживаться следующей диаграммы:
classDiagram
class MainActivity:::android
class AuthFragment:::android
class RepositoriesListFragment:::android
class DetailInfoFragment:::android
class AuthViewModel:::android
class RepositoryInfoViewModel:::android
class RepositoriesListViewModel:::android
class AppRepository:::android
class KeyValueStorage:::android
MainActivity --> AuthFragment
MainActivity --> RepositoriesListFragment
MainActivity --> DetailInfoFragment
AuthFragment --> AuthViewModel
RepositoriesListFragment --> RepositoriesListViewModel
DetailInfoFragment --> RepositoryInfoViewModel
RepositoryInfoViewModel --> AppRepository
AuthViewModel --> AppRepository
RepositoriesListViewModel --> AppRepository
AppRepository --> KeyValueStorage