From a687ff002fa6a1f68c86740da71cf842d78f48aa Mon Sep 17 00:00:00 2001 From: abazov73 <92822431+abazov73@users.noreply.github.com> Date: Tue, 26 Dec 2023 23:33:11 +0400 Subject: [PATCH] Course work: implement login --- app/build.gradle.kts | 3 + .../mobile_labs/api/MyServerService.kt | 12 ++ .../mobile_labs/api/models/Credentials.kt | 2 +- .../example/mobile_labs/api/models/Token.kt | 8 ++ .../api/people/PeopleRemoteMediator.kt | 4 +- .../common/AppViewModelProvider.kt | 4 + .../com/example/mobile_labs/ui/login/Login.kt | 116 ++++++++++++++++++ .../mobile_labs/ui/login/LoginViewModel.kt | 49 ++++++++ .../mobile_labs/ui/navigation/MainNavbar.kt | 37 +++--- .../mobile_labs/ui/navigation/Screen.kt | 13 +- .../ui/performance/list/PerformanceList.kt | 2 + app/src/main/res/values/strings.xml | 5 +- 12 files changed, 236 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/com/example/mobile_labs/api/models/Token.kt create mode 100644 app/src/main/java/com/example/mobile_labs/ui/login/Login.kt create mode 100644 app/src/main/java/com/example/mobile_labs/ui/login/LoginViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9815ed3..5814115 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") } \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/api/MyServerService.kt b/app/src/main/java/com/example/mobile_labs/api/MyServerService.kt index 335d595..4253f75 100644 --- a/app/src/main/java/com/example/mobile_labs/api/MyServerService.kt +++ b/app/src/main/java/com/example/mobile_labs/api/MyServerService.kt @@ -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 } diff --git a/app/src/main/java/com/example/mobile_labs/api/models/Credentials.kt b/app/src/main/java/com/example/mobile_labs/api/models/Credentials.kt index aae34b9..01af89b 100644 --- a/app/src/main/java/com/example/mobile_labs/api/models/Credentials.kt +++ b/app/src/main/java/com/example/mobile_labs/api/models/Credentials.kt @@ -4,6 +4,6 @@ import kotlinx.serialization.Serializable @Serializable data class Credentials( - val login: String = "", + val email: String = "", val password: String = "", ) \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/api/models/Token.kt b/app/src/main/java/com/example/mobile_labs/api/models/Token.kt new file mode 100644 index 0000000..24367c0 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/api/models/Token.kt @@ -0,0 +1,8 @@ +package com.example.mobile_labs.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class Token( + val token: String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/api/people/PeopleRemoteMediator.kt b/app/src/main/java/com/example/mobile_labs/api/people/PeopleRemoteMediator.kt index b5a556d..65ffb8b 100644 --- a/app/src/main/java/com/example/mobile_labs/api/people/PeopleRemoteMediator.kt +++ b/app/src/main/java/com/example/mobile_labs/api/people/PeopleRemoteMediator.kt @@ -72,8 +72,8 @@ class PeopleRemoteMediator( ) } -// dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.PERSON) -// dbPersonRepository.clearPeople() + dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.PERSON) + dbPersonRepository.clearPeople() dbRemoteKeyRepository.createRemoteKeys(keys) dbPersonRepository.insertPeople(people) } diff --git a/app/src/main/java/com/example/mobile_labs/common/AppViewModelProvider.kt b/app/src/main/java/com/example/mobile_labs/common/AppViewModelProvider.kt index 4e1f6fe..2ca237d 100644 --- a/app/src/main/java/com/example/mobile_labs/common/AppViewModelProvider.kt +++ b/app/src/main/java/com/example/mobile_labs/common/AppViewModelProvider.kt @@ -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() + } } } diff --git a/app/src/main/java/com/example/mobile_labs/ui/login/Login.kt b/app/src/main/java/com/example/mobile_labs/ui/login/Login.kt new file mode 100644 index 0000000..fe864f6 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/ui/login/Login.kt @@ -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), + ) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/ui/login/LoginViewModel.kt b/app/src/main/java/com/example/mobile_labs/ui/login/LoginViewModel.kt new file mode 100644 index 0000000..49abc59 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/ui/login/LoginViewModel.kt @@ -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() + val successState: LiveData + 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 + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/ui/navigation/MainNavbar.kt b/app/src/main/java/com/example/mobile_labs/ui/navigation/MainNavbar.kt index ee84fbe..ffb3b82 100644 --- a/app/src/main/java/com/example/mobile_labs/ui/navigation/MainNavbar.kt +++ b/app/src/main/java/com/example/mobile_labs/ui/navigation/MainNavbar.kt @@ -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,20 +80,22 @@ fun Navbar( ) { NavigationBar(modifier) { Screen.bottomBarItems.forEach { screen -> - NavigationBarItem( - icon = { Icon(screen.icon, contentDescription = null) }, - label = { Text(stringResource(screen.resourceId)) }, - selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true, - onClick = { - navController.navigate(screen.route) { - popUpTo(navController.graph.findStartDestination().id) { - saveState = true + if (MyServerService.getToken().isBlank() || screen.route !== "login") { + NavigationBarItem( + icon = { Icon(screen.icon, contentDescription = null) }, + label = { Text(stringResource(screen.resourceId)) }, + selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true, + onClick = { + navController.navigate(screen.route) { + popUpTo(navController.graph.findStartDestination().id) { + saveState = false + } + launchSingleTop = true + restoreState = false } - launchSingleTop = true - restoreState = true } - } - ) + ) + } } } } @@ -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() } + 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 }) diff --git a/app/src/main/java/com/example/mobile_labs/ui/navigation/Screen.kt b/app/src/main/java/com/example/mobile_labs/ui/navigation/Screen.kt index ac02e5a..d713ee2 100644 --- a/app/src/main/java/com/example/mobile_labs/ui/navigation/Screen.kt +++ b/app/src/main/java/com/example/mobile_labs/ui/navigation/Screen.kt @@ -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, diff --git a/app/src/main/java/com/example/mobile_labs/ui/performance/list/PerformanceList.kt b/app/src/main/java/com/example/mobile_labs/ui/performance/list/PerformanceList.kt index e9e7168..88e299f 100644 --- a/app/src/main/java/com/example/mobile_labs/ui/performance/list/PerformanceList.kt +++ b/app/src/main/java/com/example/mobile_labs/ui/performance/list/PerformanceList.kt @@ -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 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3530317..accd3d9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,12 +6,15 @@ Автор Режиссёр Актёры - Люди театра + Труппа О нас + Вход Представление Данные о людях отсутствуют Данные о событиях отсутствуют Данные о представлениях отсутствуют + Логин + Пароль

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!