Course work: implement login

This commit is contained in:
abazov73 2023-12-26 23:33:11 +04:00
parent d79f6982fe
commit a687ff002f
12 changed files with 236 additions and 19 deletions

View File

@ -94,4 +94,7 @@ dependencies {
androidTestImplementation("androidx.compose.ui:ui-test-junit4") androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest") debugImplementation("androidx.compose.ui:ui-test-manifest")
implementation("javax.inject:javax.inject:1")
implementation("androidx.compose.runtime:runtime-livedata")
} }

View File

@ -1,8 +1,11 @@
package com.example.mobile_labs.api package com.example.mobile_labs.api
import android.util.Log
import com.example.mobile_labs.api.models.Credentials
import com.example.mobile_labs.api.models.EventRemote import com.example.mobile_labs.api.models.EventRemote
import com.example.mobile_labs.api.models.PerformanceRemote import com.example.mobile_labs.api.models.PerformanceRemote
import com.example.mobile_labs.api.models.PersonRemote import com.example.mobile_labs.api.models.PersonRemote
import com.example.mobile_labs.api.models.Token
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
@ -52,6 +55,11 @@ interface MyServerService {
@Path("id") id: Int, @Path("id") id: Int,
): EventRemote ): EventRemote
@POST("api/login")
suspend fun login(
@Body credentials: Credentials
): Token
companion object { companion object {
private const val BASE_URL = "http://10.0.2.2:8000/" private const val BASE_URL = "http://10.0.2.2:8000/"
@ -96,6 +104,10 @@ interface MyServerService {
} }
} }
fun getToken(): String {
return _token
}
fun setToken(token: String) { fun setToken(token: String) {
_token = token _token = token
} }

View File

@ -4,6 +4,6 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class Credentials( data class Credentials(
val login: String = "", val email: String = "",
val password: String = "", val password: String = "",
) )

View File

@ -0,0 +1,8 @@
package com.example.mobile_labs.api.models
import kotlinx.serialization.Serializable
@Serializable
data class Token(
val token: String
)

View File

@ -72,8 +72,8 @@ class PeopleRemoteMediator(
) )
} }
// dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.PERSON) dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.PERSON)
// dbPersonRepository.clearPeople() dbPersonRepository.clearPeople()
dbRemoteKeyRepository.createRemoteKeys(keys) dbRemoteKeyRepository.createRemoteKeys(keys)
dbPersonRepository.insertPeople(people) dbPersonRepository.insertPeople(people)
} }

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.mobile_labs.TheatreApplication import com.example.mobile_labs.TheatreApplication
import com.example.mobile_labs.ui.event.list.EventListViewModel import com.example.mobile_labs.ui.event.list.EventListViewModel
import com.example.mobile_labs.ui.login.LoginViewModel
import com.example.mobile_labs.ui.performance.list.PerformanceListViewModel import com.example.mobile_labs.ui.performance.list.PerformanceListViewModel
import com.example.mobile_labs.ui.performance.view.PerformanceViewModel import com.example.mobile_labs.ui.performance.view.PerformanceViewModel
import com.example.mobile_labs.ui.person.list.PeopleListViewModel import com.example.mobile_labs.ui.person.list.PeopleListViewModel
@ -25,6 +26,9 @@ object AppViewModelProvider {
initializer { initializer {
PerformanceViewModel(this.createSavedStateHandle(), theatreApplication().container.performanceRestRepository) PerformanceViewModel(this.createSavedStateHandle(), theatreApplication().container.performanceRestRepository)
} }
initializer {
LoginViewModel()
}
} }
} }

View File

