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")
debugImplementation("androidx.compose.ui:ui-tooling")
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
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.PerformanceRemote
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 kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
@ -52,6 +55,11 @@ interface MyServerService {
@Path("id") id: Int,
): EventRemote
@POST("api/login")
suspend fun login(
@Body credentials: Credentials
): Token
companion object {
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) {
_token = token
}

View File

@ -4,6 +4,6 @@ import kotlinx.serialization.Serializable
@Serializable
data class Credentials(
val login: String = "",
val email: 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)
// dbPersonRepository.clearPeople()
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.PERSON)
dbPersonRepository.clearPeople()
dbRemoteKeyRepository.createRemoteKeys(keys)
dbPersonRepository.insertPeople(people)
}

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.mobile_labs.TheatreApplication
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.view.PerformanceViewModel
import com.example.mobile_labs.ui.person.list.PeopleListViewModel
@ -25,6 +26,9 @@ object AppViewModelProvider {
initializer {
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 com.example.mobile_labs.ui.theme.Mobile_LabsTheme
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.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.view.PerformanceView
import com.example.mobile_labs.ui.person.list.PeopleList
@ -78,6 +80,7 @@ fun Navbar(
) {
NavigationBar(modifier) {
Screen.bottomBarItems.forEach { screen ->
if (MyServerService.getToken().isBlank() || screen.route !== "login") {
NavigationBarItem(
icon = { Icon(screen.icon, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
@ -85,16 +88,17 @@ fun Navbar(
onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
saveState = false
}
launchSingleTop = true
restoreState = true
restoreState = false
}
}
)
}
}
}
}
@Composable
fun Navhost(
@ -108,9 +112,14 @@ fun Navhost(
modifier.padding(innerPadding)
) {
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.About.route) { About() }
if (MyServerService.getToken().isBlank()) {
composable(Screen.Login.route) { Login(navController) }
}
composable(
Screen.PerformanceView.route,
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.compose.material.icons.Icons
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.Info
import androidx.compose.material.icons.filled.List
import androidx.compose.material.icons.filled.Person
import androidx.compose.ui.graphics.vector.ImageVector
import com.example.mobile_labs.R
import com.example.mobile_labs.api.MyServerService
enum class Screen(
val route: String,
@ -30,10 +32,19 @@ enum class Screen(
),
About(
"about", R.string.about_main_title, Icons.Filled.Info
),
Login(
"login", R.string.login_main_title, Icons.Filled.Face
);
companion object {
val bottomBarItems = listOf(
val bottomBarItems = if (MyServerService.getToken().isBlank()) listOf(
Schedule,
Repertoire,
PeopleList,
About,
Login
) else listOf(
Schedule,
Repertoire,
PeopleList,

View File

@ -1,6 +1,7 @@
package com.example.mobile_labs.ui.performance.list
import android.content.res.Configuration
import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -28,6 +29,7 @@ import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import coil.compose.AsyncImage
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.common.AppViewModelProvider
import com.example.mobile_labs.ui.navigation.Screen

View File

@ -6,12 +6,15 @@
<string name="performance_author">Автор</string>
<string name="performance_director">Режиссёр</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="login_main_title">Вход</string>
<string name="performance_view_main_title">Представление</string>
<string name="people_missing_description">Данные о людях отсутствуют</string>
<string name="events_missing_description">Данные о событиях отсутствуют</string>
<string name="performance_missing_description">Данные о представлениях отсутствуют</string>
<string name="login">Логин</string>
<string name="password">Пароль</string>
<string name="about_text">
<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!