Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f2ec1fefd | ||
|
|
c6418793cb | ||
|
|
2da3b58773 | ||
|
|
a04fb915ae | ||
|
|
fa68925205 | ||
|
|
011e169f96 | ||
|
|
cd70c1cd20 | ||
|
|
79650a94e8 | ||
|
|
2af2c36ab2 | ||
|
|
87bbfcc96c | ||
|
|
b74a74f3bd | ||
|
|
d8416283ae |
@@ -2,11 +2,21 @@ package ru.myitschool.work
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import ru.myitschool.work.data.datastore.DataStoreManager
|
||||||
|
|
||||||
|
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "datastore")
|
||||||
|
|
||||||
class App: Application() {
|
class App: Application() {
|
||||||
|
|
||||||
|
lateinit var dataStoreManager: DataStoreManager
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
context = this
|
context = this
|
||||||
|
dataStoreManager = DataStoreManager(dataStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package ru.myitschool.work.data.datastore
|
||||||
|
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
class DataStoreManager(
|
||||||
|
private val dataStore: DataStore<Preferences>
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val USER_CODE_KEY = stringPreferencesKey("user_code")
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun clearUserCode() {
|
||||||
|
dataStore.edit { preferences ->
|
||||||
|
preferences.remove(USER_CODE_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveUserCode(userCode: UserCode) {
|
||||||
|
dataStore.edit { preferences ->
|
||||||
|
preferences[USER_CODE_KEY] = userCode.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUserCode(): Flow<UserCode> = dataStore.data.map { preferences ->
|
||||||
|
UserCode(
|
||||||
|
code = preferences[USER_CODE_KEY] ?: ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package ru.myitschool.work.data.datastore
|
||||||
|
|
||||||
|
data class UserCode(
|
||||||
|
val code: String
|
||||||
|
)
|
||||||
@@ -4,13 +4,7 @@ import ru.myitschool.work.data.source.NetworkDataSource
|
|||||||
|
|
||||||
object AuthRepository {
|
object AuthRepository {
|
||||||
|
|
||||||
private var codeCache: String? = null
|
suspend fun checkAndSave(code: String): Result<Boolean> {
|
||||||
|
return NetworkDataSource.checkAuth(code)
|
||||||
suspend fun checkAndSave(text: String): Result<Boolean> {
|
|
||||||
return NetworkDataSource.checkAuth(text).onSuccess { success ->
|
|
||||||
if (success) {
|
|
||||||
codeCache = text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package ru.myitschool.work.data.repo
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.source.NetworkDataSource
|
||||||
|
import ru.myitschool.work.domain.book.entities.BookingEntity
|
||||||
|
|
||||||
|
object ReserveRepo {
|
||||||
|
|
||||||
|
suspend fun loadAvailable(code: String): Result<BookingEntity> {
|
||||||
|
return NetworkDataSource.loadBooking(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun reserve(
|
||||||
|
userCode: String,
|
||||||
|
day: String,
|
||||||
|
placeId: Int,
|
||||||
|
placeLabel: String
|
||||||
|
): Result<Unit> {
|
||||||
|
return NetworkDataSource.bookPlace(
|
||||||
|
userCode = userCode,
|
||||||
|
date = day,
|
||||||
|
placeId = placeId,
|
||||||
|
placeName = placeLabel
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package ru.myitschool.work.data.repo
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.source.NetworkDataSource
|
||||||
|
import ru.myitschool.work.domain.main.entities.UserEntity
|
||||||
|
|
||||||
|
object ProfileRepo {
|
||||||
|
|
||||||
|
suspend fun loadProfile(code: String): Result<UserEntity> {
|
||||||
|
return NetworkDataSource.loadData(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
package ru.myitschool.work.data.source
|
package ru.myitschool.work.data.source
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.body
|
||||||
import io.ktor.client.engine.cio.CIO
|
import io.ktor.client.engine.cio.CIO
|
||||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.post
|
||||||
|
import io.ktor.client.request.setBody
|
||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.serialization.kotlinx.json.json
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
@@ -11,6 +15,30 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import ru.myitschool.work.core.Constants
|
import ru.myitschool.work.core.Constants
|
||||||
|
import ru.myitschool.work.domain.book.entities.BookingEntity
|
||||||
|
import ru.myitschool.work.domain.book.entities.PlaceInfo
|
||||||
|
import ru.myitschool.work.domain.main.entities.UserEntity
|
||||||
|
|
||||||
|
private const val testJson = """
|
||||||
|
{
|
||||||
|
"name": "Иванов Петр Федорович",
|
||||||
|
"photoUrl": "https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg",
|
||||||
|
"booking": {
|
||||||
|
"2025-01-05": {"id":1,"place":"102"},
|
||||||
|
"2025-01-06": {"id":2,"place":"209.13"},
|
||||||
|
"2025-01-09": {"id":3,"place":"Зона 51. 50"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
private const val testBookingJson = """
|
||||||
|
{
|
||||||
|
"2025-01-05": [{"id": 1, "place": "102"},{"id": 2, "place": "209.13"}],
|
||||||
|
"2025-01-06": [{"id": 3, "place": "Зона 51. 50"}],
|
||||||
|
"2025-01-07": [{"id": 1, "place": "102"},{"id": 2, "place": "209.13"}],
|
||||||
|
"2025-01-08": [{"id": 2, "place": "209.13"}]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
object NetworkDataSource {
|
object NetworkDataSource {
|
||||||
private val client by lazy {
|
private val client by lazy {
|
||||||
@@ -28,9 +56,38 @@ object NetworkDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun bookPlace(
|
||||||
|
userCode: String,
|
||||||
|
date: String,
|
||||||
|
placeId: Int,
|
||||||
|
placeName: String
|
||||||
|
): Result<Unit> = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext runCatching {
|
||||||
|
// Log.i("aaa", "Booking: userCode=$userCode, date=$date, placeId=$placeId, placeName=$placeName")
|
||||||
|
// println("Booking: userCode=$userCode, date=$date, placeId=$placeId, placeName=$placeName")
|
||||||
|
|
||||||
|
val response = client.post(getUrl(userCode, Constants.BOOK_URL)) {
|
||||||
|
setBody(mapOf(
|
||||||
|
"date" to date,
|
||||||
|
"placeId" to placeId,
|
||||||
|
"placeName" to placeName
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
when (response.status) {
|
||||||
|
HttpStatusCode.OK -> Unit
|
||||||
|
else -> error(response.bodyAsText())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun checkAuth(code: String): Result<Boolean> = withContext(Dispatchers.IO) {
|
suspend fun checkAuth(code: String): Result<Boolean> = withContext(Dispatchers.IO) {
|
||||||
return@withContext runCatching {
|
return@withContext runCatching {
|
||||||
|
|
||||||
|
// true // удалить при проверке
|
||||||
|
|
||||||
val response = client.get(getUrl(code, Constants.AUTH_URL))
|
val response = client.get(getUrl(code, Constants.AUTH_URL))
|
||||||
|
response.status
|
||||||
when (response.status) {
|
when (response.status) {
|
||||||
HttpStatusCode.OK -> true
|
HttpStatusCode.OK -> true
|
||||||
else -> error(response.bodyAsText())
|
else -> error(response.bodyAsText())
|
||||||
@@ -38,5 +95,35 @@ object NetworkDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun loadData(code: String): Result<UserEntity> = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext runCatching {
|
||||||
|
|
||||||
|
// Json.decodeFromString<UserEntity>(testJson) // удалить при проверке
|
||||||
|
|
||||||
|
val response = client.get(getUrl(code, Constants.INFO_URL))
|
||||||
|
when (response.status) {
|
||||||
|
HttpStatusCode.OK -> {
|
||||||
|
response.body<UserEntity>()
|
||||||
|
}
|
||||||
|
else -> error(response.bodyAsText())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun loadBooking(code: String): Result<BookingEntity> = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext runCatching {
|
||||||
|
|
||||||
|
// BookingEntity(Json.decodeFromString<Map<String, List<PlaceInfo>>>(testBookingJson)) // удалить при проверке
|
||||||
|
|
||||||
|
val response = client.get(getUrl(code, Constants.BOOKING_URL))
|
||||||
|
when (response.status) {
|
||||||
|
HttpStatusCode.OK -> {
|
||||||
|
BookingEntity(response.body<Map<String, List<PlaceInfo>>>())
|
||||||
|
}
|
||||||
|
else -> error(response.bodyAsText())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl"
|
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package ru.myitschool.work.domain.book
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.repo.BookRepository
|
||||||
|
|
||||||
|
class BookingUseCase(
|
||||||
|
private val repository: BookRepository
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(
|
||||||
|
userCode: String,
|
||||||
|
date: String,
|
||||||
|
placeId: Int,
|
||||||
|
placeName: String
|
||||||
|
): Result<Unit> {
|
||||||
|
return repository.bookPlace(userCode, date, placeId, placeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package ru.myitschool.work.domain.book
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.repo.BookRepository
|
||||||
|
import ru.myitschool.work.data.repo.MainRepository
|
||||||
|
import ru.myitschool.work.domain.book.entities.BookingEntity
|
||||||
|
import ru.myitschool.work.domain.main.entities.UserEntity
|
||||||
|
|
||||||
|
class LoadBookingUseCase(
|
||||||
|
private val repository: BookRepository
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(
|
||||||
|
text: String
|
||||||
|
): Result<BookingEntity> {
|
||||||
|
return repository.loadBooking(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package ru.myitschool.work.domain.book.entities
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class BookingEntity(
|
||||||
|
val bookings: Map<String, List<PlaceInfo>>
|
||||||
|
)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package ru.myitschool.work.domain.book.entities
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PlaceInfo(
|
||||||
|
val id: Int,
|
||||||
|
val place: String
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package ru.myitschool.work.domain.main
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.repo.MainRepository
|
||||||
|
import ru.myitschool.work.domain.main.entities.UserEntity
|
||||||
|
|
||||||
|
class LoadDataUseCase(
|
||||||
|
private val repository: MainRepository
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(
|
||||||
|
text: String
|
||||||
|
): Result<UserEntity> {
|
||||||
|
return repository.loadData(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package ru.myitschool.work.domain.main.entities
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class BookingInfo(
|
||||||
|
val id: Int,
|
||||||
|
val place: String
|
||||||
|
)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package ru.myitschool.work.domain.main.entities
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import ru.myitschool.work.formatDate
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UserEntity(
|
||||||
|
val name: String,
|
||||||
|
val photoUrl: String,
|
||||||
|
val booking: Map<String, BookingInfo>? = null
|
||||||
|
) {
|
||||||
|
fun getSortedBookings(): List<Pair<String, BookingInfo>> {
|
||||||
|
return booking?.entries
|
||||||
|
?.sortedBy { (date, _) -> date }
|
||||||
|
?.map { it.toPair() }
|
||||||
|
?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSortedBookingsWithFormattedDate(): List<Triple<String, String, BookingInfo>> {
|
||||||
|
return getSortedBookings().map { (date, bookingInfo) ->
|
||||||
|
Triple(date, date.formatDate(), bookingInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasBookings(): Boolean = !booking.isNullOrEmpty()
|
||||||
|
}
|
||||||
198
app/src/main/java/ru/myitschool/work/ui/Composables.kt
Normal file
198
app/src/main/java/ru/myitschool/work/ui/Composables.kt
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
package ru.myitschool.work.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import ru.myitschool.work.R
|
||||||
|
import ru.myitschool.work.ui.theme.Black
|
||||||
|
import ru.myitschool.work.ui.theme.Gray
|
||||||
|
import ru.myitschool.work.ui.theme.LightBlue
|
||||||
|
import ru.myitschool.work.ui.theme.LightGray
|
||||||
|
import ru.myitschool.work.ui.theme.Typography
|
||||||
|
import ru.myitschool.work.ui.theme.White
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseText24(
|
||||||
|
text: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
color: Color = Black,
|
||||||
|
textAlign: TextAlign = TextAlign.Left
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
fontSize = 24.sp,
|
||||||
|
style = Typography.bodyLarge,
|
||||||
|
modifier = modifier,
|
||||||
|
color = color,
|
||||||
|
textAlign = textAlign
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseButton(
|
||||||
|
text: String,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
contentColor = White,
|
||||||
|
disabledContainerColor = Color.Transparent,
|
||||||
|
disabledContentColor = White
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
BaseText16(text = text, color = White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Logo() {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.ic_git_clone_mini),
|
||||||
|
contentDescription = "Logo",
|
||||||
|
modifier = Modifier.padding(top = 40.dp, bottom = 60.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseText16(
|
||||||
|
text: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
color: Color = Black,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = Typography.bodySmall,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
color = color,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseText12(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
text: String,
|
||||||
|
color: Color = Black,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = Typography.bodySmall,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = color,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseText14(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
text: String,
|
||||||
|
color: Color = Black,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = Typography.bodySmall,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = color,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseInputText(
|
||||||
|
placeholder: String= "",
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
value: String
|
||||||
|
) {
|
||||||
|
|
||||||
|
TextField(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
placeholder = {
|
||||||
|
BaseText16(
|
||||||
|
text = placeholder,
|
||||||
|
color = Gray
|
||||||
|
)
|
||||||
|
},
|
||||||
|
textStyle = Typography.bodySmall.copy(fontSize = 16.sp),
|
||||||
|
colors = TextFieldDefaults.colors(
|
||||||
|
focusedContainerColor = LightBlue,
|
||||||
|
unfocusedContainerColor = LightBlue,
|
||||||
|
focusedIndicatorColor = Color.Transparent,
|
||||||
|
unfocusedIndicatorColor = Color.Transparent,
|
||||||
|
disabledIndicatorColor = Color.Transparent,
|
||||||
|
errorIndicatorColor = Color.Transparent
|
||||||
|
),
|
||||||
|
singleLine = true,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseText20(
|
||||||
|
text: String,
|
||||||
|
color: Color = Color.Unspecified,
|
||||||
|
style: TextStyle = Typography.bodySmall,
|
||||||
|
modifier: Modifier = Modifier.padding(7.dp),
|
||||||
|
textAlign: TextAlign = TextAlign.Unspecified
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = style,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
modifier = modifier,
|
||||||
|
color = color,
|
||||||
|
textAlign = textAlign
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseButton(
|
||||||
|
border: BorderStroke? = null,
|
||||||
|
enable: Boolean = true,
|
||||||
|
text: String,
|
||||||
|
btnColor: Color,
|
||||||
|
btnContentColor: Color,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
icon: @Composable RowScope.() -> Unit = {},
|
||||||
|
modifier: Modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
border = border,
|
||||||
|
enabled = enable,
|
||||||
|
onClick = onClick,
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = btnColor,
|
||||||
|
contentColor = btnContentColor,
|
||||||
|
disabledContainerColor = LightGray,
|
||||||
|
disabledContentColor = Gray
|
||||||
|
),
|
||||||
|
modifier = modifier,
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
) {
|
||||||
|
icon()
|
||||||
|
BaseText20(text = text)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ru.myitschool.work.ui.nav
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object SplashScreenDestination: AppDestination
|
||||||
@@ -14,7 +14,11 @@ import androidx.navigation.compose.rememberNavController
|
|||||||
import ru.myitschool.work.ui.nav.AuthScreenDestination
|
import ru.myitschool.work.ui.nav.AuthScreenDestination
|
||||||
import ru.myitschool.work.ui.nav.BookScreenDestination
|
import ru.myitschool.work.ui.nav.BookScreenDestination
|
||||||
import ru.myitschool.work.ui.nav.MainScreenDestination
|
import ru.myitschool.work.ui.nav.MainScreenDestination
|
||||||
|
import ru.myitschool.work.ui.nav.SplashScreenDestination
|
||||||
import ru.myitschool.work.ui.screen.auth.AuthScreen
|
import ru.myitschool.work.ui.screen.auth.AuthScreen
|
||||||
|
import ru.myitschool.work.ui.screen.book.BookScreen
|
||||||
|
import ru.myitschool.work.ui.screen.main.MainScreen
|
||||||
|
import ru.myitschool.work.ui.screen.splash.SplashScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppNavHost(
|
fun AppNavHost(
|
||||||
@@ -26,24 +30,19 @@ fun AppNavHost(
|
|||||||
enterTransition = { EnterTransition.None },
|
enterTransition = { EnterTransition.None },
|
||||||
exitTransition = { ExitTransition.None },
|
exitTransition = { ExitTransition.None },
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = AuthScreenDestination,
|
startDestination = SplashScreenDestination,
|
||||||
) {
|
) {
|
||||||
|
composable<SplashScreenDestination> {
|
||||||
|
SplashScreen(navController = navController)
|
||||||
|
}
|
||||||
composable<AuthScreenDestination> {
|
composable<AuthScreenDestination> {
|
||||||
AuthScreen(navController = navController)
|
AuthScreen(navController = navController)
|
||||||
}
|
}
|
||||||
composable<MainScreenDestination> {
|
composable<MainScreenDestination> {
|
||||||
Box(
|
MainScreen(navController = navController)
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Text(text = "Hello")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
composable<BookScreenDestination> {
|
composable<BookScreenDestination> {
|
||||||
Box(
|
BookScreen(navController = navController)
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Text(text = "Hello")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,27 +1,20 @@
|
|||||||
package ru.myitschool.work.ui.screen.auth
|
package ru.myitschool.work.ui.screen.auth
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextField
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
@@ -30,12 +23,20 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import ru.myitschool.work.R
|
import ru.myitschool.work.R
|
||||||
import ru.myitschool.work.core.TestIds
|
import ru.myitschool.work.core.TestIds
|
||||||
|
import ru.myitschool.work.ui.BaseButton
|
||||||
|
import ru.myitschool.work.ui.BaseInputText
|
||||||
|
import ru.myitschool.work.ui.BaseText12
|
||||||
|
import ru.myitschool.work.ui.BaseText24
|
||||||
|
import ru.myitschool.work.ui.Logo
|
||||||
import ru.myitschool.work.ui.nav.MainScreenDestination
|
import ru.myitschool.work.ui.nav.MainScreenDestination
|
||||||
|
import ru.myitschool.work.ui.theme.Blue
|
||||||
|
import ru.myitschool.work.ui.theme.Red
|
||||||
|
import ru.myitschool.work.ui.theme.White
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AuthScreen(
|
fun AuthScreen(
|
||||||
viewModel: AuthViewModel = viewModel(),
|
viewModel: AuthViewModel = viewModel(),
|
||||||
navController: NavController
|
navController: NavController,
|
||||||
) {
|
) {
|
||||||
val state by viewModel.uiState.collectAsState()
|
val state by viewModel.uiState.collectAsState()
|
||||||
|
|
||||||
@@ -45,24 +46,34 @@ fun AuthScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Box(
|
||||||
modifier = Modifier
|
contentAlignment = Alignment.Center,
|
||||||
.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
.padding(all = 24.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Column(
|
||||||
text = stringResource(R.string.auth_title),
|
modifier = Modifier
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
.width(400.dp)
|
||||||
textAlign = TextAlign.Center
|
.fillMaxHeight()
|
||||||
)
|
.padding(20.dp),
|
||||||
when (val currentState = state) {
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
is AuthState.Data -> Content(viewModel, currentState)
|
) {
|
||||||
is AuthState.Loading -> {
|
|
||||||
CircularProgressIndicator(
|
Logo()
|
||||||
modifier = Modifier.size(64.dp)
|
|
||||||
)
|
BaseText24(
|
||||||
|
text = stringResource(R.string.auth_title),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
when (state) {
|
||||||
|
is AuthState.Data -> Content(viewModel)
|
||||||
|
is AuthState.Loading -> {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 40.dp)
|
||||||
|
.size(64.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,28 +81,50 @@ fun AuthScreen(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Content(
|
private fun Content(
|
||||||
viewModel: AuthViewModel,
|
viewModel: AuthViewModel
|
||||||
state: AuthState.Data
|
|
||||||
) {
|
) {
|
||||||
var inputText by remember { mutableStateOf("") }
|
|
||||||
Spacer(modifier = Modifier.size(16.dp))
|
val isButtonEnabled by viewModel.isButtonEnabled.collectAsState()
|
||||||
TextField(
|
val errorStateValue by viewModel.errorStateValue.collectAsState()
|
||||||
modifier = Modifier.testTag(TestIds.Auth.CODE_INPUT).fillMaxWidth(),
|
val textState by viewModel.textState.collectAsState()
|
||||||
value = inputText,
|
|
||||||
onValueChange = {
|
Column(
|
||||||
inputText = it
|
modifier = Modifier.padding(vertical = 20.dp)
|
||||||
viewModel.onIntent(AuthIntent.TextInput(it))
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.auth_label)) }
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.size(16.dp))
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.testTag(TestIds.Auth.SIGN_BUTTON).fillMaxWidth(),
|
|
||||||
onClick = {
|
|
||||||
viewModel.onIntent(AuthIntent.Send(inputText))
|
|
||||||
},
|
|
||||||
enabled = true
|
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.auth_sign_in))
|
|
||||||
|
BaseInputText(
|
||||||
|
value = textState,
|
||||||
|
placeholder = stringResource(R.string.auth_label),
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(TestIds.Auth.CODE_INPUT)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
onValueChange = { viewModel.onIntent(AuthIntent.TextInput(it)) }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (errorStateValue != "") {
|
||||||
|
BaseText12(
|
||||||
|
text = errorStateValue,
|
||||||
|
color = Red,
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(TestIds.Auth.ERROR)
|
||||||
|
.padding(
|
||||||
|
start = 10.dp,
|
||||||
|
top = 5.dp,
|
||||||
|
bottom = 0.dp
|
||||||
|
)
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseButton(
|
||||||
|
text = stringResource(R.string.auth_sign_in),
|
||||||
|
onClick = { viewModel.onIntent(AuthIntent.Send(textState)) },
|
||||||
|
btnColor = Blue,
|
||||||
|
enable = isButtonEnabled,
|
||||||
|
btnContentColor = White,
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(TestIds.Auth.SIGN_BUTTON)
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -1,43 +1,62 @@
|
|||||||
package ru.myitschool.work.ui.screen.auth
|
package ru.myitschool.work.ui.screen.auth
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.myitschool.work.App
|
||||||
|
import ru.myitschool.work.data.datastore.UserCode
|
||||||
import ru.myitschool.work.data.repo.AuthRepository
|
import ru.myitschool.work.data.repo.AuthRepository
|
||||||
import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase
|
|
||||||
|
|
||||||
class AuthViewModel : ViewModel() {
|
class LoginViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
private val checkAndSaveAuthCodeUseCase by lazy { CheckAndSaveAuthCodeUseCase(AuthRepository) }
|
|
||||||
private val _uiState = MutableStateFlow<AuthState>(AuthState.Data)
|
|
||||||
val uiState: StateFlow<AuthState> = _uiState.asStateFlow()
|
|
||||||
|
|
||||||
private val _actionFlow: MutableSharedFlow<Unit> = MutableSharedFlow()
|
private val store by lazy { (getApplication() as App).dataStoreManager }
|
||||||
val actionFlow: SharedFlow<Unit> = _actionFlow
|
|
||||||
|
|
||||||
fun onIntent(intent: AuthIntent) {
|
private val _screenState = MutableStateFlow<LoginState>(LoginState.Ready)
|
||||||
|
val screenState: StateFlow<LoginState> = _screenState.asStateFlow()
|
||||||
|
|
||||||
|
private val _navigate = MutableSharedFlow<Unit>()
|
||||||
|
val navigate: SharedFlow<Unit> = _navigate
|
||||||
|
|
||||||
|
private val _input = MutableStateFlow("")
|
||||||
|
val input: StateFlow<String> get() = _input
|
||||||
|
|
||||||
|
private val _error = MutableStateFlow("")
|
||||||
|
val error: StateFlow<String> get() = _error
|
||||||
|
|
||||||
|
val isValid = input.map { it.length == 4 && it.matches(Regex("[A-Za-z0-9]+")) }
|
||||||
|
.stateIn(viewModelScope, SharingStarted.Eagerly, false)
|
||||||
|
|
||||||
|
fun perform(intent: LoginIntent) {
|
||||||
when (intent) {
|
when (intent) {
|
||||||
is AuthIntent.Send -> {
|
|
||||||
viewModelScope.launch(Dispatchers.Default) {
|
is LoginIntent.Type -> {
|
||||||
_uiState.update { AuthState.Loading }
|
_input.value = intent.value
|
||||||
checkAndSaveAuthCodeUseCase.invoke("9999").fold(
|
_error.value = ""
|
||||||
onSuccess = {
|
}
|
||||||
_actionFlow.emit(Unit)
|
|
||||||
},
|
is LoginIntent.Submit -> {
|
||||||
onFailure = { error ->
|
authorize()
|
||||||
error.printStackTrace()
|
|
||||||
_actionFlow.emit(Unit)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is AuthIntent.TextInput -> Unit
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private fun authorize() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_screenState.value = LoginState.Loading
|
||||||
|
|
||||||
|
AuthRepository.validateCode(_input.value).fold(
|
||||||
|
onSuccess = {
|
||||||
|
store.saveUserCode(UserCode(_input.value))
|
||||||
|
_navigate.emit(Unit)
|
||||||
|
},
|
||||||
|
onFailure = { err ->
|
||||||
|
_screenState.value = LoginState.Ready
|
||||||
|
_error.value = err.message ?: "Ошибка"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.book
|
||||||
|
|
||||||
|
sealed interface BookAction {
|
||||||
|
object Auth: BookAction
|
||||||
|
object Main: BookAction
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.book
|
||||||
|
|
||||||
|
sealed interface BookIntent {
|
||||||
|
object Back: BookIntent
|
||||||
|
object LoadBooking: BookIntent
|
||||||
|
object Book : BookIntent
|
||||||
|
data class SelectDate(val date: String) : BookIntent
|
||||||
|
data class SelectPlace(
|
||||||
|
val placeId: Int,
|
||||||
|
val placeName: String
|
||||||
|
) : BookIntent
|
||||||
|
}
|
||||||
@@ -0,0 +1,415 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.book
|
||||||
|
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.selection.selectable
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonColors
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import ru.myitschool.work.R
|
||||||
|
import ru.myitschool.work.core.TestIds.Book
|
||||||
|
import ru.myitschool.work.domain.book.entities.BookingEntity
|
||||||
|
import ru.myitschool.work.domain.book.entities.PlaceInfo
|
||||||
|
import ru.myitschool.work.formatBookingDate
|
||||||
|
import ru.myitschool.work.ui.BaseButton
|
||||||
|
import ru.myitschool.work.ui.BaseText16
|
||||||
|
import ru.myitschool.work.ui.BaseText24
|
||||||
|
import ru.myitschool.work.ui.nav.AuthScreenDestination
|
||||||
|
import ru.myitschool.work.ui.nav.MainScreenDestination
|
||||||
|
import ru.myitschool.work.ui.theme.Black
|
||||||
|
import ru.myitschool.work.ui.theme.Blue
|
||||||
|
import ru.myitschool.work.ui.theme.Typography
|
||||||
|
import ru.myitschool.work.ui.theme.White
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BookScreen(
|
||||||
|
navController: NavController,
|
||||||
|
viewModel: BookViewModel = viewModel(),
|
||||||
|
) {
|
||||||
|
val state by viewModel.uiState.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.actionFlow.collect { action ->
|
||||||
|
when(action) {
|
||||||
|
is BookAction.Auth -> navController.navigate(AuthScreenDestination)
|
||||||
|
|
||||||
|
is BookAction.Main -> navController.navigate(MainScreenDestination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when(state) {
|
||||||
|
is BookState.Loading -> {
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(64.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is BookState.Data -> {
|
||||||
|
val dataState = state as BookState.Data
|
||||||
|
DataContent(
|
||||||
|
viewModel = viewModel,
|
||||||
|
bookingData = dataState.userBooking,
|
||||||
|
selectedDate = dataState.selectedDate,
|
||||||
|
selectedPlaceId = dataState.selectedPlaceId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is BookState.Error -> ErrorContent(viewModel)
|
||||||
|
is BookState.Empty -> EmptyContent(viewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EmptyContent(
|
||||||
|
viewModel: BookViewModel
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(15.dp)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.width(320.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(80.dp))
|
||||||
|
|
||||||
|
BaseText24(
|
||||||
|
text = stringResource(R.string.book_all_booked),
|
||||||
|
modifier = Modifier.testTag(Book.EMPTY),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
BaseButton(
|
||||||
|
text = stringResource(R.string.book_back),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.testTag(Book.BACK_BUTTON),
|
||||||
|
onClick = { viewModel.onIntent(BookIntent.Back) },
|
||||||
|
btnContentColor = White,
|
||||||
|
btnColor = Blue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ErrorContent(
|
||||||
|
viewModel: BookViewModel
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(15.dp)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.width(320.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(80.dp))
|
||||||
|
|
||||||
|
BaseText24(
|
||||||
|
text = stringResource(R.string.book_error),
|
||||||
|
modifier = Modifier.testTag(Book.ERROR),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
BaseButton(
|
||||||
|
border = BorderStroke(1.dp, Blue),
|
||||||
|
text = stringResource(R.string.book_back),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.testTag(Book.BACK_BUTTON),
|
||||||
|
onClick = { viewModel.onIntent(BookIntent.Back) },
|
||||||
|
btnContentColor = Blue,
|
||||||
|
btnColor = Color.Transparent
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(15.dp))
|
||||||
|
|
||||||
|
BaseButton(
|
||||||
|
text = stringResource(R.string.main_update),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.testTag(Book.REFRESH_BUTTON),
|
||||||
|
onClick = { viewModel.onIntent(BookIntent.LoadBooking) },
|
||||||
|
btnContentColor = White,
|
||||||
|
btnColor = Blue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DataContent(
|
||||||
|
viewModel: BookViewModel,
|
||||||
|
bookingData: BookingEntity,
|
||||||
|
selectedDate: String,
|
||||||
|
selectedPlaceId: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
val availableDates = bookingData.bookings
|
||||||
|
.filter { it.value.isNotEmpty() }
|
||||||
|
.keys
|
||||||
|
.sorted()
|
||||||
|
val placesForSelectedDate = bookingData.bookings[selectedDate] ?: emptyList()
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(bottomStart = 16.dp, bottomEnd = 16.dp))
|
||||||
|
.background(Blue)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 10.dp, vertical = 15.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
BaseText24(
|
||||||
|
text = stringResource(R.string.book_new_book),
|
||||||
|
color = White,
|
||||||
|
modifier = Modifier.padding(start = 15.dp)
|
||||||
|
)
|
||||||
|
BaseButton(
|
||||||
|
text = stringResource(R.string.book_back),
|
||||||
|
modifier = Modifier.testTag(Book.BACK_BUTTON),
|
||||||
|
onClick = { viewModel.onIntent(BookIntent.Back) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(vertical = 20.dp, horizontal = 10.dp)
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.background(White)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(13.dp)
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.book_available_date),
|
||||||
|
style = Typography.bodyMedium,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
)
|
||||||
|
|
||||||
|
BookDateList(
|
||||||
|
dates = availableDates,
|
||||||
|
selectedDate = selectedDate,
|
||||||
|
onDateSelected = { date ->
|
||||||
|
viewModel.onIntent(BookIntent.SelectDate(date))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.book_choose_place),
|
||||||
|
style = Typography.bodyMedium,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
)
|
||||||
|
|
||||||
|
BookPlaceList(
|
||||||
|
places = placesForSelectedDate,
|
||||||
|
selectedPlaceId = selectedPlaceId,
|
||||||
|
onPlaceSelected = { placeId, placeName ->
|
||||||
|
viewModel.onIntent(BookIntent.SelectPlace(placeId, placeName))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseButton(
|
||||||
|
enable = selectedPlaceId != -1,
|
||||||
|
text = stringResource(R.string.booking_button),
|
||||||
|
btnColor = Blue,
|
||||||
|
btnContentColor = White,
|
||||||
|
onClick = { viewModel.onIntent(BookIntent.Book) },
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(Book.BOOK_BUTTON)
|
||||||
|
.padding(horizontal = 10.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BookPlaceList(
|
||||||
|
places: List<PlaceInfo>,
|
||||||
|
selectedPlaceId: Int,
|
||||||
|
onPlaceSelected: (Int, String) -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(vertical = 15.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
if (places.isEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = "Нет доступных мест для выбранной даты",
|
||||||
|
color = Color.Gray,
|
||||||
|
style = Typography.bodyMedium,
|
||||||
|
modifier = Modifier.padding(vertical = 8.dp)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
places.forEachIndexed { index, placeInfo ->
|
||||||
|
BookPlaceListElement(
|
||||||
|
placeInfo = placeInfo,
|
||||||
|
isSelected = placeInfo.id == selectedPlaceId,
|
||||||
|
onPlaceSelected = { onPlaceSelected(placeInfo.id, placeInfo.place) },
|
||||||
|
index = index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BookPlaceListElement(
|
||||||
|
placeInfo: PlaceInfo,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onPlaceSelected: () -> Unit,
|
||||||
|
index: Int
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.selectable(
|
||||||
|
selected = isSelected,
|
||||||
|
onClick = onPlaceSelected
|
||||||
|
)
|
||||||
|
.testTag(Book.getIdPlaceItemByPosition(index))
|
||||||
|
.padding(vertical = 12.dp, horizontal = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
BaseText16(
|
||||||
|
text = placeInfo.place,
|
||||||
|
modifier = Modifier.testTag(Book.ITEM_PLACE_TEXT)
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.border(
|
||||||
|
width = 2.dp,
|
||||||
|
color = if (isSelected) Blue else Color.Gray,
|
||||||
|
shape = CircleShape
|
||||||
|
)
|
||||||
|
.background(
|
||||||
|
color = if (isSelected) Blue else Color.Transparent,
|
||||||
|
shape = CircleShape
|
||||||
|
)
|
||||||
|
.testTag(Book.ITEM_PLACE_SELECTOR)
|
||||||
|
) {
|
||||||
|
if (isSelected) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(12.dp)
|
||||||
|
.background(Color.White, CircleShape)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BookDateList(
|
||||||
|
dates: List<String>,
|
||||||
|
selectedDate: String,
|
||||||
|
onDateSelected: (String) -> Unit
|
||||||
|
) {
|
||||||
|
FlowRow(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(7.dp),
|
||||||
|
modifier = Modifier.padding(vertical = 15.dp)
|
||||||
|
) {
|
||||||
|
dates.forEachIndexed { index, date ->
|
||||||
|
BookDateListElement(
|
||||||
|
date = date,
|
||||||
|
isSelected = date == selectedDate,
|
||||||
|
onClick = { onDateSelected(date) },
|
||||||
|
index = index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BookDateListElement(
|
||||||
|
date: String,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
index: Int
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
contentPadding = PaddingValues(0.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(Book.getIdDateItemByPosition(index))
|
||||||
|
.padding(0.dp),
|
||||||
|
border = BorderStroke(1.dp, if (isSelected) Blue else Black,),
|
||||||
|
onClick = onClick,
|
||||||
|
colors = ButtonColors(
|
||||||
|
contentColor = if (isSelected) White else Black,
|
||||||
|
containerColor = if (isSelected) Blue else Color.Transparent,
|
||||||
|
disabledContentColor = Black,
|
||||||
|
disabledContainerColor = Color.Transparent),
|
||||||
|
) {
|
||||||
|
val formattedDate = date.formatBookingDate()
|
||||||
|
BaseText16(
|
||||||
|
text = formattedDate,
|
||||||
|
modifier = Modifier.testTag(Book.ITEM_DATE),
|
||||||
|
color = if (isSelected) White else Black,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.book
|
||||||
|
|
||||||
|
import ru.myitschool.work.domain.book.entities.BookingEntity
|
||||||
|
|
||||||
|
sealed interface BookState {
|
||||||
|
object Loading: BookState
|
||||||
|
data class Data(
|
||||||
|
val userBooking: BookingEntity,
|
||||||
|
val selectedDate: String = "",
|
||||||
|
val selectedPlaceId: Int = -1,
|
||||||
|
val selectedPlaceName: String = ""
|
||||||
|
): BookState
|
||||||
|
object Error: BookState
|
||||||
|
object Empty: BookState
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.book
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.myitschool.work.App
|
||||||
|
import ru.myitschool.work.data.repo.BookRepository
|
||||||
|
import ru.myitschool.work.data.repo.MainRepository
|
||||||
|
import ru.myitschool.work.domain.book.BookingUseCase
|
||||||
|
import ru.myitschool.work.domain.book.LoadBookingUseCase
|
||||||
|
import ru.myitschool.work.domain.main.LoadDataUseCase
|
||||||
|
import ru.myitschool.work.ui.screen.main.MainAction
|
||||||
|
import ru.myitschool.work.ui.screen.main.MainIntent
|
||||||
|
import ru.myitschool.work.ui.screen.main.MainState
|
||||||
|
import kotlin.text.isEmpty
|
||||||
|
|
||||||
|
class BookViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
|
private val loadBookingUseCase by lazy { LoadBookingUseCase(BookRepository) }
|
||||||
|
|
||||||
|
private val bookingUseCase by lazy { BookingUseCase (BookRepository) }
|
||||||
|
|
||||||
|
private val dataStoreManager by lazy {
|
||||||
|
(getApplication() as App).dataStoreManager
|
||||||
|
}
|
||||||
|
private val _uiState = MutableStateFlow<BookState>(BookState.Loading)
|
||||||
|
val uiState: StateFlow<BookState> = _uiState.asStateFlow()
|
||||||
|
private val _actionFlow: MutableSharedFlow<BookAction> = MutableSharedFlow()
|
||||||
|
val actionFlow: SharedFlow<BookAction> = _actionFlow
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadBooking()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bookSelectedPlace() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val userCode = dataStoreManager.getUserCode().first()
|
||||||
|
val currentState = _uiState.value
|
||||||
|
|
||||||
|
if (currentState is BookState.Data && currentState.selectedPlaceId != -1) {
|
||||||
|
bookingUseCase.invoke(
|
||||||
|
userCode = userCode.code,
|
||||||
|
date = currentState.selectedDate,
|
||||||
|
placeId = currentState.selectedPlaceId,
|
||||||
|
placeName = currentState.selectedPlaceName
|
||||||
|
).fold(
|
||||||
|
onSuccess = {
|
||||||
|
_actionFlow.emit(BookAction.Main)
|
||||||
|
},
|
||||||
|
onFailure = { error ->
|
||||||
|
error.printStackTrace()
|
||||||
|
_uiState.update { BookState.Error }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (error: Exception) {
|
||||||
|
error.printStackTrace()
|
||||||
|
_uiState.update { BookState.Error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadBooking() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_uiState.update { BookState.Loading }
|
||||||
|
|
||||||
|
try {
|
||||||
|
val userCode = dataStoreManager.getUserCode().first()
|
||||||
|
|
||||||
|
if (userCode.code.isEmpty()) {
|
||||||
|
_actionFlow.emit(BookAction.Auth)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
loadBookingUseCase.invoke(userCode.code).fold(
|
||||||
|
onSuccess = { data ->
|
||||||
|
val availableDates = data.bookings
|
||||||
|
.filter { it.value.isNotEmpty() }
|
||||||
|
.keys
|
||||||
|
.sorted()
|
||||||
|
|
||||||
|
if (availableDates.isEmpty()) {
|
||||||
|
_uiState.update { BookState.Empty }
|
||||||
|
} else {
|
||||||
|
val selectedDate = availableDates.first()
|
||||||
|
val placesForSelectedDate = data.bookings[selectedDate] ?: emptyList()
|
||||||
|
val selectedPlaceId = placesForSelectedDate.firstOrNull()?.id ?: -1
|
||||||
|
val selectedPlaceName = placesForSelectedDate.firstOrNull()?.place ?: ""
|
||||||
|
|
||||||
|
_uiState.update {
|
||||||
|
BookState.Data(
|
||||||
|
userBooking = data,
|
||||||
|
selectedDate = selectedDate,
|
||||||
|
selectedPlaceId = selectedPlaceId,
|
||||||
|
selectedPlaceName = selectedPlaceName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure = { error ->
|
||||||
|
error.printStackTrace()
|
||||||
|
_uiState.update { BookState.Error }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (error: Exception) {
|
||||||
|
error.printStackTrace()
|
||||||
|
_uiState.update { BookState.Error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onIntent(intent: BookIntent) {
|
||||||
|
when (intent) {
|
||||||
|
is BookIntent.LoadBooking -> loadBooking()
|
||||||
|
|
||||||
|
is BookIntent.Back -> {
|
||||||
|
viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
_actionFlow.emit(BookAction.Main)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is BookIntent.Book -> bookSelectedPlace()
|
||||||
|
|
||||||
|
is BookIntent.SelectDate -> {
|
||||||
|
val currentState = _uiState.value
|
||||||
|
if (currentState is BookState.Data) {
|
||||||
|
val placesForDate =
|
||||||
|
currentState.userBooking.bookings[intent.date] ?: emptyList()
|
||||||
|
val newSelectedPlaceId = placesForDate.firstOrNull()?.id ?: -1
|
||||||
|
val newSelectedPlaceName = placesForDate.firstOrNull()?.place ?: ""
|
||||||
|
|
||||||
|
_uiState.update {
|
||||||
|
currentState.copy(
|
||||||
|
selectedDate = intent.date,
|
||||||
|
selectedPlaceId = newSelectedPlaceId,
|
||||||
|
selectedPlaceName = newSelectedPlaceName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is BookIntent.SelectPlace -> {
|
||||||
|
val currentState = _uiState.value
|
||||||
|
if (currentState is BookState.Data) {
|
||||||
|
_uiState.update {
|
||||||
|
currentState.copy(
|
||||||
|
selectedPlaceId = intent.placeId,
|
||||||
|
selectedPlaceName = intent.placeName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.main
|
||||||
|
|
||||||
|
sealed interface MainAction {
|
||||||
|
object Booking: MainAction
|
||||||
|
object Auth: MainAction
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.main
|
||||||
|
|
||||||
|
sealed interface MainIntent {
|
||||||
|
object Logout: MainIntent
|
||||||
|
object Booking: MainIntent
|
||||||
|
object LoadData: MainIntent
|
||||||
|
}
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.main
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import coil3.compose.rememberAsyncImagePainter
|
||||||
|
import ru.myitschool.work.R
|
||||||
|
import ru.myitschool.work.core.TestIds.Main
|
||||||
|
import ru.myitschool.work.domain.main.entities.BookingInfo
|
||||||
|
import ru.myitschool.work.domain.main.entities.UserEntity
|
||||||
|
import ru.myitschool.work.ui.BaseButton
|
||||||
|
import ru.myitschool.work.ui.BaseText14
|
||||||
|
import ru.myitschool.work.ui.BaseText16
|
||||||
|
import ru.myitschool.work.ui.BaseText20
|
||||||
|
import ru.myitschool.work.ui.BaseText24
|
||||||
|
import ru.myitschool.work.ui.nav.AuthScreenDestination
|
||||||
|
import ru.myitschool.work.ui.nav.BookScreenDestination
|
||||||
|
import ru.myitschool.work.ui.theme.Black
|
||||||
|
import ru.myitschool.work.ui.theme.Blue
|
||||||
|
import ru.myitschool.work.ui.theme.LightGray
|
||||||
|
import ru.myitschool.work.ui.theme.Typography
|
||||||
|
import ru.myitschool.work.ui.theme.White
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainScreen(
|
||||||
|
navController: NavController,
|
||||||
|
viewModel: MainViewModel = viewModel()
|
||||||
|
) {
|
||||||
|
|
||||||
|
val state by viewModel.uiState.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.actionFlow.collect { action ->
|
||||||
|
when(action) {
|
||||||
|
is MainAction.Auth -> navController.navigate(AuthScreenDestination)
|
||||||
|
|
||||||
|
is MainAction.Booking -> navController.navigate(BookScreenDestination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when(state) {
|
||||||
|
is MainState.Loading -> {
|
||||||
|
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(64.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is MainState.Error -> {
|
||||||
|
ErrorContent(viewModel)
|
||||||
|
}
|
||||||
|
is MainState.Data -> {
|
||||||
|
DataContent(
|
||||||
|
viewModel,
|
||||||
|
userData = (state as MainState.Data).userData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ErrorContent(viewModel: MainViewModel){
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(15.dp)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.width(320.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(80.dp))
|
||||||
|
|
||||||
|
BaseText24(
|
||||||
|
text = stringResource(R.string.data_error_message),
|
||||||
|
modifier = Modifier.testTag(Main.ERROR),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
BaseButton(
|
||||||
|
text = stringResource(R.string.main_update),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.testTag(Main.REFRESH_BUTTON),
|
||||||
|
onClick = { viewModel.onIntent(MainIntent.LoadData) },
|
||||||
|
btnContentColor = White,
|
||||||
|
btnColor = Blue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DataContent(
|
||||||
|
viewModel: MainViewModel,
|
||||||
|
userData: UserEntity
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.background(LightGray)
|
||||||
|
.fillMaxSize()
|
||||||
|
.width(400.dp)
|
||||||
|
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(bottomStart = 16.dp, bottomEnd = 16.dp))
|
||||||
|
.background(Blue)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(10.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
BaseButton(
|
||||||
|
text = stringResource(R.string.main_update),
|
||||||
|
onClick = { viewModel.onIntent(MainIntent.LoadData) },
|
||||||
|
modifier = Modifier.testTag(Main.REFRESH_BUTTON)
|
||||||
|
)
|
||||||
|
BaseButton(
|
||||||
|
text = stringResource(R.string.main_log_out),
|
||||||
|
onClick = { viewModel.onIntent(MainIntent.Logout) },
|
||||||
|
modifier = Modifier.testTag(Main.LOGOUT_BUTTON)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Image(
|
||||||
|
painter = rememberAsyncImagePainter(
|
||||||
|
model = userData.photoUrl,
|
||||||
|
error = painterResource(R.drawable.github)
|
||||||
|
),
|
||||||
|
contentDescription = stringResource(R.string.main_avatar_description),
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(999.dp))
|
||||||
|
.testTag(Main.PROFILE_IMAGE)
|
||||||
|
.width(150.dp)
|
||||||
|
.height(150.dp)
|
||||||
|
.padding(20.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
BaseText20(
|
||||||
|
text = userData.name,
|
||||||
|
color = White,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(Main.PROFILE_NAME)
|
||||||
|
.width(250.dp),
|
||||||
|
style = Typography.bodyLarge
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(20.dp)
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.background(White)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.main_booking_title),
|
||||||
|
style = Typography.bodyMedium,
|
||||||
|
color = Black,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
horizontal = 10.dp,
|
||||||
|
vertical = 20.dp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (userData.hasBookings()) {
|
||||||
|
SortedBookingList(userData = userData)
|
||||||
|
} else {
|
||||||
|
EmptyBookings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BaseButton(
|
||||||
|
text = stringResource(R.string.booking_button),
|
||||||
|
btnColor = Blue,
|
||||||
|
btnContentColor = White,
|
||||||
|
onClick = { viewModel.onIntent(MainIntent.Booking) },
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(Main.ADD_BUTTON)
|
||||||
|
.padding(horizontal = 10.dp, vertical = 15.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SortedBookingList(userData: UserEntity) {
|
||||||
|
val sortedBookings = remember(userData.booking) {
|
||||||
|
userData.getSortedBookingsWithFormattedDate()
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 20.dp)
|
||||||
|
) {
|
||||||
|
itemsIndexed(
|
||||||
|
items = sortedBookings
|
||||||
|
) { index, (originalDate, formattedDate, bookingInfo) ->
|
||||||
|
BookingItem(
|
||||||
|
originalDate = originalDate,
|
||||||
|
formattedDate = formattedDate,
|
||||||
|
bookingInfo = bookingInfo,
|
||||||
|
index = index
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BookingItem(
|
||||||
|
originalDate: String,
|
||||||
|
formattedDate: String,
|
||||||
|
bookingInfo: BookingInfo,
|
||||||
|
index: Int
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(Main.getIdItemByPosition(index))
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 20.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
BaseText14(
|
||||||
|
text = bookingInfo.place,
|
||||||
|
modifier = Modifier.testTag(Main.ITEM_PLACE)
|
||||||
|
)
|
||||||
|
BaseText14(
|
||||||
|
text = formattedDate,
|
||||||
|
modifier = Modifier.testTag(Main.ITEM_DATE)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EmptyBookings() {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
BaseText16(
|
||||||
|
text = stringResource(R.string.main_empty_booking)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.main
|
||||||
|
|
||||||
|
import ru.myitschool.work.domain.main.entities.UserEntity
|
||||||
|
|
||||||
|
sealed interface MainState {
|
||||||
|
data class Data(val userData: UserEntity): MainState
|
||||||
|
object Loading: MainState
|
||||||
|
object Error: MainState
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.main
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.myitschool.work.App
|
||||||
|
import ru.myitschool.work.data.repo.MainRepository
|
||||||
|
import ru.myitschool.work.domain.main.LoadDataUseCase
|
||||||
|
|
||||||
|
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
|
private val dataStoreManager by lazy {
|
||||||
|
(getApplication() as App).dataStoreManager
|
||||||
|
}
|
||||||
|
|
||||||
|
private val loadDataUseCase by lazy { LoadDataUseCase(MainRepository) }
|
||||||
|
private val _uiState = MutableStateFlow<MainState>(MainState.Loading)
|
||||||
|
val uiState: StateFlow<MainState> = _uiState.asStateFlow()
|
||||||
|
private val _actionFlow: MutableSharedFlow<MainAction> = MutableSharedFlow()
|
||||||
|
val actionFlow: SharedFlow<MainAction> = _actionFlow
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_uiState.update { MainState.Loading }
|
||||||
|
|
||||||
|
try {
|
||||||
|
val userCode = dataStoreManager.getUserCode().first()
|
||||||
|
|
||||||
|
if (userCode.code.isEmpty()) {
|
||||||
|
_actionFlow.emit(MainAction.Auth)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDataUseCase.invoke(userCode.code).fold(
|
||||||
|
onSuccess = { data ->
|
||||||
|
_uiState.update { MainState.Data(data) }
|
||||||
|
},
|
||||||
|
onFailure = { error ->
|
||||||
|
error.printStackTrace()
|
||||||
|
_uiState.update { MainState.Error }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (error: Exception) {
|
||||||
|
error.printStackTrace()
|
||||||
|
_uiState.update { MainState.Error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onIntent( intent: MainIntent) {
|
||||||
|
when(intent) {
|
||||||
|
is MainIntent.LoadData -> loadData()
|
||||||
|
|
||||||
|
is MainIntent.Booking -> {
|
||||||
|
viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
_actionFlow.emit(MainAction.Booking)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is MainIntent.Logout -> {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
|
||||||
|
dataStoreManager.clearUserCode()
|
||||||
|
_actionFlow.emit(MainAction.Auth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.splash
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import ru.myitschool.work.ui.nav.AuthScreenDestination
|
||||||
|
import ru.myitschool.work.ui.nav.MainScreenDestination
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SplashScreen(
|
||||||
|
navController: NavController,
|
||||||
|
viewModel: SplashViewModel = viewModel()
|
||||||
|
) {
|
||||||
|
|
||||||
|
val splashState by viewModel.splashState.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(splashState) {
|
||||||
|
when (splashState) {
|
||||||
|
is SplashState.Authenticated -> {
|
||||||
|
navController.navigate(MainScreenDestination)
|
||||||
|
}
|
||||||
|
is SplashState.UnAuthenticated -> {
|
||||||
|
navController.navigate(AuthScreenDestination)
|
||||||
|
}
|
||||||
|
is SplashState.Error -> {
|
||||||
|
navController.navigate(AuthScreenDestination)
|
||||||
|
}
|
||||||
|
SplashState.Loading -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(modifier = Modifier.size(64.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.splash
|
||||||
|
|
||||||
|
import android.os.Message
|
||||||
|
|
||||||
|
sealed interface SplashState {
|
||||||
|
object Loading: SplashState
|
||||||
|
object Authenticated: SplashState
|
||||||
|
object UnAuthenticated: SplashState
|
||||||
|
class Error(message: String): SplashState
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.splash
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.myitschool.work.App
|
||||||
|
|
||||||
|
class SplashViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
|
private val dataStoreManager by lazy {
|
||||||
|
(getApplication() as App).dataStoreManager
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _splashState = MutableStateFlow<SplashState>(SplashState.Loading)
|
||||||
|
val splashState: StateFlow<SplashState> = _splashState.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
checkAuthStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkAuthStatus() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val userCode = dataStoreManager.getUserCode().first()
|
||||||
|
|
||||||
|
val isAuthenticated = if (userCode.code.isEmpty()) false else true
|
||||||
|
|
||||||
|
_splashState.value = if (isAuthenticated) {
|
||||||
|
SplashState.Authenticated
|
||||||
|
} else {
|
||||||
|
SplashState.UnAuthenticated
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_splashState.value = SplashState.Error(e.message ?: "Unknown error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,4 +8,18 @@ val Pink80 = Color(0xFFEFB8C8)
|
|||||||
|
|
||||||
val Purple40 = Color(0xFF6650a4)
|
val Purple40 = Color(0xFF6650a4)
|
||||||
val PurpleGrey40 = Color(0xFF625b71)
|
val PurpleGrey40 = Color(0xFF625b71)
|
||||||
val Pink40 = Color(0xFF7D5260)
|
val Pink40 = Color(0xFF7D5260)
|
||||||
|
|
||||||
|
val Blue = Color(0xFF004BFF)
|
||||||
|
|
||||||
|
val Gray = Color(0xFF777777)
|
||||||
|
|
||||||
|
val LightBlue = Color(0xFFF2EFFF)
|
||||||
|
|
||||||
|
val White = Color(0xFFFFFFFF)
|
||||||
|
|
||||||
|
val Red = Color(0xFFFF4D4D)
|
||||||
|
|
||||||
|
val LightGray = Color(0xFFF2F1F7)
|
||||||
|
|
||||||
|
val Black = Color(0xFF000000)
|
||||||
@@ -2,19 +2,27 @@ package ru.myitschool.work.ui.theme
|
|||||||
|
|
||||||
import androidx.compose.material3.Typography
|
import androidx.compose.material3.Typography
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import ru.myitschool.work.R
|
||||||
|
|
||||||
// Set of Material typography styles to start with
|
// Set of Material typography styles to start with
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val Typography = Typography(
|
val Typography = Typography(
|
||||||
|
bodySmall = TextStyle(
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
),
|
||||||
|
bodyMedium = TextStyle(
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
),
|
||||||
bodyLarge = TextStyle(
|
bodyLarge = TextStyle(
|
||||||
fontFamily = FontFamily.Default,
|
fontWeight = FontWeight.Bold,
|
||||||
fontWeight = FontWeight.Normal,
|
),
|
||||||
fontSize = 16.sp,
|
|
||||||
lineHeight = 24.sp,
|
|
||||||
letterSpacing = 0.5.sp
|
|
||||||
)
|
|
||||||
/* Other default text styles to override
|
/* Other default text styles to override
|
||||||
titleLarge = TextStyle(
|
titleLarge = TextStyle(
|
||||||
fontFamily = FontFamily.Default,
|
fontFamily = FontFamily.Default,
|
||||||
|
|||||||
26
app/src/main/java/ru/myitschool/work/utils.kt
Normal file
26
app/src/main/java/ru/myitschool/work/utils.kt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package ru.myitschool.work
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
fun String.formatDate(): String {
|
||||||
|
return try {
|
||||||
|
val inputFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
val outputFormat = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault())
|
||||||
|
val date = inputFormat.parse(this)
|
||||||
|
outputFormat.format(date)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.formatBookingDate(): String {
|
||||||
|
return try {
|
||||||
|
val inputFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
val outputFormat = SimpleDateFormat("dd.MM", Locale.getDefault())
|
||||||
|
val date = inputFormat.parse(this)
|
||||||
|
outputFormat.format(date)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/src/main/res/drawable/actions_icon.xml
Normal file
27
app/src/main/res/drawable/actions_icon.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="34dp"
|
||||||
|
android:height="34dp"
|
||||||
|
android:viewportWidth="34"
|
||||||
|
android:viewportHeight="34">
|
||||||
|
<path
|
||||||
|
android:pathData="M17.321,18.625C18.08,18.625 18.695,18.01 18.695,17.252C18.695,16.493 18.08,15.878 17.321,15.878C16.563,15.878 15.948,16.493 15.948,17.252C15.948,18.01 16.563,18.625 17.321,18.625Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="3.38015"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M17.321,9.012C18.08,9.012 18.695,8.398 18.695,7.639C18.695,6.881 18.08,6.266 17.321,6.266C16.563,6.266 15.948,6.881 15.948,7.639C15.948,8.398 16.563,9.012 17.321,9.012Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="3.38015"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M17.321,28.237C18.08,28.237 18.695,27.622 18.695,26.864C18.695,26.105 18.08,25.491 17.321,25.491C16.563,25.491 15.948,26.105 15.948,26.864C15.948,27.622 16.563,28.237 17.321,28.237Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="3.38015"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
19
app/src/main/res/drawable/app_start_button.xml
Normal file
19
app/src/main/res/drawable/app_start_button.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="151dp"
|
||||||
|
android:height="72dp"
|
||||||
|
android:viewportWidth="151"
|
||||||
|
android:viewportHeight="72">
|
||||||
|
<path
|
||||||
|
android:pathData="M4,8C4,3.58 7.58,0 12,0H139C143.42,0 147,3.58 147,8V56C147,60.42 143.42,64 139,64H12C7.58,64 4,60.42 4,56V8Z"
|
||||||
|
android:fillColor="#4B70F5"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M34.46,40.23C33.51,40.23 32.61,40.16 31.74,40C30.88,39.84 30.1,39.62 29.39,39.32L29.77,36.47C30.63,36.84 31.47,37.12 32.3,37.32C33.13,37.5 33.91,37.6 34.64,37.6C35.58,37.6 36.29,37.43 36.77,37.11C37.25,36.77 37.49,36.28 37.49,35.64C37.49,34.8 37.02,34.17 36.06,33.73L33.47,32.5C32.31,31.95 31.41,31.28 30.78,30.47C30.15,29.67 29.84,28.74 29.84,27.7C29.84,26.24 30.3,25.12 31.22,24.32C32.15,23.52 33.46,23.13 35.16,23.13C36.22,23.13 37.2,23.3 38.09,23.65C38.99,24 39.81,24.52 40.55,25.22L38.62,27.36C38.02,26.82 37.43,26.43 36.86,26.16C36.3,25.89 35.73,25.75 35.17,25.75C34.44,25.75 33.87,25.92 33.46,26.25C33.05,26.58 32.85,27.06 32.85,27.7C32.85,28.13 32.99,28.52 33.26,28.86C33.54,29.18 33.95,29.48 34.47,29.75L36.8,30.91C38,31.5 38.91,32.19 39.54,32.97C40.18,33.74 40.5,34.64 40.5,35.65C40.5,37.14 39.99,38.28 38.96,39.06C37.93,39.84 36.43,40.23 34.46,40.23ZM51.58,40.23C49.54,40.23 48.07,39.82 47.16,38.99C46.25,38.16 45.8,36.84 45.8,35.02V33.2H48.68V34.79C48.68,35.75 48.93,36.45 49.41,36.88C49.9,37.32 50.7,37.54 51.81,37.54C52.18,37.54 52.57,37.52 53,37.48C53.42,37.43 53.9,37.38 54.44,37.3L54.72,39.95C54.2,40.05 53.68,40.12 53.17,40.16C52.67,40.21 52.14,40.23 51.58,40.23ZM45.8,34.02V23.83H48.68V34.02H45.8ZM42.77,30.14V27.58H54.25V30.14H42.77ZM65.55,40.12L65.32,36.44L65.16,35.05V32.31C65.16,31.59 64.88,31.05 64.3,30.72C63.73,30.38 62.88,30.19 61.75,30.16L58.66,30.07L58.89,27.46L61.52,27.5C63.69,27.53 65.3,27.97 66.36,28.83C67.41,29.68 67.94,30.93 67.94,32.57V37.4L69.81,37.66V40L65.55,40.12ZM61.27,40.23C59.96,40.23 58.94,39.91 58.21,39.27C57.49,38.63 57.13,37.73 57.13,36.55C57.13,35.2 57.62,34.15 58.59,33.43C59.55,32.7 60.93,32.34 62.7,32.34C63.4,32.34 64.02,32.38 64.55,32.45C65.08,32.53 65.59,32.65 66.07,32.82L65.56,34.97C65.05,34.86 64.58,34.79 64.14,34.77C63.7,34.75 63.25,34.74 62.77,34.74C60.93,34.74 60,35.28 60,36.37C60,36.84 60.16,37.21 60.48,37.46C60.81,37.71 61.29,37.83 61.9,37.83C62.62,37.83 63.22,37.7 63.71,37.45C64.19,37.18 64.55,36.85 64.8,36.45C65.04,36.04 65.16,35.63 65.16,35.2V34.19L65.72,37.38H64.73L65.14,37C65.12,37.72 64.95,38.32 64.63,38.8C64.32,39.28 63.88,39.64 63.31,39.88C62.74,40.12 62.06,40.23 61.27,40.23ZM76.57,34.66L75.85,30.53H76.88C77.01,28.41 78.1,27.34 80.15,27.34C81.37,27.34 82.28,27.75 82.89,28.56C83.5,29.38 83.8,30.6 83.8,32.24H80.93C80.93,31.44 80.8,30.86 80.52,30.51C80.26,30.16 79.82,29.98 79.22,29.98C78.33,29.98 77.67,30.39 77.23,31.2C76.79,32.01 76.57,33.16 76.57,34.66ZM71.01,40V37.55H79.54V40H71.01ZM73.69,40V27.58H76.11L76.57,31.14V40H73.69ZM71.48,30.03V27.58H75.94L76.18,30.03H71.48ZM93.77,40.23C91.73,40.23 90.25,39.82 89.35,38.99C88.44,38.16 87.99,36.84 87.99,35.02V33.2H90.87V34.79C90.87,35.75 91.11,36.45 91.6,36.88C92.09,37.32 92.89,37.54 94,37.54C94.37,37.54 94.76,37.52 95.18,37.48C95.61,37.43 96.09,37.38 96.63,37.3L96.91,39.95C96.38,40.05 95.87,40.12 95.36,40.16C94.86,40.21 94.33,40.23 93.77,40.23ZM87.99,34.02V23.83H90.87V34.02H87.99ZM84.95,30.14V27.58H96.44V30.14H84.95Z"
|
||||||
|
android:fillColor="#F5F5F5"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M110.33,26L119.67,32L110.33,38V26Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#F5F5F5"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/back.xml
Normal file
13
app/src/main/res/drawable/back.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="28dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="28">
|
||||||
|
<path
|
||||||
|
android:pathData="M14,26L2,14L14,2"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/branch_opened.xml
Normal file
13
app/src/main/res/drawable/branch_opened.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="10dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="10">
|
||||||
|
<path
|
||||||
|
android:pathData="M2,2L7.931,7.931L13.862,2"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2.43316"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#4B70F5"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/cpu.xml
Normal file
13
app/src/main/res/drawable/cpu.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:pathData="M18,2V8M30,2V8M18,40V46M30,40V46M40,18H46M40,28H46M2,18H8M2,28H8M12,8H36C38.209,8 40,9.791 40,12V36C40,38.209 38.209,40 36,40H12C9.791,40 8,38.209 8,36V12C8,9.791 9.791,8 12,8ZM18,18H30V30H18V18Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/editor.xml
Normal file
13
app/src/main/res/drawable/editor.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="39dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="39">
|
||||||
|
<path
|
||||||
|
android:pathData="M20,36.243H38M29,3.243C29.796,2.447 30.875,2 32,2C32.557,2 33.109,2.11 33.624,2.323C34.138,2.536 34.606,2.849 35,3.243C35.394,3.637 35.707,4.104 35.92,4.619C36.133,5.134 36.243,5.685 36.243,6.243C36.243,6.8 36.133,7.351 35.92,7.866C35.707,8.381 35.394,8.849 35,9.243L10,34.243L2,36.243L4,28.243L29,3.243Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/file.xml
Normal file
13
app/src/main/res/drawable/file.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="36dp"
|
||||||
|
android:height="44dp"
|
||||||
|
android:viewportWidth="36"
|
||||||
|
android:viewportHeight="44">
|
||||||
|
<path
|
||||||
|
android:pathData="M20,2H6C4.939,2 3.922,2.421 3.172,3.172C2.421,3.922 2,4.939 2,6V38C2,39.061 2.421,40.078 3.172,40.828C3.922,41.579 4.939,42 6,42H30C31.061,42 32.078,41.579 32.828,40.828C33.579,40.078 34,39.061 34,38V16M20,2L34,16M20,2V16H34"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/file_plus.xml
Normal file
13
app/src/main/res/drawable/file_plus.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="36dp"
|
||||||
|
android:height="44dp"
|
||||||
|
android:viewportWidth="36"
|
||||||
|
android:viewportHeight="44">
|
||||||
|
<path
|
||||||
|
android:pathData="M22,2H6C4.939,2 3.922,2.421 3.172,3.172C2.421,3.922 2,4.939 2,6V38C2,39.061 2.421,40.078 3.172,40.828C3.922,41.579 4.939,42 6,42H30C31.061,42 32.078,41.579 32.828,40.828C33.579,40.078 34,39.061 34,38V14M22,2L34,14M22,2V14H34M18,34V22M12,28H24"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/folder.xml
Normal file
13
app/src/main/res/drawable/folder.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="44dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="44"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:pathData="M42,34C42,35.061 41.579,36.078 40.828,36.828C40.078,37.579 39.061,38 38,38H6C4.939,38 3.922,37.579 3.172,36.828C2.421,36.078 2,35.061 2,34V6C2,4.939 2.421,3.922 3.172,3.172C3.922,2.421 4.939,2 6,2H16L20,8H38C39.061,8 40.078,8.421 40.828,9.172C41.579,9.922 42,10.939 42,12V34Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/folder_filled.xml
Normal file
9
app/src/main/res/drawable/folder_filled.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="29dp"
|
||||||
|
android:height="23dp"
|
||||||
|
android:viewportWidth="29"
|
||||||
|
android:viewportHeight="23">
|
||||||
|
<path
|
||||||
|
android:pathData="M3.602,22.236C2.847,22.236 2.2,21.967 1.663,21.429C1.125,20.891 0.856,20.245 0.856,19.489V3.012C0.856,2.257 1.125,1.61 1.663,1.072C2.2,0.535 2.847,0.266 3.602,0.266H11.841L14.587,3.012H25.572C26.327,3.012 26.974,3.281 27.512,3.819C28.05,4.356 28.319,5.003 28.319,5.758V19.489C28.319,20.245 28.05,20.891 27.512,21.429C26.974,21.967 26.327,22.236 25.572,22.236H3.602Z"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/folder_plus.xml
Normal file
13
app/src/main/res/drawable/folder_plus.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="44dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="44"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:pathData="M22,18V30M16,24H28M42,34C42,35.061 41.579,36.078 40.828,36.828C40.078,37.579 39.061,38 38,38H6C4.939,38 3.922,37.579 3.172,36.828C2.421,36.078 2,35.061 2,34V6C2,4.939 2.421,3.922 3.172,3.172C3.922,2.421 4.939,2 6,2H16L20,8H38C39.061,8 40.078,8.421 40.828,9.172C41.579,9.922 42,10.939 42,12V34Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/git_commit.xml
Normal file
13
app/src/main/res/drawable/git_commit.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="20">
|
||||||
|
<path
|
||||||
|
android:pathData="M2,10H13.9M33.92,10H45.82M31.9,10C31.9,14.418 28.318,18 23.9,18C19.482,18 15.9,14.418 15.9,10C15.9,5.582 19.482,2 23.9,2C28.318,2 31.9,5.582 31.9,10Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/git_icon.xml
Normal file
9
app/src/main/res/drawable/git_icon.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="47dp"
|
||||||
|
android:height="47dp"
|
||||||
|
android:viewportWidth="47"
|
||||||
|
android:viewportHeight="47">
|
||||||
|
<path
|
||||||
|
android:pathData="M23.5,0.313C22.665,0.313 21.85,0.617 21.223,1.244L16.564,5.954C16.312,6.09 16.099,6.284 15.943,6.523L1.244,21.223C-0.011,22.471 -0.011,24.522 1.244,25.777L21.223,45.756C22.478,47.005 24.522,47.005 25.777,45.756L45.756,25.777C47.011,24.529 47.011,22.478 45.756,21.223L25.777,1.244C25.15,0.617 24.334,0.313 23.5,0.313ZM23.5,3.677L43.323,23.5L23.5,43.323L3.677,23.5L17.392,9.784L20.291,12.683C20.213,12.961 20.187,13.258 20.187,13.563C20.187,14.785 20.854,15.833 21.844,16.409V30.591C20.854,31.167 20.187,32.215 20.187,33.438C20.187,35.268 21.669,36.75 23.5,36.75C25.331,36.75 26.812,35.268 26.812,33.438C26.812,32.215 26.146,31.167 25.156,30.591V17.6L30.229,22.672C30.157,22.937 30.125,23.215 30.125,23.5C30.125,25.331 31.607,26.813 33.437,26.813C35.268,26.813 36.75,25.331 36.75,23.5C36.75,21.669 35.268,20.188 33.437,20.188C33.153,20.188 32.875,20.22 32.609,20.291L26.709,14.391C26.78,14.125 26.812,13.847 26.812,13.563C26.812,11.732 25.331,10.25 23.5,10.25C23.196,10.25 22.898,10.276 22.62,10.354L19.722,7.455L23.5,3.677Z"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/git_pull.xml
Normal file
13
app/src/main/res/drawable/git_pull.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:pathData="M32,26C28.686,26 26,28.686 26,32C26,35.314 28.686,38 32,38C35.314,38 38,35.314 38,32C38,28.686 35.314,26 32,26ZM32,26V12C32,10.939 31.579,9.922 30.828,9.172C30.078,8.421 29.061,8 28,8H22M8,14C11.314,14 14,11.314 14,8C14,4.686 11.314,2 8,2C4.686,2 2,4.686 2,8C2,11.314 4.686,14 8,14ZM8,14V38"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/github.xml
Normal file
13
app/src/main/res/drawable/github.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="43dp"
|
||||||
|
android:height="47dp"
|
||||||
|
android:viewportWidth="43"
|
||||||
|
android:viewportHeight="47">
|
||||||
|
<path
|
||||||
|
android:pathData="M16,38.054C6,41.054 6,33.054 2,32.054M30,44.054V36.314C30.075,35.36 29.946,34.401 29.622,33.501C29.298,32.601 28.786,31.78 28.12,31.094C34.4,30.394 41,28.014 41,17.094C41,14.301 39.925,11.616 38,9.594C38.912,7.151 38.847,4.45 37.82,2.054C37.82,2.054 35.46,1.354 30,5.014C25.416,3.771 20.584,3.771 16,5.014C10.54,1.354 8.18,2.054 8.18,2.054C7.153,4.45 7.088,7.151 8,9.594C6.06,11.631 4.985,14.34 5,17.154C5,27.994 11.6,30.374 17.88,31.154C17.222,31.833 16.715,32.644 16.391,33.533C16.067,34.423 15.934,35.37 16,36.314V44.054"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/ic_arrow.xml
Normal file
13
app/src/main/res/drawable/ic_arrow.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="10dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="10"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:pathData="M2,13.862L7.931,7.931L2,2"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2.43316"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#4B70F5"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/ic_arrow_common.xml
Normal file
13
app/src/main/res/drawable/ic_arrow_common.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:pathData="M10,24H38M38,24L24,10M38,24L24,38"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_checkbox_no.xml
Normal file
9
app/src/main/res/drawable/ic_checkbox_no.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M7,13H17V11H7V13ZM5,21C4.45,21 3.979,20.804 3.588,20.413C3.196,20.021 3,19.55 3,19V5C3,4.45 3.196,3.979 3.588,3.588C3.979,3.196 4.45,3 5,3H19C19.55,3 20.021,3.196 20.413,3.588C20.804,3.979 21,4.45 21,5V19C21,19.55 20.804,20.021 20.413,20.413C20.021,20.804 19.55,21 19,21H5Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_checkbox_yes.xml
Normal file
9
app/src/main/res/drawable/ic_checkbox_yes.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M10.6,16.2L17.65,9.15L16.25,7.75L10.6,13.4L7.75,10.55L6.35,11.95L10.6,16.2ZM5,21C4.45,21 3.979,20.804 3.588,20.413C3.196,20.021 3,19.55 3,19V5C3,4.45 3.196,3.979 3.588,3.588C3.979,3.196 4.45,3 5,3H19C19.55,3 20.021,3.196 20.413,3.588C20.804,3.979 21,4.45 21,5V19C21,19.55 20.804,20.021 20.413,20.413C20.021,20.804 19.55,21 19,21H5Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
19
app/src/main/res/drawable/ic_file.xml
Normal file
19
app/src/main/res/drawable/ic_file.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="22dp"
|
||||||
|
android:viewportWidth="18"
|
||||||
|
android:viewportHeight="22">
|
||||||
|
<path
|
||||||
|
android:pathData="M9.875,2H3.75C3.286,2 2.841,2.184 2.513,2.513C2.184,2.841 2,3.286 2,3.75V17.75C2,18.214 2.184,18.659 2.513,18.987C2.841,19.316 3.286,19.5 3.75,19.5H14.25C14.714,19.5 15.159,19.316 15.487,18.987C15.816,18.659 16,18.214 16,17.75V8.125L9.875,2Z"
|
||||||
|
android:fillColor="#4B70F5"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M9.875,2V8.125H16"
|
||||||
|
android:fillColor="#4B70F5"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h18v22h-18zM9.875,2H3.75C3.286,2 2.841,2.184 2.513,2.513C2.184,2.841 2,3.286 2,3.75V17.75C2,18.214 2.184,18.659 2.513,18.987C2.841,19.316 3.286,19.5 3.75,19.5H14.25C14.714,19.5 15.159,19.316 15.487,18.987C15.816,18.659 16,18.214 16,17.75V8.125L9.875,2ZM9.875,2V8.125H16"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M9.875,2L11.112,0.763C10.784,0.434 10.339,0.25 9.875,0.25V2ZM3.75,2L3.75,0.25L3.75,2ZM2,3.75H0.25H2ZM2,17.75L0.25,17.75L2,17.75ZM16,8.125H17.75C17.75,7.661 17.566,7.216 17.237,6.888L16,8.125ZM9.875,8.125H8.125C8.125,9.092 8.908,9.875 9.875,9.875V8.125ZM9.875,0.25H3.75V3.75H9.875V0.25ZM3.75,0.25C2.822,0.25 1.931,0.619 1.275,1.275L3.75,3.75L3.75,3.75L3.75,0.25ZM1.275,1.275C0.619,1.931 0.25,2.822 0.25,3.75L3.75,3.75L3.75,3.75L1.275,1.275ZM0.25,3.75V17.75H3.75V3.75H0.25ZM0.25,17.75C0.25,18.678 0.619,19.569 1.275,20.225L3.75,17.75L3.75,17.75L0.25,17.75ZM1.275,20.225C1.931,20.881 2.822,21.25 3.75,21.25V17.75L3.75,17.75L1.275,20.225ZM3.75,21.25H14.25V17.75H3.75V21.25ZM14.25,21.25C15.178,21.25 16.069,20.881 16.725,20.225L14.25,17.75L14.25,17.75V21.25ZM16.725,20.225C17.381,19.569 17.75,18.678 17.75,17.75H14.25L14.25,17.75L16.725,20.225ZM17.75,17.75V8.125H14.25V17.75H17.75ZM17.237,6.888L11.112,0.763L8.638,3.237L14.763,9.362L17.237,6.888ZM8.125,2V8.125H11.625V2H8.125ZM9.875,9.875H16V6.375H9.875V9.875Z"
|
||||||
|
android:fillColor="#4B70F5"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_folder.xml
Normal file
9
app/src/main/res/drawable/ic_folder.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="19dp"
|
||||||
|
android:height="17dp"
|
||||||
|
android:viewportWidth="19"
|
||||||
|
android:viewportHeight="17">
|
||||||
|
<path
|
||||||
|
android:pathData="M1.87,16.542C1.356,16.542 0.916,16.343 0.549,15.945C0.183,15.548 0,15.07 0,14.512V2.329C0,1.771 0.183,1.293 0.549,0.895C0.916,0.498 1.356,0.299 1.87,0.299H7.48L9.35,2.329H16.831C17.345,2.329 17.785,2.528 18.152,2.926C18.518,3.323 18.701,3.801 18.701,4.36V14.512C18.701,15.07 18.518,15.548 18.152,15.945C17.785,16.343 17.345,16.542 16.831,16.542H1.87Z"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
</vector>
|
||||||
16
app/src/main/res/drawable/ic_git_clone_mini.xml
Normal file
16
app/src/main/res/drawable/ic_git_clone_mini.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="27dp"
|
||||||
|
android:height="28dp"
|
||||||
|
android:viewportWidth="27"
|
||||||
|
android:viewportHeight="28">
|
||||||
|
<path
|
||||||
|
android:pathData="M9.974,23.29C3.564,25.145 3.564,20.199 1,19.581ZM18.949,27V22.215C18.997,21.625 18.914,21.032 18.706,20.476C18.499,19.919 18.17,19.412 17.744,18.987C21.769,18.555 26,17.083 26,10.332C26,8.605 25.311,6.945 24.077,5.695C24.661,4.184 24.62,2.515 23.962,1.033C23.962,1.033 22.449,0.6 18.949,2.863C16.01,2.095 12.913,2.095 9.974,2.863C6.474,0.6 4.962,1.033 4.962,1.033C4.303,2.515 4.262,4.184 4.846,5.695C3.603,6.954 2.913,8.63 2.923,10.369C2.923,17.071 7.154,18.542 11.179,19.024C10.758,19.445 10.432,19.946 10.225,20.496C10.017,21.045 9.932,21.631 9.974,22.215V27"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M9.974,23.29C3.564,25.145 3.564,20.199 1,19.581M18.949,27V22.215C18.997,21.625 18.914,21.032 18.706,20.476C18.499,19.919 18.17,19.412 17.744,18.987C21.769,18.555 26,17.083 26,10.332C26,8.605 25.311,6.945 24.077,5.695C24.661,4.184 24.62,2.515 23.962,1.033C23.962,1.033 22.449,0.6 18.949,2.863C16.01,2.095 12.913,2.095 9.974,2.863C6.474,0.6 4.962,1.033 4.962,1.033C4.303,2.515 4.262,4.184 4.846,5.695C3.603,6.954 2.913,8.63 2.923,10.369C2.923,17.071 7.154,18.542 11.179,19.024C10.758,19.445 10.432,19.946 10.225,20.496C10.017,21.045 9.932,21.631 9.974,22.215V27"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/ic_paste.xml
Normal file
13
app/src/main/res/drawable/ic_paste.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:pathData="M28,4H12C10.939,4 9.922,4.421 9.172,5.172C8.421,5.922 8,6.939 8,8V40C8,41.061 8.421,42.078 9.172,42.828C9.922,43.579 10.939,44 12,44H36C37.061,44 38.078,43.579 38.828,42.828C39.579,42.078 40,41.061 40,40V16M28,4L40,16M28,4V16H40M32,26H16M32,34H16M20,18H16"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
19
app/src/main/res/drawable/ic_project_create_mini.xml
Normal file
19
app/src/main/res/drawable/ic_project_create_mini.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="20"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M11,2H4C3.47,2 2.961,2.211 2.586,2.586C2.211,2.961 2,3.47 2,4V20C2,20.53 2.211,21.039 2.586,21.414C2.961,21.789 3.47,22 4,22H16C16.53,22 17.039,21.789 17.414,21.414C17.789,21.039 18,20.53 18,20V9L11,2Z"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11,2V9H18"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h20v24h-20zM11,2H4C3.47,2 2.961,2.211 2.586,2.586C2.211,2.961 2,3.47 2,4V20C2,20.53 2.211,21.039 2.586,21.414C2.961,21.789 3.47,22 4,22H16C16.53,22 17.039,21.789 17.414,21.414C17.789,21.039 18,20.53 18,20V9L11,2ZM11,2V9H18"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11,2L12.237,0.763C11.909,0.434 11.464,0.25 11,0.25V2ZM4,2L4,0.25L4,2ZM2,4H0.25H2ZM2,20L0.25,20L2,20ZM18,9H19.75C19.75,8.536 19.566,8.091 19.237,7.763L18,9ZM11,9H9.25C9.25,9.967 10.033,10.75 11,10.75V9ZM11,2V0.25H4V2V3.75H11V2ZM4,2L4,0.25C3.005,0.25 2.052,0.645 1.348,1.348L2.586,2.586L3.823,3.823C3.87,3.776 3.934,3.75 4,3.75L4,2ZM2.586,2.586L1.348,1.348C0.645,2.052 0.25,3.005 0.25,4L2,4L3.75,4C3.75,3.934 3.776,3.87 3.823,3.823L2.586,2.586ZM2,4H0.25V20H2H3.75V4H2ZM2,20L0.25,20C0.25,20.995 0.645,21.948 1.348,22.652L2.586,21.414L3.823,20.177C3.776,20.13 3.75,20.066 3.75,20L2,20ZM2.586,21.414L1.348,22.652C2.052,23.355 3.005,23.75 4,23.75V22V20.25C3.934,20.25 3.87,20.224 3.823,20.177L2.586,21.414ZM4,22V23.75H16V22V20.25H4V22ZM16,22V23.75C16.995,23.75 17.948,23.355 18.652,22.652L17.414,21.414L16.177,20.177C16.13,20.224 16.066,20.25 16,20.25V22ZM17.414,21.414L18.652,22.652C19.355,21.948 19.75,20.995 19.75,20H18H16.25C16.25,20.066 16.224,20.13 16.177,20.177L17.414,21.414ZM18,20H19.75V9H18H16.25V20H18ZM18,9L19.237,7.763L12.237,0.763L11,2L9.763,3.237L16.763,10.237L18,9ZM11,2H9.25V9H11H12.75V2H11ZM11,9V10.75H18V9V7.25H11V9Z"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/ic_recreate.xml
Normal file
13
app/src/main/res/drawable/ic_recreate.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="33dp"
|
||||||
|
android:height="29dp"
|
||||||
|
android:viewportWidth="33"
|
||||||
|
android:viewportHeight="29">
|
||||||
|
<path
|
||||||
|
android:pathData="M31.602,3.265V11.504M31.602,11.504H23.363M31.602,11.504L25.23,5.517C23.754,4.041 21.929,2.962 19.923,2.382C17.918,1.802 15.798,1.74 13.762,2.2C11.726,2.661 9.84,3.63 8.28,5.017C6.719,6.404 5.536,8.163 4.839,10.131M1.393,25.235V16.997M1.393,16.997H9.632M1.393,16.997L7.764,22.983C9.24,24.46 11.066,25.539 13.071,26.119C15.076,26.699 17.196,26.761 19.232,26.301C21.268,25.84 23.154,24.871 24.715,23.484C26.275,22.097 27.459,20.338 28.155,18.37"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="3.38"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
17
app/src/main/res/drawable/ic_reset.xml
Normal file
17
app/src/main/res/drawable/ic_reset.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h48v48h-48z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M2,8V20M2,20H14M2,20L11.28,11.28C14.042,8.523 17.625,6.738 21.489,6.194C25.354,5.65 29.29,6.377 32.706,8.265C36.121,10.153 38.83,13.1 40.425,16.662C42.02,20.224 42.414,24.207 41.548,28.013C40.682,31.818 38.603,35.239 35.624,37.759C32.645,40.28 28.927,41.765 25.031,41.989C21.135,42.213 17.272,41.165 14.023,39.003C10.775,36.84 8.317,33.681 7.02,30"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="6"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/ic_run_mini.xml
Normal file
13
app/src/main/res/drawable/ic_run_mini.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="12dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="12"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:pathData="M1.334,2L10.667,8L1.334,14V2Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#F5F5F5"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
12
app/src/main/res/drawable/ic_star.xml
Normal file
12
app/src/main/res/drawable/ic_star.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="52dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="52"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:pathData="M27.152,2.727L33.286,16.525L48.853,17.863C49.098,17.882 49.333,17.971 49.526,18.119C49.72,18.266 49.863,18.465 49.939,18.691C50.015,18.917 50.02,19.16 49.953,19.389C49.886,19.617 49.751,19.821 49.563,19.976L37.78,29.864L41.255,44.526C41.291,44.68 41.295,44.839 41.267,44.995C41.239,45.151 41.18,45.3 41.092,45.433C41.004,45.567 40.89,45.682 40.756,45.773C40.621,45.863 40.47,45.928 40.31,45.962C39.979,46.029 39.633,45.967 39.349,45.789L25.977,38.104L12.569,45.827C12.429,45.909 12.274,45.963 12.112,45.986C11.95,46.01 11.784,46.003 11.625,45.965C11.466,45.926 11.317,45.859 11.185,45.764C11.054,45.671 10.943,45.552 10.859,45.417C10.775,45.284 10.719,45.137 10.694,44.983C10.669,44.83 10.676,44.673 10.715,44.522L14.193,29.86L2.425,19.976C2.178,19.765 2.027,19.468 2.003,19.151C1.98,18.833 2.087,18.519 2.301,18.277C2.536,18.044 2.857,17.909 3.195,17.901L18.703,16.562L24.837,2.727C24.936,2.511 25.098,2.327 25.303,2.198C25.509,2.069 25.749,2 25.994,2C26.24,2 26.48,2.069 26.685,2.198C26.891,2.327 27.053,2.511 27.152,2.727Z"
|
||||||
|
android:strokeLineJoin="bevel"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#FFD401"
|
||||||
|
android:strokeColor="#FFD401"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/icon.xml
Normal file
13
app/src/main/res/drawable/icon.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="44dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="44">
|
||||||
|
<path
|
||||||
|
android:pathData="M2,10H6M6,10H38M6,10V38C6,39.061 6.421,40.078 7.172,40.828C7.922,41.579 8.939,42 10,42H30C31.061,42 32.078,41.579 32.828,40.828C33.579,40.078 34,39.061 34,38V10M12,10V6C12,4.939 12.421,3.922 13.172,3.172C13.922,2.421 14.939,2 16,2H24C25.061,2 26.078,2.421 26.828,3.172C27.579,3.922 28,4.939 28,6V10"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
20
app/src/main/res/drawable/img_icon_back.xml
Normal file
20
app/src/main/res/drawable/img_icon_back.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="56dp"
|
||||||
|
android:height="56dp"
|
||||||
|
android:viewportWidth="56"
|
||||||
|
android:viewportHeight="56">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.2,0.788L44.155,0.788A10.985,10.985 0,0 1,55.14 11.773L55.14,44.728A10.985,10.985 0,0 1,44.155 55.713L11.2,55.713A10.985,10.985 0,0 1,0.215 44.728L0.215,11.773A10.985,10.985 0,0 1,11.2 0.788z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.2,0.788L44.155,0.788A10.985,10.985 0,0 1,55.14 11.773L55.14,44.728A10.985,10.985 0,0 1,44.155 55.713L11.2,55.713A10.985,10.985 0,0 1,0.215 44.728L0.215,11.773A10.985,10.985 0,0 1,11.2 0.788z"
|
||||||
|
android:fillColor="#2D246D"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M31.797,36.489L23.558,28.25L31.797,20.012"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="3.38"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
16
app/src/main/res/drawable/img_icon_folder.xml
Normal file
16
app/src/main/res/drawable/img_icon_folder.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="56dp"
|
||||||
|
android:height="56dp"
|
||||||
|
android:viewportWidth="56"
|
||||||
|
android:viewportHeight="56">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.11,0.788L44.065,0.788A10.985,10.985 0,0 1,55.05 11.773L55.05,44.728A10.985,10.985 0,0 1,44.065 55.713L11.11,55.713A10.985,10.985 0,0 1,0.125 44.728L0.125,11.773A10.985,10.985 0,0 1,11.11 0.788z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.11,0.788L44.065,0.788A10.985,10.985 0,0 1,55.05 11.773L55.05,44.728A10.985,10.985 0,0 1,44.065 55.713L11.11,55.713A10.985,10.985 0,0 1,0.125 44.728L0.125,11.773A10.985,10.985 0,0 1,11.11 0.788z"
|
||||||
|
android:fillColor="#2D246D"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M16.602,39.236C15.847,39.236 15.2,38.967 14.663,38.429C14.125,37.891 13.856,37.245 13.856,36.489V20.012C13.856,19.257 14.125,18.61 14.663,18.072C15.2,17.535 15.847,17.266 16.602,17.266H24.841L27.587,20.012H38.572C39.327,20.012 39.974,20.281 40.512,20.819C41.05,21.356 41.319,22.003 41.319,22.758V36.489C41.319,37.245 41.05,37.891 40.512,38.429C39.974,38.967 39.327,39.236 38.572,39.236H16.602Z"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
24
app/src/main/res/drawable/img_icon_git.xml
Normal file
24
app/src/main/res/drawable/img_icon_git.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="56dp"
|
||||||
|
android:height="56dp"
|
||||||
|
android:viewportWidth="56"
|
||||||
|
android:viewportHeight="56">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.93,0.788L44.887,0.788A10.986,10.986 0,0 1,55.872 11.774L55.872,44.73A10.986,10.986 0,0 1,44.887 55.715L11.93,55.715A10.986,10.986 0,0 1,0.945 44.73L0.945,11.774A10.986,10.986 0,0 1,11.93 0.788z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.93,0.788L44.887,0.788A10.986,10.986 0,0 1,55.872 11.774L55.872,44.73A10.986,10.986 0,0 1,44.887 55.715L11.93,55.715A10.986,10.986 0,0 1,0.945 44.73L0.945,11.774A10.986,10.986 0,0 1,11.93 0.788z"
|
||||||
|
android:fillColor="#2D246D"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.93,11.773h32.956v32.956h-32.956z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M24.289,37.864C17.423,39.924 17.423,34.431 14.677,33.744M33.901,41.984V36.669C33.953,36.014 33.864,35.356 33.642,34.738C33.419,34.12 33.068,33.557 32.611,33.085C36.922,32.605 41.454,30.971 41.454,23.473C41.453,21.556 40.716,19.712 39.394,18.324C40.02,16.646 39.976,14.792 39.271,13.147C39.271,13.147 37.65,12.666 33.901,15.179C30.754,14.326 27.436,14.326 24.289,15.179C20.54,12.666 18.92,13.147 18.92,13.147C18.215,14.792 18.17,16.646 18.796,18.324C17.465,19.722 16.726,21.583 16.736,23.514C16.736,30.957 21.268,32.591 25.58,33.126C25.128,33.593 24.78,34.15 24.557,34.761C24.335,35.371 24.243,36.021 24.289,36.669V41.984"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="3.38015"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
16
app/src/main/res/drawable/img_icon_options.xml
Normal file
16
app/src/main/res/drawable/img_icon_options.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="56dp"
|
||||||
|
android:height="56dp"
|
||||||
|
android:viewportWidth="56"
|
||||||
|
android:viewportHeight="56">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.843,0.788L44.8,0.788A10.986,10.986 0,0 1,55.785 11.774L55.785,44.73A10.986,10.986 0,0 1,44.8 55.715L11.843,55.715A10.986,10.986 0,0 1,0.858 44.73L0.858,11.774A10.986,10.986 0,0 1,11.843 0.788z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.843,0.788L44.8,0.788A10.986,10.986 0,0 1,55.785 11.774L55.785,44.73A10.986,10.986 0,0 1,44.8 55.715L11.843,55.715A10.986,10.986 0,0 1,0.858 44.73L0.858,11.774A10.986,10.986 0,0 1,11.843 0.788z"
|
||||||
|
android:fillColor="#2D246D"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M28.322,39.237C27.566,39.237 26.92,38.968 26.382,38.43C25.844,37.893 25.575,37.246 25.575,36.491C25.575,35.735 25.844,35.089 26.382,34.551C26.92,34.013 27.566,33.744 28.322,33.744C29.077,33.744 29.723,34.013 30.261,34.551C30.799,35.089 31.068,35.735 31.068,36.491C31.068,37.246 30.799,37.893 30.261,38.43C29.723,38.968 29.077,39.237 28.322,39.237ZM28.322,30.998C27.566,30.998 26.92,30.729 26.382,30.191C25.844,29.653 25.575,29.007 25.575,28.252C25.575,27.496 25.844,26.85 26.382,26.312C26.92,25.774 27.566,25.505 28.322,25.505C29.077,25.505 29.723,25.774 30.261,26.312C30.799,26.85 31.068,27.496 31.068,28.252C31.068,29.007 30.799,29.653 30.261,30.191C29.723,30.729 29.077,30.998 28.322,30.998ZM28.322,22.759C27.566,22.759 26.92,22.49 26.382,21.952C25.844,21.414 25.575,20.768 25.575,20.013C25.575,19.257 25.844,18.611 26.382,18.073C26.92,17.535 27.566,17.266 28.322,17.266C29.077,17.266 29.723,17.535 30.261,18.073C30.799,18.611 31.068,19.257 31.068,20.013C31.068,20.768 30.799,21.414 30.261,21.952C29.723,22.49 29.077,22.759 28.322,22.759Z"
|
||||||
|
android:fillColor="#3DC2EC"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
20
app/src/main/res/drawable/img_icon_run.xml
Normal file
20
app/src/main/res/drawable/img_icon_run.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="55dp"
|
||||||
|
android:height="56dp"
|
||||||
|
android:viewportWidth="55"
|
||||||
|
android:viewportHeight="56">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.02,0.788L43.975,0.788A10.985,10.985 0,0 1,54.96 11.773L54.96,44.728A10.985,10.985 0,0 1,43.975 55.713L11.02,55.713A10.985,10.985 0,0 1,0.035 44.728L0.035,11.773A10.985,10.985 0,0 1,11.02 0.788z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.02,0.788L43.975,0.788A10.985,10.985 0,0 1,54.96 11.773L54.96,44.728A10.985,10.985 0,0 1,43.975 55.713L11.02,55.713A10.985,10.985 0,0 1,0.035 44.728L0.035,11.773A10.985,10.985 0,0 1,11.02 0.788z"
|
||||||
|
android:fillColor="#2D246D"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M17.885,15.892L37.109,28.25L17.885,40.608V15.892Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="3.38"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
20
app/src/main/res/drawable/img_icon_save.xml
Normal file
20
app/src/main/res/drawable/img_icon_save.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="56dp"
|
||||||
|
android:height="56dp"
|
||||||
|
android:viewportWidth="56"
|
||||||
|
android:viewportHeight="56">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M11.843,0.788L44.8,0.788A10.986,10.986 0,0 1,55.785 11.774L55.785,44.73A10.986,10.986 0,0 1,44.8 55.715L11.843,55.715A10.986,10.986 0,0 1,0.858 44.73L0.858,11.774A10.986,10.986 0,0 1,11.843 0.788z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.843,0.788L44.8,0.788A10.986,10.986 0,0 1,55.785 11.774L55.785,44.73A10.986,10.986 0,0 1,44.8 55.715L11.843,55.715A10.986,10.986 0,0 1,0.858 44.73L0.858,11.774A10.986,10.986 0,0 1,11.843 0.788z"
|
||||||
|
android:fillColor="#2D246D"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M35.188,40.61V29.625H21.456V40.61M21.456,15.893V22.759H32.441M37.934,40.61H18.709C17.981,40.61 17.282,40.321 16.767,39.806C16.252,39.291 15.963,38.592 15.963,37.864V18.639C15.963,17.911 16.252,17.212 16.767,16.698C17.282,16.182 17.981,15.893 18.709,15.893H33.814L40.68,22.759V37.864C40.68,38.592 40.391,39.291 39.876,39.806C39.361,40.321 38.662,40.61 37.934,40.61Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="3.38015"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/play_icon.xml
Normal file
13
app/src/main/res/drawable/play_icon.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="23dp"
|
||||||
|
android:height="28dp"
|
||||||
|
android:viewportWidth="23"
|
||||||
|
android:viewportHeight="28">
|
||||||
|
<path
|
||||||
|
android:pathData="M3.042,3.5L19.958,14L3.042,24.5V3.5Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="6"
|
||||||
|
android:fillColor="#3DC2EC"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/search.xml
Normal file
13
app/src/main/res/drawable/search.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:pathData="M38,38L29.3,29.3M34,18C34,26.837 26.837,34 18,34C9.163,34 2,26.837 2,18C2,9.163 9.163,2 18,2C26.837,2 34,9.163 34,18Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/search_1.xml
Normal file
13
app/src/main/res/drawable/search_1.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:pathData="M38,38L29.3,29.3M34,18C34,26.837 26.837,34 18,34C9.163,34 2,26.837 2,18C2,9.163 9.163,2 18,2C26.837,2 34,9.163 34,18Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/settings_icon.xml
Normal file
13
app/src/main/res/drawable/settings_icon.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="42dp"
|
||||||
|
android:height="42dp"
|
||||||
|
android:viewportWidth="42"
|
||||||
|
android:viewportHeight="42">
|
||||||
|
<path
|
||||||
|
android:pathData="M25.403,10.607C25.036,10.981 24.831,11.484 24.831,12.007C24.831,12.531 25.036,13.033 25.403,13.407L28.603,16.607C28.976,16.974 29.479,17.179 30.003,17.179C30.526,17.179 31.029,16.974 31.403,16.607L38.943,9.067C39.948,11.29 40.253,13.766 39.816,16.166C39.378,18.565 38.22,20.775 36.495,22.5C34.77,24.225 32.561,25.383 30.161,25.82C27.761,26.257 25.285,25.953 23.063,24.947L9.243,38.767C8.447,39.563 7.368,40.01 6.243,40.01C5.117,40.01 4.038,39.563 3.243,38.767C2.447,37.972 2,36.893 2,35.767C2,34.642 2.447,33.563 3.243,32.767L17.063,18.947C16.057,16.725 15.752,14.249 16.19,11.849C16.627,9.449 17.785,7.24 19.51,5.515C21.235,3.79 23.444,2.632 25.844,2.194C28.244,1.757 30.72,2.062 32.943,3.067L25.403,10.607Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/settings_menu.xml
Normal file
13
app/src/main/res/drawable/settings_menu.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="31dp"
|
||||||
|
android:height="31dp"
|
||||||
|
android:viewportWidth="31"
|
||||||
|
android:viewportHeight="31">
|
||||||
|
<path
|
||||||
|
android:pathData="M18.067,7.909C17.816,8.166 17.675,8.511 17.675,8.871C17.675,9.23 17.816,9.575 18.067,9.832L20.264,12.029C20.521,12.28 20.866,12.421 21.226,12.421C21.585,12.421 21.93,12.28 22.187,12.029L27.363,6.852C28.054,8.378 28.263,10.078 27.963,11.726C27.663,13.373 26.867,14.89 25.683,16.074C24.499,17.259 22.982,18.054 21.334,18.354C19.687,18.654 17.987,18.445 16.461,17.755L6.973,27.243C6.426,27.789 5.685,28.096 4.913,28.096C4.14,28.096 3.399,27.789 2.853,27.243C2.307,26.697 2,25.956 2,25.183C2,24.411 2.307,23.67 2.853,23.124L12.341,13.635C11.651,12.11 11.442,10.41 11.742,8.762C12.042,7.114 12.837,5.597 14.022,4.413C15.206,3.229 16.723,2.434 18.371,2.133C20.018,1.833 21.718,2.042 23.244,2.733L18.067,7.909Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#ffffff"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/trash.xml
Normal file
13
app/src/main/res/drawable/trash.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="44dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="44">
|
||||||
|
<path
|
||||||
|
android:pathData="M2,10H6M6,10H38M6,10V38C6,39.061 6.421,40.078 7.172,40.828C7.922,41.579 8.939,42 10,42H30C31.061,42 32.078,41.579 32.828,40.828C33.579,40.078 34,39.061 34,38V10M12,10V6C12,4.939 12.421,3.922 13.172,3.172C13.922,2.421 14.939,2 16,2H24C25.061,2 26.078,2.421 26.828,3.172C27.579,3.922 28,4.939 28,6V10"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/tutorials.xml
Normal file
13
app/src/main/res/drawable/tutorials.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="32"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:pathData="M8.42,27.78L6,46L16,40L26,46L23.58,27.76M30,16C30,23.732 23.732,30 16,30C8.268,30 2,23.732 2,16C2,8.268 8.268,2 16,2C23.732,2 30,8.268 30,16Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/tutorials_menu.xml
Normal file
13
app/src/main/res/drawable/tutorials_menu.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="35dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="35">
|
||||||
|
<path
|
||||||
|
android:pathData="M7.156,19.7L5.494,32.209L12.36,28.089L19.226,32.209L17.564,19.686M21.972,11.612C21.972,16.92 17.668,21.224 12.36,21.224C7.051,21.224 2.748,16.92 2.748,11.612C2.748,6.303 7.051,2 12.36,2C17.668,2 21.972,6.303 21.972,11.612Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#ffffff"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/undo.xml
Normal file
13
app/src/main/res/drawable/undo.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="36dp"
|
||||||
|
android:height="36dp"
|
||||||
|
android:viewportWidth="36"
|
||||||
|
android:viewportHeight="36">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,22L2,12M2,12L12,2M2,12L26,12C28.122,12 30.157,12.843 31.657,14.343C33.157,15.843 34,17.878 34,20L34,34"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="4"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#3DC2EC"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/x_icon.xml
Normal file
13
app/src/main/res/drawable/x_icon.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="30dp"
|
||||||
|
android:height="30dp"
|
||||||
|
android:viewportWidth="30"
|
||||||
|
android:viewportHeight="30">
|
||||||
|
<path
|
||||||
|
android:pathData="M27,3L3,27M3,3L27,27"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="6"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#CC425E"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
@@ -1,7 +1,21 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Work</string>
|
<string name="app_name">Work</string>
|
||||||
<string name="title_activity_root">RootActivity</string>
|
<string name="title_activity_root">RootActivity</string>
|
||||||
<string name="auth_title">Привет! Введи код для авторизации</string>
|
<string name="auth_title">Введите код для авторизации</string>
|
||||||
<string name="auth_label">Код</string>
|
<string name="auth_label">Код</string>
|
||||||
<string name="auth_sign_in">Войти</string>
|
<string name="auth_sign_in">Войти</string>
|
||||||
|
<string name="main_update">Обновить</string>
|
||||||
|
<string name="main_log_out">Выйти</string>
|
||||||
|
<string name="main_avatar_description">Фото пользователя</string>
|
||||||
|
<string name="main_booking_title">Ваши забронированные места</string>
|
||||||
|
<string name="booking_button">Бронировать</string>
|
||||||
|
<string name="add_icon_description">Иконка добавления</string>
|
||||||
|
<string name="data_error_message">Ошибка загрузки данных</string>
|
||||||
|
<string name="main_empty_booking">Нет бронирований</string>
|
||||||
|
<string name="book_new_book">Новая встреча</string>
|
||||||
|
<string name="book_back">Назад</string>
|
||||||
|
<string name="book_available_date">Доступные даты</string>
|
||||||
|
<string name="book_choose_place">Выберите место встречи</string>
|
||||||
|
<string name="book_all_booked">Всё забронировано</string>
|
||||||
|
<string name="book_error">Ошибка сервера</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user