@ -0,0 +1,116 @@
package com.example.mobile_labs.ui.login
import androidx.compose.foundation.layout.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.*
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.mobile_labs.R
import com.example.mobile_labs.common.AppViewModelProvider
import com.example.mobile_labs.ui.navigation.Screen
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun Login(
navController: NavHostController,
viewModel: LoginViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val success = viewModel.successState.observeAsState().value
val login = remember { mutableStateOf(TextFieldValue(""))}
val password = remember { mutableStateOf(TextFieldValue(""))}
if (success == false) {
AlertDialog(
title = {
Text(
text = "Ошибка авторизации",
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
},
text = {
Text(
text = "Неверный логин или пароль",
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
},
onDismissRequest = {
viewModel.calmSuccessState()
},
confirmButton = {
TextButton(
onClick = {
viewModel.calmSuccessState()
}
) {
Text("OK")
}
}
)
}
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize()
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth(0.7f).fillMaxHeight().padding(vertical=100.dp)
) {
Text(
text = "Вход",
style = TextStyle(
fontSize = 40.sp,
fontWeight = FontWeight(400),
),
modifier = Modifier.fillMaxHeight(0.3f)
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth().padding(bottom=30.dp),
value = login.value,
onValueChange = { login.value = it },
label = { Text(stringResource(id = R.string.login)) },
singleLine = true
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth().padding(bottom=30.dp),
value = password.value,
onValueChange = { password.value = it },
label = { Text(stringResource(id = R.string.password)) },
singleLine = true
)
Button(
{
viewModel.login(login.value.text, password.value.text, navController)
},
modifier = Modifier.padding(bottom=20.dp).fillMaxWidth(0.5f)
) {
Text(
text = "Войти",
style = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight(400),
color = Color(0xFFFFFFFF),
)
)
}
}
}
}

View File

@ -0,0 +1,49 @@
package com.example.mobile_labs.ui.login
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.example.mobile_labs.api.MyServerService
import com.example.mobile_labs.api.models.Credentials
import com.example.mobile_labs.ui.navigation.Screen
import kotlinx.coroutines.launch
import javax.inject.Inject
class LoginViewModel : ViewModel() {
private val _successState = MutableLiveData<Boolean?>()
val successState: LiveData<Boolean?>
get() = _successState
fun calmSuccessState() {
_successState.postValue(null)
}
fun login(login: String, password: String, navController: NavHostController) {
viewModelScope.launch {
val token = MyServerService.getInstance().login(Credentials(login, password))
if (token.token.isEmpty()) {
_successState.postValue(false)
return@launch
}
MyServerService.setToken(token.token)
_successState.setValue(true)
navigate(navController)
}
}
fun navigate(navController: NavHostController) {
Screen.About.route.let {
navController.navigate(it) {
popUpTo(it) {
inclusive = true
}
}
}
}
}

View File

@ -33,8 +33,10 @@ import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.example.mobile_labs.ui.theme.Mobile_LabsTheme import com.example.mobile_labs.ui.theme.Mobile_LabsTheme
import com.example.mobile_labs.R import com.example.mobile_labs.R
import com.example.mobile_labs.api.MyServerService
import com.example.mobile_labs.ui.about.About import com.example.mobile_labs.ui.about.About
import com.example.mobile_labs.ui.event.list.EventList import com.example.mobile_labs.ui.event.list.EventList
import com.example.mobile_labs.ui.login.Login
import com.example.mobile_labs.ui.performance.list.PerformanceList import com.example.mobile_labs.ui.performance.list.PerformanceList
import com.example.mobile_labs.ui.performance.view.PerformanceView import com.example.mobile_labs.ui.performance.view.PerformanceView
import com.example.mobile_labs.ui.person.list.PeopleList import com.example.mobile_labs.ui.person.list.PeopleList
@ -78,6 +80,7 @@ fun Navbar(
) { ) {
NavigationBar(modifier) { NavigationBar(modifier) {
Screen.bottomBarItems.forEach { screen -> Screen.bottomBarItems.forEach { screen ->
if (MyServerService.getToken().isBlank() || screen.route !== "login") {
NavigationBarItem( NavigationBarItem(
icon = { Icon(screen.icon, contentDescription = null) }, icon = { Icon(screen.icon, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) }, label = { Text(stringResource(screen.resourceId)) },
@ -85,15 +88,16 @@ fun Navbar(
onClick = { onClick = {
navController.navigate(screen.route) { navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) { popUpTo(navController.graph.findStartDestination().id) {
saveState = true saveState = false
} }
launchSingleTop = true launchSingleTop = true
restoreState = true restoreState = false
} }
} }
) )
} }
} }
}
} }
@Composable @Composable
@ -108,9 +112,14 @@ fun Navhost(
modifier.padding(innerPadding) modifier.padding(innerPadding)
) { ) {
composable(Screen.Schedule.route) { EventList(navController) } composable(Screen.Schedule.route) { EventList(navController) }
composable(Screen.Repertoire.route) { PerformanceList(navController) } composable(Screen.Repertoire.route) {
PerformanceList(navController)
}
composable(Screen.PeopleList.route) { PeopleList() } composable(Screen.PeopleList.route) { PeopleList() }
composable(Screen.About.route) { About() } composable(Screen.About.route) { About() }
if (MyServerService.getToken().isBlank()) {
composable(Screen.Login.route) { Login(navController) }
}
composable( composable(
Screen.PerformanceView.route, Screen.PerformanceView.route,
arguments = listOf(navArgument("id") { type = NavType.IntType }) arguments = listOf(navArgument("id") { type = NavType.IntType })

View File

@ -3,12 +3,14 @@ package com.example.mobile_labs.ui.navigation
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DateRange import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.Face
import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.List import androidx.compose.material.icons.filled.List
import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Person
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import com.example.mobile_labs.R import com.example.mobile_labs.R
import com.example.mobile_labs.api.MyServerService
enum class Screen( enum class Screen(
val route: String, val route: String,
@ -30,10 +32,19 @@ enum class Screen(
), ),
About( About(
"about", R.string.about_main_title, Icons.Filled.Info "about", R.string.about_main_title, Icons.Filled.Info
),
Login(
"login", R.string.login_main_title, Icons.Filled.Face
); );
companion object { companion object {
val bottomBarItems = listOf( val bottomBarItems = if (MyServerService.getToken().isBlank()) listOf(
Schedule,
Repertoire,
PeopleList,
About,
Login
) else listOf(
Schedule, Schedule,
Repertoire, Repertoire,
PeopleList, PeopleList,

View File

@ -1,6 +1,7 @@
package com.example.mobile_labs.ui.performance.list package com.example.mobile_labs.ui.performance.list
import android.content.res.Configuration import android.content.res.Configuration
import android.util.Log
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -28,6 +29,7 @@ import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey import androidx.paging.compose.itemKey
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.example.mobile_labs.R import com.example.mobile_labs.R
import com.example.mobile_labs.api.MyServerService
import com.example.mobile_labs.database.performance.model.Performance import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.common.AppViewModelProvider import com.example.mobile_labs.common.AppViewModelProvider
import com.example.mobile_labs.ui.navigation.Screen import com.example.mobile_labs.ui.navigation.Screen

View File

@ -6,12 +6,15 @@
<string name="performance_author">Автор</string> <string name="performance_author">Автор</string>
<string name="performance_director">Режиссёр</string> <string name="performance_director">Режиссёр</string>
<string name="performance_actors">Актёры</string> <string name="performance_actors">Актёры</string>
<string name="people_main_title">Люди театра</string> <string name="people_main_title">Труппа</string>
<string name="about_main_title">О нас</string> <string name="about_main_title">О нас</string>
<string name="login_main_title">Вход</string>
<string name="performance_view_main_title">Представление</string> <string name="performance_view_main_title">Представление</string>
<string name="people_missing_description">Данные о людях отсутствуют</string> <string name="people_missing_description">Данные о людях отсутствуют</string>
<string name="events_missing_description">Данные о событиях отсутствуют</string> <string name="events_missing_description">Данные о событиях отсутствуют</string>
<string name="performance_missing_description">Данные о представлениях отсутствуют</string> <string name="performance_missing_description">Данные о представлениях отсутствуют</string>
<string name="login">Логин</string>
<string name="password">Пароль</string>
<string name="about_text"> <string name="about_text">
<p> <p>
Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis! Facilisi pretium vehicula purus porttitor vitae aliquet dignissim. Donec diam molestie litora magnis dolor aptent scelerisque mus. Sit mi, venenatis interdum. Commodo vel malesuada tincidunt eget. Aenean laoreet lacinia platea sem? Libero urna odio diam? Nisl, sodales nisi gravida. Interdum elementum libero turpis dapibus tristique per sed maecenas ante integer massa? Tortor molestie sapien himenaeos condimentum. Facilisis accumsan ullamcorper semper fermentum elementum quisque. Curae;, vivamus ante hac elit fringilla odio ornare curabitur quisque magna commodo. Placerat proin! Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis! Facilisi pretium vehicula purus porttitor vitae aliquet dignissim. Donec diam molestie litora magnis dolor aptent scelerisque mus. Sit mi, venenatis interdum. Commodo vel malesuada tincidunt eget. Aenean laoreet lacinia platea sem? Libero urna odio diam? Nisl, sodales nisi gravida. Interdum elementum libero turpis dapibus tristique per sed maecenas ante integer massa? Tortor molestie sapien himenaeos condimentum. Facilisis accumsan ullamcorper semper fermentum elementum quisque. Curae;, vivamus ante hac elit fringilla odio ornare curabitur quisque magna commodo. Placerat proin!