This commit is contained in:
Екатерина Рогашова 2023-12-23 02:21:34 +04:00
commit e279d49391
125 changed files with 7160 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
/server/node_modules/

3
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
My Application

6
.idea/compiler.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

20
.idea/gradle.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -0,0 +1,41 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
</profile>
</component>

6
.idea/kotlinc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.20" />
</component>
</project>

9
.idea/misc.xml Normal file
View File

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

16
app/.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
node_modules/
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

103
app/build.gradle.kts Normal file
View File

@ -0,0 +1,103 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
id("org.jetbrains.kotlin.plugin.serialization")
}
android {
namespace = "com.example.myapplication"
compileSdk = 33
defaultConfig {
applicationId = "com.example.myapplication"
minSdk = 24
targetSdk = 33
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.5"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-compose:1.7.2")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.navigation:navigation-compose:2.6.0")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3:1.1.2")
// Room
val room_version = "2.5.2"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
ksp("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version")
implementation("androidx.room:room-paging:$room_version")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
//Paging
implementation ("androidx.paging:paging-compose:3.2.1")
implementation ("androidx.paging:paging-runtime:3.2.1")
// retrofit
val retrofitVersion = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
implementation("androidx.paging:paging-compose:3.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
// Tests
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package com.example.myapplication
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.myapplication", appContext.packageName)
}
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".LessonApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Pmudemo"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31">
<activity
android:name=".MainComposeActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Pmudemo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,56 @@
package com.example.myapplication
import android.content.res.Configuration
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.example.myapplication.ui.theme.PmudemoTheme
@Composable
fun BrDance() {
val localContext = LocalContext.current
val aboutText = localContext.resources.getText(R.string.brdance_text)
Column(Modifier.padding(all = 10.dp)) {
AndroidView(
modifier = Modifier
.fillMaxWidth(),
factory = { context -> TextView(context) },
update = { it.text = aboutText }
)
Spacer(Modifier.padding(bottom = 10.dp))
Image(
painter = painterResource(id = R.drawable.brdance),
modifier = Modifier
.size(400.dp) // Задайте требуемый размер картинке здесь
.padding(19.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun BrDancePreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
BrDance()
}
}
}

View File

@ -0,0 +1,56 @@
package com.example.myapplication
import android.content.res.Configuration
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.example.myapplication.ui.theme.PmudemoTheme
@Composable
fun Contemp() {
val localContext = LocalContext.current
val aboutText = localContext.resources.getText(R.string.contemp_text)
Column(Modifier.padding(all = 10.dp)) {
AndroidView(
modifier = Modifier
.fillMaxWidth(),
factory = { context -> TextView(context) },
update = { it.text = aboutText }
)
Spacer(Modifier.padding(bottom = 10.dp))
Image(
painter = painterResource(id = R.drawable.contemp),
modifier = Modifier
.size(400.dp) // Задайте требуемый размер картинке здесь
.padding(19.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun ContempPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
Contemp()
}
}
}

View File

@ -0,0 +1,99 @@
package com.example.myapplication
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.example.myapplication.ui.theme.PmudemoTheme
@Composable
fun DirectionView(navController: NavHostController?) {
val localContext = LocalContext.current
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
) {
Image(
painter = painterResource(id = R.drawable.kpop_button),
modifier = Modifier.clickable { navController?.navigate("kpop") }
.size(180.dp) // Задайте требуемый размер картинке здесь
.padding(16.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
Image(
painter = painterResource(id = R.drawable.house_button),
modifier = Modifier.clickable { navController?.navigate("house") }
.size(180.dp) // Задайте требуемый размер картинке здесь
.padding(16.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
Image(
painter = painterResource(id = R.drawable.img_7),
modifier = Modifier.clickable { }
.size(180.dp) // Задайте требуемый размер картинке здесь
.padding(16.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
}
Column(
Modifier
.fillMaxWidth()
.padding(top = 11.dp)
.padding(start = 180.dp)
) {
Image(
painter = painterResource(id = R.drawable.contemp_button),
modifier = Modifier.clickable { navController?.navigate("contemp") }
.size(180.dp) // Задайте требуемый размер картинке здесь
.padding(16.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
Image(
painter = painterResource(id = R.drawable.brdance_button),
modifier = Modifier.clickable { navController?.navigate("brdance") }
.size(180.dp) // Задайте требуемый размер картинке здесь
.padding(19.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
Image(
painter = painterResource(id = R.drawable.jazz_button),
modifier = Modifier.clickable { }
.size(180.dp) // Задайте требуемый размер картинке здесь
.padding(16.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun DirectionViewPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
DirectionView(navController = null)
}
}
}

View File

@ -0,0 +1,242 @@
package com.example.myapplication
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import com.example.myapplication.Graph.AuthScreen
import com.example.myapplication.Graph.Graph
import com.example.myapplication.dataBase.model.User
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.navigation.Screen
import com.example.myapplication.ui.theme.PmudemoTheme
import com.example.myapplication.ui.user.CurrentUserViewModel
import com.example.myapplication.ui.user.EntryViewModel
@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Enter(navController: NavController, modifier: Modifier = Modifier, entryScreenViewModel: EntryViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
var emailValue by rememberSaveable { mutableStateOf("") }
var passwordValue by rememberSaveable { mutableStateOf("") }
entryScreenViewModel.setUserList()
val users = mutableStateOf<List<User>>(entryScreenViewModel.userList)
val argument = currentUserViewModel.argument.value
var passwordVisibility by rememberSaveable { mutableStateOf(false) }
Box(
modifier = modifier
.requiredWidth(width = 412.dp)
.requiredHeight(height = 1200.dp)
.background(color = Color.White)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 128.dp,
y = 730.dp
)
.requiredWidth(width = 381.dp)
.requiredHeight(height = 268.dp)
.rotate(degrees = 8.33f)
) {
}
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = (-111.28436279296875).dp,
y = (-96.86965942382812).dp
)
.requiredWidth(width = 250.dp)
.requiredHeight(height = 290.dp)
.rotate(degrees = 12.96f)
) {
}
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = (-111).dp,
y = (-96).dp
)
.requiredWidth(width = 250.dp)
.requiredHeight(height = 290.dp)
.rotate(degrees = 12.96f)
) {
}
}
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.8f)
.clip(RoundedCornerShape(15.dp))
.padding(10.dp)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Box(
modifier = Modifier
.fillMaxWidth()
, contentAlignment = Alignment.TopCenter
) {
}
Text(
text = "Добро пожаловать!",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 20.sp
),
modifier = modifier
.requiredWidth(width = 436.dp)
.requiredHeight(height = 27.dp)
)
Spacer(modifier = Modifier.padding(10.dp))
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Spacer(modifier = Modifier.padding(3.dp))
OutlinedTextField(
value = emailValue,
onValueChange = { emailValue = it },
modifier = modifier
.requiredWidth(width = 300.dp)
.requiredHeight(height = 80.dp)
.background(Color.White),
label = { Text("Логин") },
singleLine = true,
placeholder = { Text("Введите логин") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
)
OutlinedTextField(
value = passwordValue,
onValueChange = { passwordValue = it },
modifier = modifier
.requiredWidth(width = 300.dp)
.requiredHeight(height = 80.dp)
.background(Color.White),
label = { Text("Пароль") },
singleLine = true,
placeholder = { Text("Введите пароль") },
visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
val image = if (passwordVisibility)
Icons.Filled.Done
else Icons.Filled.Lock
val description =
if (passwordVisibility) "Hide password" else "Show password"
IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
Icon(imageVector = image, description)
}
}
)
Spacer(modifier = Modifier.padding(10.dp))
Button(
onClick = {
if (passwordValue.isNotEmpty()) {
users.value.forEach { user ->
if (user.password == passwordValue && user.userName == emailValue) {
currentUserViewModel.setArgument(user.uid.toString())
Log.d("CurrentUserViewModel1", "Текущий пользователь: $user")
navController.navigate(route = Graph.passUserId(user.uid.toString())) {
}
}
}
}
},
modifier = Modifier
.fillMaxWidth()
.requiredWidth(width = 300.dp)
.height(50.dp),
shape = RoundedCornerShape(0.dp)
) {
Text(text = "Войти", fontSize = 20.sp)
}
Spacer(modifier = Modifier.padding(3.dp))
Text(
text = "Нет аккаунта? Зарегистрироваться",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 15.sp
),
modifier = Modifier
.fillMaxSize()
.offset(
x = 0.dp,
y = 75.dp
)
.clickable {
navController?.navigate(route = AuthScreen.Register.route)
}
)
Spacer(modifier = Modifier.padding(20.dp))
}
}
}
}
}

View File

@ -0,0 +1,31 @@
package com.example.myapplication.Graph
import androidx.compose.ui.Modifier
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navigation
import com.example.myapplication.Enter
import com.example.myapplication.Registration
import com.example.myapplication.ui.user.CurrentUserViewModel
import com.example.myapplication.ui.user.EntryViewModel
import com.example.myapplication.ui.user.RegisterViewModel
fun NavGraphBuilder.authNavGraph(navController: NavHostController, registerScreenViewModel: RegisterViewModel, entryScreenViewModel: EntryViewModel, currentUserViewModel: CurrentUserViewModel){
navigation(
route=Graph.AUTHENTICATION,
startDestination = AuthScreen.Enter.route
){
composable(route=AuthScreen.Enter.route){
Enter(navController = navController, Modifier,entryScreenViewModel,currentUserViewModel)
}
composable(route=AuthScreen.Register.route){
Registration(navController = navController, Modifier,registerScreenViewModel,currentUserViewModel)
}
}
}
sealed class AuthScreen(val route: String){
object Enter : AuthScreen(route = "ENTER")
object Register : AuthScreen(route="REGISTER")
}

View File

@ -0,0 +1,112 @@
package com.example.myapplication.Graph
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
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.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
@Composable
fun BottomBar(navController: NavHostController){
val screens = listOf(
BottomBarScreen.LessonList,
BottomBarScreen.DirectionView,
BottomBarScreen.HomeView,
BottomBarScreen.Profile,
BottomBarScreen.About,
BottomBarScreen.Contemp,
BottomBarScreen.BrDance,
BottomBarScreen.House,
BottomBarScreen.Kpop,
BottomBarScreen.LessonEdit,
BottomBarScreen.LessonRecord
)
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination=navBackStackEntry?.destination
val bottomBarDestination=screens.any{ it.route==currentDestination?.route }
Row(
modifier = Modifier
.background(Color.Transparent),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
if (bottomBarDestination) {
NavigationBar(
containerColor = Color.Black,
modifier = Modifier
.height(90.dp)
) {
screens.forEach { screen ->
AddItem(
screen = screen,
currentDestination = currentDestination,
navController = navController
)
}
}
}
}
}
@Composable
fun RowScope.AddItem(
screen: BottomBarScreen,
currentDestination: NavDestination?,
navController: NavController
){
val selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true
val background = Color.Black
Box(
modifier = Modifier
.height(90.dp)
.background(background)
.clickable(onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id)
launchSingleTop = true
}
})
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
Icon(
imageVector = screen.icon,
contentDescription = "icon",
tint = Color.White,
modifier = Modifier.size(60.dp).padding(start=13.dp, end = 10.dp)
)
}
}
}

View File

@ -0,0 +1,69 @@
package com.example.myapplication.Graph
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.Home
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.List
import androidx.compose.ui.graphics.vector.ImageVector
import com.example.myapplication.R
sealed class BottomBarScreen(
val route: String,
val title: String,
val icon: ImageVector = Icons.Filled.Favorite
){
object LessonList: BottomBarScreen(
route = "LESSONLIST",
title="",
icon= Icons.Filled.DateRange,
)
object Profile: BottomBarScreen(
route = "PROFILE",
title="",
icon= Icons.Filled.Face,
)
object About: BottomBarScreen(
route = "ABOUT",
title="",
icon= Icons.Filled.Info,
)
object HomeView: BottomBarScreen(
route = "HOME",
title="",
icon= Icons.Filled.Home,
)
object LessonEdit: BottomBarScreen(
route = "lesson-edit/{id}",
title=""
)
object House: BottomBarScreen(
route = "HOUSE",
title=""
)
object Contemp: BottomBarScreen(
route = "CONTEMP",
title=""
)
object BrDance: BottomBarScreen(
route = "BRDANCE",
title=""
)
object Kpop: BottomBarScreen(
route = "KPOP",
title=""
)
object DirectionView: BottomBarScreen(
route = "DIRECTIONVIEW",
title="",
icon= Icons.Filled.List
)
object LessonRecord: BottomBarScreen(
route = "lesson-record/{id}",
title=""
)
}

View File

@ -0,0 +1,71 @@
package com.example.myapplication.Graph
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.example.myapplication.BrDance
import com.example.myapplication.Contemp
import com.example.myapplication.DirectionView
import com.example.myapplication.Enter
import com.example.myapplication.HomeView
import com.example.myapplication.House
import com.example.myapplication.Kpop
import com.example.myapplication.Profile
import com.example.myapplication.Registration
import com.example.myapplication.ui.About
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.edit.DayDropDownViewModel
import com.example.myapplication.ui.edit.DirectionDropDownViewModel
import com.example.myapplication.ui.edit.LessonEdit
import com.example.myapplication.ui.edit.LessonRecord
import com.example.myapplication.ui.edit.LessonEditViewModel
import com.example.myapplication.ui.lesson.LessonList
import com.example.myapplication.ui.lesson.LessonListViewModel
import com.example.myapplication.ui.navigation.Screen
import com.example.myapplication.ui.user.CurrentUserViewModel
@Composable
fun HomeNavGraph(navController: NavHostController, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory), lessonListViewModel: LessonListViewModel = viewModel(factory = AppViewModelProvider.Factory)){
Column {
TopBar()
NavHost(
navController = navController,
route = Graph.MAIN,
startDestination = BottomBarScreen.HomeView.route
) {
composable(route = BottomBarScreen.LessonList.route) { LessonList(navController, lessonListViewModel, currentUserViewModel) }
composable(route = BottomBarScreen.About.route) { About() }
composable(route = BottomBarScreen.HomeView.route) { HomeView(navController, Modifier) }
composable(route = BottomBarScreen.House.route) { House() }
composable(route = BottomBarScreen.Contemp.route) { Contemp() }
composable(route = BottomBarScreen.BrDance.route) { BrDance() }
composable(route = BottomBarScreen.Kpop.route) { Kpop() }
composable(
BottomBarScreen.LessonEdit.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) {
LessonEdit(navController)
}
composable(
BottomBarScreen.LessonRecord.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) {
LessonRecord(navController)
}
composable(route = BottomBarScreen.DirectionView.route) { DirectionView(navController) }
composable(route = BottomBarScreen.Profile.route) {
Profile(
navController,
Modifier,
currentUserViewModel
)
}
}
}
}

View File

@ -0,0 +1,43 @@
package com.example.myapplication.Graph
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHost
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.example.myapplication.LoadScreen
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.user.CurrentUserViewModel
import com.example.myapplication.ui.user.EntryViewModel
import com.example.myapplication.ui.user.RegisterViewModel
const val USERID_ARGUMENT="uid"
@Composable
fun RootNavigationGraph(navController: NavHostController, registerScreenViewModel: RegisterViewModel = viewModel(factory = AppViewModelProvider.Factory), entryScreenViewModel: EntryViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
NavHost(
navController=navController,
route = Graph.ROOT,
startDestination = Graph.AUTHENTICATION
){
authNavGraph(navController=navController,registerScreenViewModel,entryScreenViewModel,currentUserViewModel)
composable(route=Graph.MAIN,
arguments = listOf(navArgument(USERID_ARGUMENT){
type= NavType.StringType
})){
LoadScreen(currentUserViewModel = currentUserViewModel)
}
}
}
object Graph{
const val ROOT="root_graph"
const val AUTHENTICATION="auth_graph"
const val MAIN="main_graph/{$USERID_ARGUMENT}"
fun passUserId(uid: String): String{
return "main_graph/$uid"
}
}

View File

@ -0,0 +1,37 @@
package com.example.myapplication.Graph
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.material.icons.Icons
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
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.unit.dp
import com.example.myapplication.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopBar() {
TopAppBar(
modifier = Modifier.requiredHeight(height = 70.dp),
colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = Color.Black ),
title = {
}
)
}

View File

@ -0,0 +1,69 @@
package com.example.myapplication
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.example.myapplication.Graph.BottomBar
import com.example.myapplication.Graph.HomeNavGraph
import com.example.myapplication.ui.theme.PmudemoTheme
import com.example.myapplication.ui.user.CurrentUserViewModel
@Composable
fun HomeView(navController: NavController, modifier: Modifier = Modifier) {
val localContext = LocalContext.current
Box(modifier = Modifier.fillMaxSize().padding(all = 0.dp).background(Color.Black)) {
Image(
painter = painterResource(id = R.drawable.img_5),
contentDescription = null,
modifier = Modifier.fillMaxSize()
)
}
Column(Modifier.padding(all = 10.dp)) {
AndroidView(
modifier = Modifier
.fillMaxWidth(),
factory = { context -> TextView(context) }
)
Spacer(Modifier.padding(bottom = 10.dp))
}
}
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoadScreen(navController: NavHostController = rememberNavController(), currentUserViewModel: CurrentUserViewModel){
Scaffold(
bottomBar = { BottomBar(navController = navController) },
) {
Modifier
.padding(it)
HomeNavGraph(navController = navController, currentUserViewModel)
}
}

View File

@ -0,0 +1,56 @@
package com.example.myapplication
import android.content.res.Configuration
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.example.myapplication.ui.theme.PmudemoTheme
@Composable
fun House() {
val localContext = LocalContext.current
val aboutText = localContext.resources.getText(R.string.house_text)
Column(Modifier.padding(all = 10.dp)) {
AndroidView(
modifier = Modifier
.fillMaxWidth(),
factory = { context -> TextView(context) },
update = { it.text = aboutText }
)
Spacer(Modifier.padding(bottom = 10.dp))
Image(
painter = painterResource(id = R.drawable.house),
modifier = Modifier
.size(400.dp) // Задайте требуемый размер картинке здесь
.padding(19.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun HousePreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
House()
}
}
}

View File

@ -0,0 +1,56 @@
package com.example.myapplication
import android.content.res.Configuration
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.example.myapplication.ui.theme.PmudemoTheme
@Composable
fun Kpop() {
val localContext = LocalContext.current
val aboutText = localContext.resources.getText(R.string.kpop_text)
Column(Modifier.padding(all = 10.dp)) {
AndroidView(
modifier = Modifier
.fillMaxWidth(),
factory = { context -> TextView(context) },
update = { it.text = aboutText }
)
Spacer(Modifier.padding(bottom = 10.dp))
Image(
painter = painterResource(id = R.drawable.kpop),
modifier = Modifier
.size(400.dp) // Задайте требуемый размер картинке здесь
.padding(19.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun KpopPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
Kpop()
}
}
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication
import android.app.Application
import com.example.myapplication.dataBase.AppContainer
import com.example.myapplication.dataBase.AppDataContainer
class LessonApplication : Application() {
lateinit var container: AppContainer
override fun onCreate() {
super.onCreate()
container = AppDataContainer(this)
}
}

View File

@ -0,0 +1,32 @@
package com.example.myapplication
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.rememberNavController
import com.example.myapplication.Graph.RootNavigationGraph
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.navigation.MainNavbar
import com.example.myapplication.ui.theme.PmudemoTheme
class MainComposeActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PmudemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
//MainNavbar()
RootNavigationGraph(navController = rememberNavController(),currentUserViewModel = viewModel(factory = AppViewModelProvider.Factory))
}
}
}
}
}

View File

@ -0,0 +1,453 @@
package com.example.myapplication
import android.annotation.SuppressLint
import android.util.Log
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
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.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
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.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
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 com.example.myapplication.Graph.AuthScreen
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.user.CurrentUserViewModel
import kotlinx.coroutines.launch
@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Profile(navController: NavController, modifier: Modifier = Modifier, currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val context = LocalContext.current
var getUser by remember { mutableStateOf(currentUserViewModel.user) }
val coroutineScope = rememberCoroutineScope()
Log.d("Profile", "getUser: $getUser")
var openDialogUser by remember { mutableStateOf(false) }
var userName by remember { mutableStateOf(getUser?.userName) }
var password by remember { mutableStateOf(getUser?.password) }
var fio by remember { mutableStateOf(getUser?.fio) }
// Вывод лога о параметрах пользователя
Log.d("Profile", "userName: $userName, password: $password, fio: $fio")
if (openDialogUser) {
AlertDialog(
onDismissRequest = {
openDialogUser = false
userName = getUser?.userName.toString()
password = getUser?.password.toString()
fio = getUser?.fio.toString()
},
content = {
Column(
modifier = Modifier
.padding(16.dp)
.background(Color.White),
) {
Text(
text = "Редактирование профиля"
)
userName?.let {
OutlinedTextField(
value = it,
onValueChange = {
userName = it
},
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(10.dp),
shape = RoundedCornerShape(0.dp),
label = { Text("Логин") },
placeholder = { Text("Введите логин...") },
minLines = 1,
maxLines = 1,
)
}
password?.let {
OutlinedTextField(
value = it,
onValueChange = {
password = it
},
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(10.dp),
shape = RoundedCornerShape(0.dp),
label = { Text("Пароль") },
placeholder = { Text("Введите пароль...") },
minLines = 1,
maxLines = 1,
)
}
fio?.let {
OutlinedTextField(
value = it,
onValueChange = {
fio = it
},
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(10.dp),
shape = RoundedCornerShape(0.dp),
label = { Text("ФИО") },
placeholder = { Text("Введите ФИО...") },
minLines = 1,
maxLines = 1,
)
}
Row(
modifier = Modifier.padding(all = 8.dp),
horizontalArrangement = Arrangement.Center
) {
Button(
onClick = {
openDialogUser = false
if (password?.isNotEmpty() == true && userName?.isNotEmpty() == true && fio?.isNotEmpty() == true
) {
getUser?.userName = userName as String
getUser?.password = password as String
getUser?.fio = fio as String
coroutineScope.launch {
getUser?.let {
currentUserViewModel.updateUser(it)
}
}
}
},
modifier = Modifier
.padding(start = 100.dp)
.fillMaxWidth()
.height(40.dp),
shape = RoundedCornerShape(0.dp)
) {
Text(text = "Редактировать", fontSize = 15.sp)
}
}
}
},
)
}
LazyColumn(
modifier = modifier
.requiredWidth(width = 412.dp)
.requiredHeight(height = 605.dp)
.background(color = Color.White)
) {
item {
Box(
modifier = Modifier
.offset(
x = 9.dp,
y = 80.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 27.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
.clip(shape = RoundedCornerShape(5.dp))
.background(color = Color.White)
.border(
border = BorderStroke(3.dp, Color.Black),
shape = RoundedCornerShape(5.dp)
)
)
Text(
text = "Мой профиль",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)
}
}
}
item {
Box(
modifier = Modifier
.offset(
x = 106.dp,
y = 95.dp
)
.requiredWidth(width = 200.dp)
.requiredHeight(height = 310.dp)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 90.dp
)
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
)
{
Box(
modifier = Modifier
.requiredWidth(width = 300.dp)
.requiredHeight(height = 80.dp)
.background(Color.White)
.border(width = 1.dp, color = Color.Black) // Добавляем черную границу,
)
Text(
text = "${getUser?.fio}",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 17.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
.wrapContentHeight(align = Alignment.CenterVertically)
)
}
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 180.dp
)
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 300.dp)
.requiredHeight(height = 80.dp)
.background(Color.White)
.border(width = 1.dp, color = Color.Black) // Добавляем черную границу
)
Text(
text = "${getUser?.userName}",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 17.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
.wrapContentHeight(align = Alignment.CenterVertically)
)
}
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 250.dp
)
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.requiredWidth(width = 300.dp)
.height(50.dp)
.background(Color.Black),
)
Text(
text = "Редактировать",
color = Color.White,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 17.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
.wrapContentHeight(align = Alignment.CenterVertically)
.clickable { openDialogUser = true })
}
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 300.dp
)
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
)
// {
// Box(
// modifier = Modifier
// .fillMaxWidth()
// .requiredWidth(width = 300.dp)
// .height(50.dp)
// .background(Color.Black),
// )
// Text(
// text = "Выйти",
// color = Color.White,
// textAlign = TextAlign.Center,
// style = TextStyle(
// fontSize = 17.sp,
// fontWeight = FontWeight.Bold
// ),
// modifier = Modifier
// .requiredWidth(width = 200.dp)
// .requiredHeight(height = 44.dp)
// .wrapContentHeight(align = Alignment.CenterVertically)
// .clickable { navController.navigate(route = AuthScreen.Enter.route); getUser = null })
// }
}
}
item {
Box(
modifier = Modifier
.offset(
x = 9.dp,
y = 220.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 27.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
.clip(shape = RoundedCornerShape(5.dp))
.background(color = Color.White)
)
Text(
text = "Ближайшая запись:",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)
}
}
}
item {
Box(
modifier = Modifier
.offset(
x = 9.dp,
y = 220.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 27.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 70.dp)
.clip(shape = RoundedCornerShape(5.dp))
.background(color = Color.White)
)
Text(
text = "${getUser?.lessonId}",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.background(Color.LightGray)
.requiredWidth(300.dp)
.requiredWidth(70.dp)
)
}
}
}
}
}

View File

@ -0,0 +1,281 @@
package com.example.myapplication
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.Graph.AuthScreen
import com.example.myapplication.Graph.Graph
import com.example.myapplication.dataBase.model.RoleEnum
import com.example.myapplication.dataBase.model.User
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.navigation.Screen
import com.example.myapplication.ui.theme.PmudemoTheme
import com.example.myapplication.ui.user.CurrentUserViewModel
import com.example.myapplication.ui.user.RegisterViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Registration(navController: NavController, modifier: Modifier = Modifier, registerScreenViewModel: RegisterViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
var loginValue by rememberSaveable { mutableStateOf("") }
var passwordValue by rememberSaveable { mutableStateOf("") }
var fioValue by rememberSaveable { mutableStateOf("") }
val coroutineScope = rememberCoroutineScope()
registerScreenViewModel.setUserList()
var users = mutableStateOf<List<User>>(emptyList())
registerScreenViewModel.users.observeForever { userList ->
users.value = userList
}
var passwordVisibility by rememberSaveable { mutableStateOf(false) }
Box(
modifier = modifier
.requiredWidth(width = 412.dp)
.requiredHeight(height = 915.dp)
.background(color = Color.White)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 128.dp,
y = 730.dp
)
.requiredWidth(width = 381.dp)
.requiredHeight(height = 268.dp)
.rotate(degrees = 8.33f)
) {
}
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = (-111.28436279296875).dp,
y = (-96.86965942382812).dp
)
.requiredWidth(width = 250.dp)
.requiredHeight(height = 290.dp)
.rotate(degrees = 12.96f)
) {
}
}
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.8f)
.clip(RoundedCornerShape(15.dp))
.background(Color.Transparent)
.padding(10.dp)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "Регистрация",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 20.sp
),
modifier = modifier
.requiredWidth(width = 436.dp)
.requiredHeight(height = 27.dp)
)
Spacer(modifier = Modifier.padding(10.dp))
Column(horizontalAlignment = Alignment.CenterHorizontally) {
OutlinedTextField(
value = loginValue,
onValueChange = {
loginValue = it
},
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.White
),
modifier = Modifier
.requiredWidth(width = 300.dp)
.requiredHeight(height = 80.dp)
.background(Color.White),
label = { Text("Логин") },
placeholder = { Text("Логин") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
singleLine = true,
)
Spacer(modifier = Modifier.padding(3.dp))
OutlinedTextField(
value = fioValue,
onValueChange = {
fioValue = it
},
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.White
),
modifier = Modifier
.requiredWidth(width = 300.dp)
.requiredHeight(height = 80.dp)
.background(Color.White),
label = { Text("ФИО") },
placeholder = { Text("ФИО") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
singleLine = true,
)
Spacer(modifier = Modifier.padding(3.dp))
OutlinedTextField(
value = passwordValue,
onValueChange = { passwordValue = it },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.White
),
modifier = modifier
.requiredWidth(width = 300.dp)
.requiredHeight(height = 80.dp)
.background(Color.White),
label = { Text("Пароль") },
singleLine = true,
placeholder = { Text("Пароль") },
visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
val image = if (passwordVisibility)
Icons.Filled.Done
else Icons.Filled.Lock
val description =
if (passwordVisibility) "Hide password" else "Show password"
IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
Icon(imageVector = image, description)
}
}
)
Spacer(modifier = Modifier.padding(10.dp))
Button(
onClick = {
var isExist = false;
if (passwordValue.isNotEmpty() && loginValue.isNotEmpty() && fioValue.isNotEmpty()) {
users.value.forEach { user ->
if (user.userName == loginValue || user.fio == fioValue) {
Log.d("User already exist. User id: ", user.uid.toString())
isExist = true
}
}
if (!isExist) {
val newUser = User(0, loginValue, passwordValue, fioValue, RoleEnum.User, null)
coroutineScope.launch {
val insertResult = async {
registerScreenViewModel.insertUser(newUser)
}
insertResult.await()
registerScreenViewModel.setUserList()
registerScreenViewModel.users.observeForever { userList ->
users.value = userList
Log.println(Log.ASSERT, "UsersList", users.value.toString())
users.value?.forEach { user ->
if (user.password == passwordValue && user.userName == loginValue) {
currentUserViewModel.setArgument(user.uid.toString())
navController.navigate(route = Graph.passUserId(user.uid.toString())) {
}
}
}
}
}
}
}
},
modifier = Modifier
.fillMaxWidth()
.requiredWidth(width = 300.dp)
.height(50.dp),
shape = RoundedCornerShape(0.dp)
) {
Text(text = "Зарегистироваться", fontSize = 20.sp)
}
Spacer(modifier = Modifier.padding(0.dp))
Text(
text = "Уже есть аккаунт? Войти",
color = Color.Black,
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 15.sp
),
modifier = Modifier
.fillMaxSize()
.offset(
x = 0.dp,
y = 75.dp
)
.clickable {
navController.navigate(route = AuthScreen.Enter.route)
}
)
Spacer(modifier = Modifier.padding(20.dp))
}
}
}
}
}

View File

@ -0,0 +1,102 @@
package com.example.myapplication.api
import com.example.myapplication.api.model.DayOfWeekRemote
import com.example.myapplication.api.model.DirectionRemote
import com.example.myapplication.api.model.LessonRemote
import com.example.myapplication.api.model.UserRemote
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query
interface MyServerService {
@GET("days")
suspend fun getDays(): List<DayOfWeekRemote>
@GET("users")
suspend fun getUsers(): List<UserRemote>
@GET("directions")
suspend fun getDirections(): List<DirectionRemote>
@GET("lessons")
suspend fun getLessons(
@Query("_page") page: Int,
@Query("_limit") limit: Int,
): List<LessonRemote>
@GET("lessons/{id}")
suspend fun getLesson(
@Path("id") id: Int,
): LessonRemote
@POST("lessons")
suspend fun createLesson(
@Body lesson: LessonRemote,
): LessonRemote
@PUT("lessons/{id}")
suspend fun updateLesson(
@Path("id") id: Int,
@Body lesson: LessonRemote,
): LessonRemote
@DELETE("lessons/{id}")
suspend fun deleteLesson(
@Path("id") id: Int,
): LessonRemote
@GET("users/{id}")
suspend fun getUserById(
@Path("id") id: Int,
): UserRemote
@POST("users")
suspend fun createUser(
@Body user: UserRemote,
): UserRemote
@PUT("users/{id}")
suspend fun updateUser(
@Path("id") id: Int,
@Body user: UserRemote,
): UserRemote
@DELETE("users/{id}")
suspend fun deleteUser(
@Path("id") id: Int,
): UserRemote
companion object {
private const val BASE_URL = "http://192.168.1.67:8079/"
@Volatile
private var INSTANCE: MyServerService? = null
fun getInstance(): MyServerService {
return INSTANCE ?: synchronized(this) {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BASIC
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
.create(MyServerService::class.java)
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,35 @@
package com.example.myapplication.api.day
import android.util.Log
import com.example.myapplication.api.MyServerService
import com.example.myapplication.dataBase.model.DayOfWeek
import com.example.myapplication.dataBase.repository.DayOfWeekRepository
import com.example.myapplication.dataBase.repository.OfflineDayOfWeekRepository
import com.example.myapplication.api.lesson.RestLessonRepository
import com.example.myapplication.api.model.toDayOfWeek
import kotlinx.coroutines.flow.Flow
class RestDayOfWeekRepository(
private val service: MyServerService,
private val dbDayOfWeekRepository: OfflineDayOfWeekRepository,
): DayOfWeekRepository {
override suspend fun getAllDays(): List<DayOfWeek> {
Log.d(RestLessonRepository::class.simpleName, "Get days")
val existDayOfWeeks = dbDayOfWeekRepository.getAllDays().associateBy { it.uid }.toMutableMap()
service.getDays()
.map { it.toDayOfWeek() }
.forEach { dayOfWeek ->
val existDayOfWeek = existDayOfWeeks[dayOfWeek.uid]
if (existDayOfWeek == null) {
dbDayOfWeekRepository.createDayOfWeek(dayOfWeek)
} else if (existDayOfWeek != dayOfWeek) {
dbDayOfWeekRepository.updateDayOfWeek(dayOfWeek)
}
existDayOfWeeks[dayOfWeek.uid] = dayOfWeek
}
return existDayOfWeeks.map { it.value }.sortedBy { it.uid }
}
}

View File

@ -0,0 +1,34 @@
package com.example.myapplication.api.direction
import android.util.Log
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.lesson.RestLessonRepository
import com.example.myapplication.api.model.toDirection
import com.example.myapplication.dataBase.model.Direction
import com.example.myapplication.dataBase.repository.DirectionRepository
import com.example.myapplication.dataBase.repository.OfflineDirectionRepository
class RestDirectionRepository(
private val service: MyServerService,
private val dbDirectionRepository: OfflineDirectionRepository,
): DirectionRepository {
override suspend fun getAllDirections(): List<Direction> {
Log.d(RestLessonRepository::class.simpleName, "Get directions")
val existDirections = dbDirectionRepository.getAllDirections().associateBy { it.uid }.toMutableMap()
service.getDirections()
.map { it.toDirection() }
.forEach { dayOfWeek ->
val existDirection = existDirections[dayOfWeek.uid]
if (existDirection == null) {
dbDirectionRepository.createDirection(dayOfWeek)
} else if (existDirection != dayOfWeek) {
dbDirectionRepository.updateDirection(dayOfWeek)
}
existDirections[dayOfWeek.uid] = dayOfWeek
}
return existDirections.map { it.value }.sortedBy { it.uid }
}
}

View File

@ -0,0 +1,113 @@
package com.example.myapplication.api.lesson
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.day.RestDayOfWeekRepository
import com.example.myapplication.api.direction.RestDirectionRepository
import com.example.myapplication.api.model.toLesson
import com.example.myapplication.dataBase.AppDatabase
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeyType
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeys
import com.example.myapplication.dataBase.remotekeys.repository.OfflineRemoteKeyRepository
import com.example.myapplication.dataBase.repository.OfflineLessonRepository
import retrofit2.HttpException
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class LessonRemoteMediator(
private val service: MyServerService,
private val dbLessonRepository: OfflineLessonRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val dayOfWeekRestRepository: RestDayOfWeekRepository,
private val directionRestRepository: RestDirectionRepository,
private val database: AppDatabase
) : RemoteMediator<Int, Lesson>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Lesson>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val lessons = service.getLessons(page, state.config.pageSize).map { it.toLesson() }
val endOfPaginationReached = lessons.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.LESSON)
dbLessonRepository.clearLessons()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = lessons.map {
RemoteKeys(
entityId = it.uid,
type = RemoteKeyType.LESSON,
prevKey = prevKey,
nextKey = nextKey
)
}
dayOfWeekRestRepository.getAllDays()
directionRestRepository.getAllDirections()
dbRemoteKeyRepository.createRemoteKeys(keys)
dbLessonRepository.insertLessons(lessons)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Lesson>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { student ->
dbRemoteKeyRepository.getAllRemoteKeys(student.uid, RemoteKeyType.LESSON)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Lesson>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { student ->
dbRemoteKeyRepository.getAllRemoteKeys(student.uid, RemoteKeyType.LESSON)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Lesson>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.uid?.let { studentUid ->
dbRemoteKeyRepository.getAllRemoteKeys(studentUid, RemoteKeyType.LESSON)
}
}
}
}

View File

@ -0,0 +1,68 @@
package com.example.myapplication.api.lesson
import android.util.Log
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.day.RestDayOfWeekRepository
import com.example.myapplication.api.direction.RestDirectionRepository
import com.example.myapplication.api.model.toLesson
import com.example.myapplication.api.model.toLessonRemote
import com.example.myapplication.dataBase.AppContainer
import com.example.myapplication.dataBase.AppDatabase
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.remotekeys.repository.OfflineRemoteKeyRepository
import com.example.myapplication.dataBase.repository.LessonRepository
import com.example.myapplication.dataBase.repository.OfflineLessonRepository
import kotlinx.coroutines.flow.Flow
class RestLessonRepository(
private val service: MyServerService,
private val dbLessonRepository: OfflineLessonRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val dayOfWeekRestRepository: RestDayOfWeekRepository,
private val directionRestRepository: RestDirectionRepository,
private val database: AppDatabase
) : LessonRepository {
override fun getAllLessons(): Flow<PagingData<Lesson>> {
Log.d(RestLessonRepository::class.simpleName, "Get lessons")
val pagingSourceFactory = { dbLessonRepository.getAllLessonsPagingSource() }
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = LessonRemoteMediator(
service,
dbLessonRepository,
dbRemoteKeyRepository,
dayOfWeekRestRepository,
directionRestRepository,
database,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
override suspend fun getLesson(uid: Int): Lesson =
service.getLesson(uid).toLesson()
override suspend fun insertLesson(lesson: Lesson) {
service.createLesson(lesson.toLessonRemote()).toLesson()
}
override suspend fun updateLesson(lesson: Lesson) {
service.updateLesson(lesson.uid, lesson.toLessonRemote()).toLesson()
}
override suspend fun deleteLesson(lesson: Lesson) {
service.deleteLesson(lesson.uid).toLesson()
}
}

View File

@ -0,0 +1,15 @@
package com.example.myapplication.api.model
import com.example.myapplication.dataBase.model.DayOfWeek
import kotlinx.serialization.Serializable
@Serializable
data class DayOfWeekRemote(
val id: Int = 0,
val dayName: String
)
fun DayOfWeekRemote.toDayOfWeek(): DayOfWeek = DayOfWeek(
id,
dayName
)

View File

@ -0,0 +1,15 @@
package com.example.myapplication.api.model
import com.example.myapplication.dataBase.model.Direction
import kotlinx.serialization.Serializable
@Serializable
data class DirectionRemote(
val id: Int = 0,
val directionName: String
)
fun DirectionRemote.toDirection(): Direction = Direction(
id,
directionName
)

View File

@ -0,0 +1,34 @@
package com.example.myapplication.api.model
import com.example.myapplication.dataBase.model.Lesson
import kotlinx.serialization.Serializable
@Serializable
data class LessonRemote(
val id: Int = 0,
val teacher: String = "",
val time: String = "",
val classNumber: Int = 0,
val dayOfWeekId: Int = 0,
val directionId: Int = 0
)
fun LessonRemote.toLesson(): Lesson = Lesson(
id,
teacher,
time,
classNumber,
dayOfWeekId,
directionId,
)
fun Lesson.toLessonRemote(): LessonRemote = LessonRemote(
uid,
teacher,
time,
classNumber,
dayOfWeekId,
directionId
)

View File

@ -0,0 +1,34 @@
package com.example.myapplication.api.model
import com.example.myapplication.dataBase.model.Direction
import com.example.myapplication.dataBase.model.RoleEnum
import com.example.myapplication.dataBase.model.User
import kotlinx.serialization.Serializable
@Serializable
data class UserRemote(
val id: Int=0,
var userName: String="",
var password: String="",
var fio: String="",
val role: RoleEnum,
val lessonId: Int?=0
)
fun UserRemote.toUser(): User = User(
id,
userName,
password,
fio,
role,
lessonId
)
fun User.toUserRemote(): UserRemote = UserRemote(
uid,
userName,
password,
fio,
role,
lessonId
)

View File

@ -0,0 +1,70 @@
package com.example.myapplication.api.user
import android.util.Log
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.day.RestDayOfWeekRepository
import com.example.myapplication.api.direction.RestDirectionRepository
import com.example.myapplication.api.lesson.LessonRemoteMediator
import com.example.myapplication.api.lesson.RestLessonRepository
import com.example.myapplication.api.model.toDayOfWeek
import com.example.myapplication.api.model.toLesson
import com.example.myapplication.api.model.toLessonRemote
import com.example.myapplication.api.model.toUser
import com.example.myapplication.api.model.toUserRemote
import com.example.myapplication.dataBase.AppContainer
import com.example.myapplication.dataBase.AppDatabase
import com.example.myapplication.dataBase.model.DayOfWeek
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.model.User
import com.example.myapplication.dataBase.remotekeys.repository.OfflineRemoteKeyRepository
import com.example.myapplication.dataBase.repository.DayOfWeekRepository
import com.example.myapplication.dataBase.repository.LessonRepository
import com.example.myapplication.dataBase.repository.OfflineDayOfWeekRepository
import com.example.myapplication.dataBase.repository.OfflineUserRepository
import com.example.myapplication.dataBase.repository.UserRepository
import kotlinx.coroutines.flow.Flow
class RestUserRepository(
private val service: MyServerService,
private val dbUserRepository: OfflineUserRepository
) : UserRepository {
override suspend fun getAllUsers(): List<User> {
Log.d(RestUserRepository::class.simpleName, "Get users")
val existUsers = dbUserRepository.getAllUsers().associateBy { it.uid }.toMutableMap()
service.getUsers()
.map { it.toUser() }
.forEach { user ->
val existUser = existUsers[user.uid]
if (existUser == null) {
dbUserRepository.createUser(user)
} else if (existUser != user) {
dbUserRepository.updateUser(user)
}
existUsers[user.uid] = user
}
return existUsers.map { it.value }.sortedBy { it.uid }
}
override suspend fun getUserById(uid: Int?): User? =
uid?.let { service.getUserById(it).toUser() }
override suspend fun createUser(user: User) {
service.createUser(user.toUserRemote()).toUser()
}
override suspend fun updateUser(user: User) {
service.updateUser(user.uid, user.toUserRemote())
dbUserRepository.updateUser(user)
}
override suspend fun deleteUser(user: User) {
user.uid?.let { service.deleteUser(it).toUser() }
}
}

View File

@ -0,0 +1,83 @@
package com.example.myapplication.dataBase
import android.content.Context
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.day.RestDayOfWeekRepository
import com.example.myapplication.api.direction.RestDirectionRepository
import com.example.myapplication.api.lesson.RestLessonRepository
import com.example.myapplication.api.user.RestUserRepository
import com.example.myapplication.dataBase.remotekeys.repository.OfflineRemoteKeyRepository
import com.example.myapplication.dataBase.remotekeys.repository.RemoteKeyRepository
import com.example.myapplication.dataBase.repository.DayOfWeekRepository
import com.example.myapplication.dataBase.repository.DirectionRepository
import com.example.myapplication.dataBase.repository.LessonRepository
import com.example.myapplication.dataBase.repository.OfflineDayOfWeekRepository
import com.example.myapplication.dataBase.repository.OfflineDirectionRepository
import com.example.myapplication.dataBase.repository.OfflineLessonRepository
import com.example.myapplication.dataBase.repository.OfflineUserRepository
import com.example.myapplication.dataBase.repository.UserRepository
interface AppContainer {
// val lessonRepository: LessonRepository
// val directionRepository: DirectionRepository
// val dayRepository: DayOfWeekRepository
val lessonRestRepository: RestLessonRepository
val directionRestRepository: RestDirectionRepository
val dayRestRepository: RestDayOfWeekRepository
val userRestRepository: RestUserRepository
companion object {
const val TIMEOUT = 5000L
const val LIMIT = 10
}
}
class AppDataContainer(private val context: Context) : AppContainer {
private val lessonRepository: OfflineLessonRepository by lazy {
OfflineLessonRepository(AppDatabase.getInstance(context).lessonDao())
}
private val dayRepository: OfflineDayOfWeekRepository by lazy {
OfflineDayOfWeekRepository(AppDatabase.getInstance(context).dayDao())
}
private val directionRepository: OfflineDirectionRepository by lazy {
OfflineDirectionRepository(AppDatabase.getInstance(context).directionDao())
}
private val remoteKeyRepository: OfflineRemoteKeyRepository by lazy {
OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao())
}
private val userRepository: OfflineUserRepository by lazy {
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
}
override val lessonRestRepository: RestLessonRepository by lazy {
RestLessonRepository(
MyServerService.getInstance(),
lessonRepository,
remoteKeyRepository,
dayRestRepository,
directionRestRepository,
AppDatabase.getInstance(context)
)
}
override val directionRestRepository: RestDirectionRepository by lazy {
RestDirectionRepository(
MyServerService.getInstance(),
directionRepository
)
}
override val dayRestRepository: RestDayOfWeekRepository by lazy {
RestDayOfWeekRepository(
MyServerService.getInstance(),
dayRepository
)
}
override val userRestRepository: RestUserRepository by lazy {
RestUserRepository(
MyServerService.getInstance(),
userRepository
)
}
}

View File

@ -0,0 +1,111 @@
package com.example.myapplication.dataBase
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.myapplication.dataBase.dao.DayDao
import com.example.myapplication.dataBase.dao.DirectionDao
import com.example.myapplication.dataBase.dao.LessonDao
import com.example.myapplication.dataBase.dao.UserDao
import com.example.myapplication.dataBase.model.DayOfWeek
import com.example.myapplication.dataBase.model.Direction
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.model.RoleEnum
import com.example.myapplication.dataBase.model.User
import com.example.myapplication.dataBase.remotekeys.dao.RemoteKeysDao
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeys
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Database(entities = [Lesson::class, DayOfWeek::class, Direction::class, User::class, RemoteKeys::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun lessonDao(): LessonDao
abstract fun dayDao(): DayDao
abstract fun directionDao(): DirectionDao
abstract fun userDao(): UserDao
abstract fun remoteKeysDao(): RemoteKeysDao
companion object {
private const val DB_NAME: String = "pmy-dbbbb4"
@Volatile
private var INSTANCE: AppDatabase? = null
private suspend fun populateDatabase() {
INSTANCE?.let { database ->
// Days
val dayDao = database.dayDao()
val day1 = DayOfWeek(1, "Понедельник")
val day2 = DayOfWeek(2, "Вторник")
val day3 = DayOfWeek(3, "Среда")
val day4 = DayOfWeek(4, "Четверг")
val day5 = DayOfWeek(5, "Пятница")
val day6 = DayOfWeek(6, "Суббота")
val day7 = DayOfWeek(7, "Воскресенье")
dayDao.insert(day1)
dayDao.insert(day2)
dayDao.insert(day3)
dayDao.insert(day4)
dayDao.insert(day5)
dayDao.insert(day6)
dayDao.insert(day7)
// Direction
val directionDao = database.directionDao()
val direction1 = Direction(1, "Contemporary")
val direction2 = Direction(2, "K-pop")
val direction3 = Direction(3, "House")
val direction4 = Direction(4, "BreakDance")
val direction5 = Direction(5, "Dancehall")
val direction6 = Direction(6, "Jazz-funk")
directionDao.insert(direction1)
directionDao.insert(direction2)
directionDao.insert(direction3)
directionDao.insert(direction4)
directionDao.insert(direction5)
directionDao.insert(direction6)
// Lessons
val lessonDao = database.lessonDao()
val lesson1 = Lesson(1, "Anna", "9.00", 1, 1, 1)
val lesson2 = Lesson(2, "Elena", "10.00", 2, 1, 2)
val lesson3 = Lesson(3, "Anastasia", "18.00", 3, 2, 3)
val lesson4 = Lesson(4, "Vika", "9.00", 4, 3, 4)
val lesson5 = Lesson(5, "Tatyana", "11.00", 3, 5, 6)
lessonDao.insert(lesson1)
lessonDao.insert(lesson2)
lessonDao.insert(lesson3)
lessonDao.insert(lesson4)
lessonDao.insert(lesson5)
//users
val userDao = database.userDao()
val user1 = User(1, "user1", "1234", "FIO", RoleEnum.Admin, 1)
val user2 = User(2, "user2", "5678", "FIO1", RoleEnum.User, 1)
userDao.insert(user1)
userDao.insert(user2)
}
}
fun getInstance(appContext: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
Room.databaseBuilder(
appContext,
AppDatabase::class.java,
DB_NAME
)
// .addCallback(object : Callback() {
// override fun onCreate(db: SupportSQLiteDatabase) {
// super.onCreate(db)
// CoroutineScope(Dispatchers.IO).launch {
// populateDatabase()
// }
// }
// })
.build()
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,24 @@
package com.example.myapplication.dataBase.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.dataBase.model.DayOfWeek
import kotlinx.coroutines.flow.Flow
@Dao
interface DayDao {
@Query("select * from days")
suspend fun getAll(): List<DayOfWeek>
@Query("select * from days where days.uid = :uid")
fun getDayById(uid: Int): Flow<DayOfWeek>
@Insert
suspend fun insert(day: DayOfWeek)
@Update
suspend fun update(day: DayOfWeek)
@Delete
suspend fun delete(day: DayOfWeek)
}

View File

@ -0,0 +1,23 @@
package com.example.myapplication.dataBase.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.dataBase.model.Direction
import kotlinx.coroutines.flow.Flow
@Dao
interface DirectionDao {
@Query("select * from directions")
suspend fun getAll(): List<Direction>
@Query("select * from directions where directions.uid = :uid")
fun getDirectionById(uid: Int): Flow<Direction>
@Insert
suspend fun insert(direction: Direction)
@Update
suspend fun update(direction: Direction)
@Delete
suspend fun delete(direction: Direction)
}

View File

@ -0,0 +1,28 @@
package com.example.myapplication.dataBase.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.dataBase.model.Lesson
import kotlinx.coroutines.flow.Flow
@Dao
interface LessonDao {
@Query("select * from lessons")
fun getAll(): PagingSource<Int, Lesson>
@Query("select * from lessons where lessons.uid = :uid")
fun getLessonByUid(uid: Int): Flow<Lesson>
@Insert
suspend fun insert(vararg lesson: Lesson)
@Update
suspend fun update(lesson: Lesson)
@Delete
suspend fun delete(lesson: Lesson)
@Query("DELETE FROM lessons")
suspend fun deleteAll()
}

View File

@ -0,0 +1,25 @@
package com.example.myapplication.dataBase.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.dataBase.model.User
import kotlinx.coroutines.flow.Flow
@Dao
interface UserDao {
@Query("select * from users")
suspend fun getAll(): List<User>
@Query("select * from users where users.uid = :uid")
fun getUserById(uid: Int?): Flow<User>
@Insert
suspend fun insert(user: User)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
@Query("SELECT * FROM users WHERE user_name = :email")
suspend fun getUserByLogin(email: String): User
}

View File

@ -0,0 +1,32 @@
package com.example.myapplication.dataBase.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "days")
data class DayOfWeek(
@PrimaryKey(autoGenerate = true)
val uid: Int = 0,
@ColumnInfo(name = "day_name")
val dayName: String
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as DayOfWeek
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid ?: -1
}
companion object {
val DEMO_DAY = DayOfWeek(
0,
"Понедельник"
)
}
}

View File

@ -0,0 +1,32 @@
package com.example.myapplication.dataBase.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "directions")
data class Direction(
@PrimaryKey(autoGenerate = true)
val uid: Int = 0,
@ColumnInfo(name = "direction_name")
val directionName: String
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Direction
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid ?: -1
}
companion object {
val DEMO_DIRECTION = Direction(
0,
"K-pop"
)
}
}

View File

@ -0,0 +1,65 @@
package com.example.myapplication.dataBase.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
tableName = "lessons", foreignKeys = [
ForeignKey(
entity = DayOfWeek::class,
parentColumns = ["uid"],
childColumns = ["day_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT
),
ForeignKey(
entity = Direction::class,
parentColumns = ["uid"],
childColumns = ["direction_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT
)
]
)
data class Lesson(
@PrimaryKey(autoGenerate = true)
val uid: Int = 0,
@ColumnInfo(name = "teacher")
val teacher: String,
@ColumnInfo(name = "time")
val time: String,
@ColumnInfo(name = "classNumber")
val classNumber: Int,
@ColumnInfo(name = "day_id", index = true)
val dayOfWeekId: Int,
@ColumnInfo(name = "direction_id", index = true)
val directionId: Int
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Lesson
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid ?: -1
}
companion object {
fun getLesson(index: Int = 0): Lesson {
return Lesson(
index,
"teacher",
"9.00",
1,
1,
1
)
}
}
}

View File

@ -0,0 +1,6 @@
package com.example.myapplication.dataBase.model
enum class RoleEnum {
Admin,
User
}

View File

@ -0,0 +1,54 @@
package com.example.myapplication.dataBase.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(tableName = "users", foreignKeys = [
ForeignKey(
entity = Lesson::class,
parentColumns = ["uid"],
childColumns = ["lesson_id"],
onDelete = ForeignKey.NO_ACTION,
onUpdate = ForeignKey.NO_ACTION
)])
data class User(
@PrimaryKey(autoGenerate = true)
val uid: Int,
@ColumnInfo(name = "user_name")
var userName: String,
@ColumnInfo(name = "user_password")
var password: String,
@ColumnInfo(name = "user_fio")
var fio: String,
@ColumnInfo(name = "role")
val role: RoleEnum,
@ColumnInfo(name = "lesson_id", index = true)
val lessonId: Int?
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as User
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid?: -1
}
companion object {
fun getUser(index: Int = 0): User {
return User(
index,
"useruseruser",
"user",
"useeeeer",
RoleEnum.User,
1
)
}
}
}

View File

@ -0,0 +1,20 @@
package com.example.myapplication.dataBase.remotekeys.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeyType
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeys
@Dao
interface RemoteKeysDao {
@Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type")
suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(remoteKey: List<RemoteKeys>)
@Query("DELETE FROM remote_keys WHERE type = :type")
suspend fun clearRemoteKeys(type: RemoteKeyType)
}

View File

@ -0,0 +1,26 @@
package com.example.myapplication.dataBase.remotekeys.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import com.example.myapplication.dataBase.model.Lesson
enum class RemoteKeyType(private val type: String) {
LESSON(Lesson::class.simpleName ?: "Lesson");
@TypeConverter
fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value }
@TypeConverter
fun fromRemoteKeyType(value: RemoteKeyType) = value.type
}
@Entity(tableName = "remote_keys")
data class RemoteKeys(
@PrimaryKey val entityId: Int,
@TypeConverters(RemoteKeyType::class)
val type: RemoteKeyType,
val prevKey: Int?,
val nextKey: Int?
)

View File

@ -0,0 +1,16 @@
package com.example.myapplication.dataBase.remotekeys.repository
import com.example.myapplication.dataBase.remotekeys.dao.RemoteKeysDao
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeyType
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeys
class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository {
override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) =
remoteKeysDao.getRemoteKeys(id, type)
override suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>) =
remoteKeysDao.insertAll(remoteKeys)
override suspend fun deleteRemoteKey(type: RemoteKeyType) =
remoteKeysDao.clearRemoteKeys(type)
}

View File

@ -0,0 +1,10 @@
package com.example.myapplication.dataBase.remotekeys.repository
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeyType
import com.example.myapplication.dataBase.remotekeys.model.RemoteKeys
interface RemoteKeyRepository {
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys?
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>)
suspend fun deleteRemoteKey(type: RemoteKeyType)
}

View File

@ -0,0 +1,8 @@
package com.example.myapplication.dataBase.repository
import com.example.myapplication.dataBase.model.DayOfWeek
import kotlinx.coroutines.flow.Flow
interface DayOfWeekRepository {
suspend fun getAllDays(): List<DayOfWeek>
}

View File

@ -0,0 +1,8 @@
package com.example.myapplication.dataBase.repository
import com.example.myapplication.dataBase.model.Direction
import kotlinx.coroutines.flow.Flow
interface DirectionRepository {
suspend fun getAllDirections(): List<Direction>
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication.dataBase.repository
import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.myapplication.dataBase.model.Lesson
import kotlinx.coroutines.flow.Flow
interface LessonRepository {
suspend fun insertLesson(lesson: Lesson)
suspend fun updateLesson(lesson: Lesson)
suspend fun deleteLesson(lesson: Lesson)
fun getAllLessons(): Flow<PagingData<Lesson>>
suspend fun getLesson(uid: Int): Lesson
}

View File

@ -0,0 +1,11 @@
package com.example.myapplication.dataBase.repository
import com.example.myapplication.dataBase.dao.DayDao
import com.example.myapplication.dataBase.model.DayOfWeek
import kotlinx.coroutines.flow.Flow
class OfflineDayOfWeekRepository(private val dayDao: DayDao) : DayOfWeekRepository{
override suspend fun getAllDays(): List<DayOfWeek> = dayDao.getAll()
suspend fun createDayOfWeek(dayOfWeek: DayOfWeek) = dayDao.insert(dayOfWeek)
suspend fun updateDayOfWeek(dayOfWeek: DayOfWeek) = dayDao.update(dayOfWeek)
}

View File

@ -0,0 +1,12 @@
package com.example.myapplication.dataBase.repository
import com.example.myapplication.dataBase.dao.DirectionDao
import com.example.myapplication.dataBase.model.DayOfWeek
import com.example.myapplication.dataBase.model.Direction
import kotlinx.coroutines.flow.Flow
class OfflineDirectionRepository(private val directionDao: DirectionDao) : DirectionRepository {
override suspend fun getAllDirections(): List<Direction> = directionDao.getAll()
suspend fun createDirection(direction: Direction) = directionDao.insert(direction)
suspend fun updateDirection(direction: Direction) = directionDao.update(direction)
}

View File

@ -0,0 +1,30 @@
package com.example.myapplication.dataBase.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.myapplication.dataBase.AppContainer
import com.example.myapplication.dataBase.dao.LessonDao
import com.example.myapplication.dataBase.model.Lesson
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
class OfflineLessonRepository(private val lessonDao: LessonDao) : LessonRepository {
override fun getAllLessons (): Flow<PagingData<Lesson>> = Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = lessonDao::getAll
).flow
fun getAllLessonsPagingSource(): PagingSource<Int, Lesson> = lessonDao.getAll()
override suspend fun insertLesson(lesson: Lesson) = lessonDao.insert(lesson)
override suspend fun updateLesson(lesson: Lesson) = lessonDao.update(lesson)
override suspend fun deleteLesson(lesson: Lesson) = lessonDao.delete(lesson)
suspend fun clearLessons() = lessonDao.deleteAll()
override suspend fun getLesson(uid: Int): Lesson = lessonDao.getLessonByUid(uid).first()
suspend fun insertLessons(lessons: List<Lesson>) =
lessonDao.insert(*lessons.toTypedArray())
}

View File

@ -0,0 +1,18 @@
package com.example.myapplication.dataBase.repository
import com.example.myapplication.dataBase.dao.UserDao
import com.example.myapplication.dataBase.model.User
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
override suspend fun createUser(user: User) = userDao.insert(user)
override suspend fun updateUser(user: User) = userDao.update(user)
override suspend fun deleteUser(user: User) = userDao.delete(user)
override suspend fun getUserById(uid: Int?): User = userDao.getUserById(uid).first()
override suspend fun getAllUsers(): List<User> = userDao.getAll()
}

View File

@ -0,0 +1,12 @@
package com.example.myapplication.dataBase.repository
import com.example.myapplication.dataBase.model.User
import kotlinx.coroutines.flow.Flow
interface UserRepository {
suspend fun createUser(user: User)
suspend fun updateUser(user: User)
suspend fun deleteUser(user: User)
suspend fun getUserById(uid: Int?): User?
suspend fun getAllUsers(): List<User>
}

View File

@ -0,0 +1,67 @@
package com.example.myapplication.ui
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.example.myapplication.R
import com.example.myapplication.ui.theme.PmudemoTheme
@Composable
fun About() {
val localContext = LocalContext.current
val aboutText = localContext.resources.getText(R.string.about_text)
val urlOnClick = {
val openURL = Intent(Intent.ACTION_VIEW)
openURL.data = Uri.parse("https://vk.com/id204968285")
localContext.startActivity(openURL)
}
Column(Modifier.padding(top = 70.dp)) {
AndroidView(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = urlOnClick).padding(start = 70.dp),
factory = { context -> TextView(context) },
update = { it.text = aboutText }
)
Spacer(Modifier.padding(bottom = 10.dp))
Image(
painter = painterResource(id = R.drawable.img_8),
modifier = Modifier
.size(400.dp) // Задайте требуемый размер картинке здесь
.padding(19.dp), // Добавьте отступы, если необходимо,
contentDescription = null
)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun AboutPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
About()
}
}
}

View File

@ -0,0 +1,47 @@
package com.example.myapplication.ui
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.myapplication.LessonApplication
import com.example.myapplication.ui.edit.DayDropDownViewModel
import com.example.myapplication.ui.edit.DirectionDropDownViewModel
import com.example.myapplication.ui.edit.LessonEditViewModel
import com.example.myapplication.ui.lesson.LessonListViewModel
import com.example.myapplication.ui.user.CurrentUserViewModel
import com.example.myapplication.ui.user.EntryViewModel
import com.example.myapplication.ui.user.RegisterViewModel
object AppViewModelProvider {
val Factory = viewModelFactory {
initializer {
LessonListViewModel(lessonApplication().container.lessonRestRepository)
}
initializer {
LessonEditViewModel(
this.createSavedStateHandle(),
lessonApplication().container.lessonRestRepository
)
}
initializer {
DayDropDownViewModel(lessonApplication().container.dayRestRepository)
}
initializer {
DirectionDropDownViewModel(lessonApplication().container.directionRestRepository)
}
initializer {
EntryViewModel(lessonApplication().container.userRestRepository)
}
initializer {
RegisterViewModel(lessonApplication().container.userRestRepository)
}
initializer {
CurrentUserViewModel(lessonApplication().container.userRestRepository)
}
}
}
fun CreationExtras.lessonApplication(): LessonApplication =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as LessonApplication)

View File

@ -0,0 +1,48 @@
package com.example.myapplication.ui.edit
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import com.example.myapplication.dataBase.model.DayOfWeek
import com.example.myapplication.dataBase.repository.DayOfWeekRepository
import kotlinx.coroutines.launch
class DayDropDownViewModel(
private val dayOfWeekRepository: DayOfWeekRepository
) : ViewModel() {
var daysListUiState by mutableStateOf(DayOfWeeksListUiState())
private set
var dayOfWeekUiState by mutableStateOf(DayOfWeekUiState())
private set
init {
viewModelScope.launch {
daysListUiState = DayOfWeeksListUiState(dayOfWeekRepository.getAllDays())
}
}
fun setCurrentDayOfWeek(dayOfWeekId: Int) {
val dayOfWeek: DayOfWeek? =
daysListUiState.dayOfWeekList.firstOrNull { dayOfWeek -> dayOfWeek.uid == dayOfWeekId }
dayOfWeek?.let { updateUiState(it) }
}
fun updateUiState(dayOfWeek: DayOfWeek) {
dayOfWeekUiState = DayOfWeekUiState(
dayOfWeek = dayOfWeek
)
}
}
data class DayOfWeeksListUiState(val dayOfWeekList: List<DayOfWeek> = listOf())
data class DayOfWeekUiState(
val dayOfWeek: DayOfWeek? = null
)
fun DayOfWeek.toUiState() = DayOfWeekUiState(dayOfWeek = DayOfWeek(uid = uid, dayName = dayName))

View File

@ -0,0 +1,47 @@
package com.example.myapplication.ui.edit
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.dataBase.model.Direction
import com.example.myapplication.dataBase.repository.DirectionRepository
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
class DirectionDropDownViewModel(
private val directionRepository: DirectionRepository
) : ViewModel() {
var directionsListUiState by mutableStateOf(DirectionsListUiState())
private set
var directionUiState by mutableStateOf(DirectionUiState())
private set
init {
viewModelScope.launch {
directionsListUiState = DirectionsListUiState(directionRepository.getAllDirections())
}
}
fun setCurrentDirection(directionId: Int) {
val direction: Direction? =
directionsListUiState.directionList.firstOrNull { direction -> direction.uid == directionId }
direction?.let { updateUiState(it) }
}
fun updateUiState(direction: Direction) {
directionUiState = DirectionUiState(
direction = direction
)
}
}
data class DirectionsListUiState(val directionList: List<Direction> = listOf())
data class DirectionUiState(
val direction: Direction? = null
)
fun Direction.toUiState() = DirectionUiState(direction = Direction(uid = uid, directionName = directionName))

View File

@ -0,0 +1,262 @@
package com.example.myapplication.ui.edit
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.R
import com.example.myapplication.dataBase.model.DayOfWeek
import com.example.myapplication.dataBase.model.Direction
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.model.RoleEnum
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.theme.PmudemoTheme
import com.example.myapplication.ui.user.CurrentUserViewModel
import kotlinx.coroutines.launch
@Composable
fun LessonEdit(
navController: NavController,
viewModel: LessonEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
directionViewModel: DirectionDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory),
dayViewModel: DayDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory),
currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
directionViewModel.setCurrentDirection(viewModel.lessonUiState.lessonDetails.directionId)
dayViewModel.setCurrentDayOfWeek(viewModel.lessonUiState.lessonDetails.dayOfWeekId)
var getUser by remember { mutableStateOf(currentUserViewModel.user) }
LessonEdit(
lessonUiState = viewModel.lessonUiState,
directionUiState = directionViewModel.directionUiState,
directionsListUiState = directionViewModel.directionsListUiState,
daysUiState = dayViewModel.dayOfWeekUiState,
daysListUiState = dayViewModel.daysListUiState,
onClick = {
if (getUser?.role == RoleEnum.Admin) {
coroutineScope.launch {
viewModel.saveLesson()
navController.popBackStack()
}
}
},
onUpdate = viewModel::updateUiState,
onDirectionUpdate = directionViewModel::updateUiState,
onDayUpdate = dayViewModel::updateUiState
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun DirectionDropDown(
directionUiState: DirectionUiState,
directionsListUiState: DirectionsListUiState,
onDirectionUpdate: (Direction) -> Unit
) {
var expanded: Boolean by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
modifier = Modifier
.padding(top = 7.dp),
expanded = expanded,
onExpandedChange = {
expanded = !expanded
}
) {
TextField(
value = directionUiState.direction?.directionName
?: stringResource(id = R.string.lesson_direction),
onValueChange = {},
readOnly = true,
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor()
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.White)
.exposedDropdownSize()
) {
directionsListUiState.directionList.forEach { direction ->
DropdownMenuItem(
text = {
Text(text = direction.directionName)
},
onClick = {
onDirectionUpdate(direction)
expanded = false
}
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun DayDropDown(
daysUiState: DayOfWeekUiState,
daysListUiState: DayOfWeeksListUiState,
onDayUpdate: (DayOfWeek) -> Unit
) {
var expanded: Boolean by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
modifier = Modifier
.padding(top = 7.dp),
expanded = expanded,
onExpandedChange = {
expanded = !expanded
}
) {
TextField(
value = daysUiState.dayOfWeek?.dayName
?: stringResource(id = R.string.lesson_day),
onValueChange = {},
readOnly = true,
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor()
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.White)
.exposedDropdownSize()
) {
daysListUiState.dayOfWeekList.forEach { dayOfWeek ->
DropdownMenuItem(
text = {
Text(text = dayOfWeek.dayName)
},
onClick = {
onDayUpdate(dayOfWeek)
expanded = false
}
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LessonEdit(
lessonUiState: LessonUiState,
directionUiState: DirectionUiState,
directionsListUiState: DirectionsListUiState,
daysUiState: DayOfWeekUiState,
daysListUiState: DayOfWeeksListUiState,
onClick: () -> Unit,
onUpdate: (LessonDetails) -> Unit,
onDirectionUpdate: (Direction) -> Unit,
onDayUpdate: (DayOfWeek) -> Unit
) {
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = lessonUiState.lessonDetails.time,
onValueChange = { onUpdate(lessonUiState.lessonDetails.copy(time = it)) },
label = { Text(stringResource(id = R.string.lesson_TimeDate)) },
singleLine = true
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = lessonUiState.lessonDetails.teacher,
onValueChange = { onUpdate(lessonUiState.lessonDetails.copy(teacher = it)) },
label = { Text(stringResource(id = R.string.lesson_teacher)) },
singleLine = true
)
DirectionDropDown(
directionUiState = directionUiState,
directionsListUiState = directionsListUiState,
onDirectionUpdate = {
onUpdate(lessonUiState.lessonDetails.copy(directionId = it.uid))
onDirectionUpdate(it)
}
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = lessonUiState.lessonDetails.classNumber.toString(),
onValueChange = { onUpdate(lessonUiState.lessonDetails.copy(classNumber = it.toIntOrNull() ?: 0)) },
label = { Text(stringResource(id = R.string.lesson_classNumber)) },
singleLine = true
)
DayDropDown(
daysUiState = daysUiState,
daysListUiState = daysListUiState,
onDayUpdate = {
onUpdate(lessonUiState.lessonDetails.copy(dayOfWeekId = it.uid))
onDayUpdate(it)
}
)
Button(
onClick = onClick,
enabled = lessonUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.lesson_save_button))
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun LessonEditPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
LessonEdit(
lessonUiState = Lesson.getLesson().toUiState(true),
daysUiState = DayOfWeek.DEMO_DAY.toUiState(),
directionUiState = Direction.DEMO_DIRECTION.toUiState(),
directionsListUiState = DirectionsListUiState(listOf()),
daysListUiState = DayOfWeeksListUiState(listOf()),
onClick = {},
onUpdate = {},
onDirectionUpdate = {},
onDayUpdate = {}
)
}
}
}

View File

@ -0,0 +1,95 @@
package com.example.myapplication.ui.edit
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.repository.LessonRepository
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
class LessonEditViewModel(
savedStateHandle: SavedStateHandle,
private val lessonRepository: LessonRepository
) : ViewModel() {
var lessonUiState by mutableStateOf(LessonUiState())
private set
private val lessonUid: Int = checkNotNull(savedStateHandle["id"])
init {
viewModelScope.launch {
if (lessonUid > 0) {
lessonUiState = lessonRepository.getLesson(lessonUid)
.toUiState(true)
}
}
}
fun updateUiState(lessonDetails: LessonDetails) {
lessonUiState = LessonUiState(
lessonDetails = lessonDetails,
isEntryValid = validateInput(lessonDetails)
)
}
suspend fun saveLesson() {
if (validateInput()) {
if (lessonUid > 0) {
lessonRepository.updateLesson(lessonUiState.lessonDetails.toLesson(lessonUid))
} else {
lessonRepository.insertLesson(lessonUiState.lessonDetails.toLesson())
}
}
}
private fun validateInput(uiState: LessonDetails = lessonUiState.lessonDetails): Boolean {
return with(uiState) {
teacher.isNotBlank()
&& time.isNotBlank()
&& classNumber > 0
&& directionId > 0
&& dayOfWeekId > 0
}
}
}
data class LessonUiState(
val lessonDetails: LessonDetails = LessonDetails(),
val isEntryValid: Boolean = false
)
data class LessonDetails(
val teacher: String = "",
val time: String = "",
val classNumber: Int = 0,
val directionId: Int = 0,
val dayOfWeekId: Int = 0
)
fun LessonDetails.toLesson(uid: Int = 0): Lesson = Lesson(
uid = uid,
teacher = teacher,
time = time,
classNumber = classNumber,
directionId = directionId,
dayOfWeekId = dayOfWeekId
)
fun Lesson.toDetails(): LessonDetails = LessonDetails(
teacher = teacher,
time = time,
classNumber = classNumber,
directionId = directionId,
dayOfWeekId = dayOfWeekId
)
fun Lesson.toUiState(isEntryValid: Boolean = false): LessonUiState = LessonUiState(
lessonDetails = this.toDetails(),
isEntryValid = isEntryValid
)

View File

@ -0,0 +1,121 @@
package com.example.myapplication.ui.edit
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.R
import com.example.myapplication.dataBase.model.DayOfWeek
import com.example.myapplication.dataBase.model.Direction
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.model.RoleEnum
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.theme.PmudemoTheme
import com.example.myapplication.ui.user.CurrentUserViewModel
import kotlinx.coroutines.launch
@Composable
fun LessonRecord(
navController: NavController,
viewModel: LessonEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
directionViewModel: DirectionDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory),
currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
directionViewModel.setCurrentDirection(viewModel.lessonUiState.lessonDetails.directionId)
var getUser by remember { mutableStateOf(currentUserViewModel.user) }
LessonEdit(
lessonUiState = viewModel.lessonUiState,
directionUiState = directionViewModel.directionUiState,
onClick = {
if (getUser?.role == RoleEnum.Admin) {
coroutineScope.launch {
viewModel.saveLesson()
navController.popBackStack()
}
}
},
onUpdate = viewModel::updateUiState
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LessonEdit(
lessonUiState: LessonUiState,
directionUiState: DirectionUiState,
onClick: () -> Unit,
onUpdate: (LessonDetails) -> Unit
) {
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = lessonUiState.lessonDetails.time,
onValueChange = { onUpdate(lessonUiState.lessonDetails.copy(time = it)) },
label = { Text(stringResource(id = R.string.lesson_TimeDate)) },
singleLine = true,
readOnly = true
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = lessonUiState.lessonDetails.teacher,
onValueChange = { onUpdate(lessonUiState.lessonDetails.copy(teacher = it)) },
label = { Text(stringResource(id = R.string.lesson_teacher)) },
singleLine = true,
readOnly = true
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = directionUiState.direction?.directionName
?: stringResource(id = R.string.lesson_direction),
label = { Text(stringResource(id = R.string.lesson_direction)) },
onValueChange = {},
readOnly = true,
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = lessonUiState.lessonDetails.classNumber.toString(),
onValueChange = { onUpdate(lessonUiState.lessonDetails.copy(classNumber = it.toIntOrNull() ?: 0)) },
label = { Text(stringResource(id = R.string.lesson_classNumber)) },
singleLine = true,
readOnly = true
)
Button(
onClick = onClick,
enabled = lessonUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.lesson_record_button))
}
}
}

View File

@ -0,0 +1,563 @@
package com.example.myapplication.ui.lesson
import android.content.res.Configuration
import android.util.Log
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DismissDirection
import androidx.compose.material3.DismissState
import androidx.compose.material3.DismissValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.SwipeToDismiss
import androidx.compose.material3.Text
import androidx.compose.material3.rememberDismissState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import kotlinx.coroutines.launch
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import androidx.navigation.NavHostController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.myapplication.Enter
import com.example.myapplication.Graph.BottomBarScreen
import com.example.myapplication.R
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.model.RoleEnum
import com.example.myapplication.dataBase.model.User
import com.example.myapplication.ui.AppViewModelProvider
import com.example.myapplication.ui.navigation.Screen
import com.example.myapplication.ui.theme.PmudemoTheme
import com.example.myapplication.ui.user.CurrentUserViewModel
import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LessonList(
navController: NavController,
viewModel: LessonListViewModel = viewModel(factory = AppViewModelProvider.Factory),
currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val lessonListUiState = viewModel.lessonListUiState.collectAsLazyPagingItems()
var getUser by remember { mutableStateOf(currentUserViewModel.user) }
Log.d("List", "getUser: $getUser")
val coroutineScope = rememberCoroutineScope()
val state = rememberScrollState()
LaunchedEffect(Unit) { state.animateScrollTo(500) }
Scaffold(
topBar = {},
floatingActionButton = {
if (getUser?.role == RoleEnum.Admin) {
FloatingActionButton(
onClick = {
val route = Screen.LessonEdit.route.replace("{id}", 0.toString())
navController.navigate(route)
},
modifier = Modifier
.padding(bottom = 80.dp) // Добавляем отступ сверху и снизу
) {
Icon(Icons.Filled.Add, "Добавить")
}
}
}
) { innerPadding ->
LessonList(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
lessonList = lessonListUiState,
onClick = { uid: Int ->
if (getUser?.role == RoleEnum.Admin) {
val route = BottomBarScreen.LessonEdit.route.replace("{id}", uid.toString())
navController.navigate(route)
} else{
val route = BottomBarScreen.LessonRecord.route.replace("{id}", uid.toString())
navController.navigate(route)
}
}
){ lesson: Lesson ->
if (getUser?.role == RoleEnum.Admin) {
coroutineScope.launch {
viewModel.deleteLesson(lesson)
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DismissBackground(dismissState: DismissState) {
val color = when (dismissState.dismissDirection) {
DismissDirection.StartToEnd -> Color.Transparent
DismissDirection.EndToStart -> Color(0xFFFF1744)
null -> Color.Transparent
}
val direction = dismissState.dismissDirection
Row(
modifier = Modifier
.fillMaxSize()
.background(color)
.padding(12.dp, 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
if (direction == DismissDirection.EndToStart) {
Icon(
Icons.Default.Delete,
contentDescription = "delete",
tint = Color.White
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SwipeToDelete(
dismissState: DismissState,
lesson: Lesson,
onClick: (uid: Int) -> Unit
) {
SwipeToDismiss(
modifier = Modifier.zIndex(1f),
state = dismissState,
directions = setOf(
DismissDirection.EndToStart
),
background = {
DismissBackground(dismissState)
},
dismissContent = {
LessonListItem(lesson = lesson,
modifier = Modifier
.padding(vertical = 7.dp)
.clickable { onClick(lesson.uid) })
}
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LessonList(
modifier: Modifier = Modifier,
lessonList: LazyPagingItems<Lesson>,
onClick: (uid: Int) -> Unit,
onSwipe: (lesson: Lesson) -> Unit
) {
val refreshScope = rememberCoroutineScope()
var refreshing by remember { mutableStateOf(false) }
fun refresh() = refreshScope.launch {
refreshing = true
lessonList.refresh()
refreshing = false
}
Column(
modifier = modifier
) {
if (lessonList.itemSnapshotList.isEmpty()) {
Text(
text = stringResource(R.string.lesson_empty_description),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleLarge
)
} else {
val filteredLessons = lessonList.itemSnapshotList.filter { it?.dayOfWeekId == 1 }
if (filteredLessons.isNotEmpty()) {
Text(text = "Понедельник", style = TextStyle(fontSize = 24.sp), textAlign = TextAlign.Center)
}
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = filteredLessons, key = { it?.uid ?: "" }) { lesson ->
var show by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmValueChange = {
if (it == DismissValue.DismissedToStart ||
it == DismissValue.DismissedToEnd
) {
show = false
true
} else false
}, positionalThreshold = { 200.dp.toPx() }
)
AnimatedVisibility(
show, exit = fadeOut(spring())
) {
if (lesson != null) {
SwipeToDelete(
dismissState = dismissState,
lesson = lesson,
onClick = onClick
)
}
}
LaunchedEffect(show) {
if (!show) {
delay(800)
if (lesson != null) {
onSwipe(lesson)
}
}
}
}
}
val filteredLessons1 = lessonList.itemSnapshotList.filter { it?.dayOfWeekId == 2 }
if (filteredLessons1.isNotEmpty()) {
Text(text = "Вторник", style = TextStyle(fontSize = 24.sp))
}
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = filteredLessons1, key = { it?.uid ?: "" }) { lesson ->
var show by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmValueChange = {
if (it == DismissValue.DismissedToStart ||
it == DismissValue.DismissedToEnd
) {
show = false
true
} else false
}, positionalThreshold = { 200.dp.toPx() }
)
AnimatedVisibility(
show, exit = fadeOut(spring())
) {
if (lesson != null) {
SwipeToDelete(
dismissState = dismissState,
lesson = lesson,
onClick = onClick
)
}
}
LaunchedEffect(show) {
if (!show) {
delay(800)
if (lesson != null) {
onSwipe(lesson)
}
}
}
}
}
val filteredLessons2 = lessonList.itemSnapshotList.filter { it?.dayOfWeekId == 3 }
if (filteredLessons2.isNotEmpty()) {
Text(text = "Среда", style = TextStyle(fontSize = 24.sp))
}
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = filteredLessons2, key = { it?.uid ?: "" }) { lesson ->
var show by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmValueChange = {
if (it == DismissValue.DismissedToStart ||
it == DismissValue.DismissedToEnd
) {
show = false
true
} else false
}, positionalThreshold = { 200.dp.toPx() }
)
AnimatedVisibility(
show, exit = fadeOut(spring())
) {
if (lesson != null) {
SwipeToDelete(
dismissState = dismissState,
lesson = lesson,
onClick = onClick
)
}
}
LaunchedEffect(show) {
if (!show) {
delay(800)
if (lesson != null) {
onSwipe(lesson)
}
}
}
}
}
val filteredLessons3 = lessonList.itemSnapshotList.filter { it?.dayOfWeekId == 4 }
if (filteredLessons3.isNotEmpty()) {
Text(text = "Четверг", style = TextStyle(fontSize = 24.sp))
}
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = filteredLessons3, key = { it?.uid ?: "" }) { lesson ->
var show by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmValueChange = {
if (it == DismissValue.DismissedToStart ||
it == DismissValue.DismissedToEnd
) {
show = false
true
} else false
}, positionalThreshold = { 200.dp.toPx() }
)
AnimatedVisibility(
show, exit = fadeOut(spring())
) {
if (lesson != null) {
SwipeToDelete(
dismissState = dismissState,
lesson = lesson,
onClick = onClick
)
}
}
LaunchedEffect(show) {
if (!show) {
delay(800)
if (lesson != null) {
onSwipe(lesson)
}
}
}
}
}
val filteredLessons4 = lessonList.itemSnapshotList.filter { it?.dayOfWeekId == 5 }
if (filteredLessons4.isNotEmpty()) {
Text(text = "Пятница", style = TextStyle(fontSize = 24.sp))
}
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = filteredLessons4, key = { it?.uid ?: "" }) { lesson ->
var show by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmValueChange = {
if (it == DismissValue.DismissedToStart ||
it == DismissValue.DismissedToEnd
) {
show = false
true
} else false
}, positionalThreshold = { 200.dp.toPx() }
)
AnimatedVisibility(
show, exit = fadeOut(spring())
) {
if (lesson != null) {
SwipeToDelete(
dismissState = dismissState,
lesson = lesson,
onClick = onClick
)
}
}
LaunchedEffect(show) {
if (!show) {
delay(800)
if (lesson != null) {
onSwipe(lesson)
}
}
}
}
}
val filteredLessons5 = lessonList.itemSnapshotList.filter { it?.dayOfWeekId == 6 }
if (filteredLessons5.isNotEmpty()) {
Text(text = "Суббота", style = TextStyle(fontSize = 24.sp))
}
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = filteredLessons5, key = { it?.uid ?: "" }) { lesson ->
var show by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmValueChange = {
if (it == DismissValue.DismissedToStart ||
it == DismissValue.DismissedToEnd
) {
show = false
true
} else false
}, positionalThreshold = { 200.dp.toPx() }
)
AnimatedVisibility(
show, exit = fadeOut(spring())
) {
if (lesson != null) {
SwipeToDelete(
dismissState = dismissState,
lesson = lesson,
onClick = onClick
)
}
}
LaunchedEffect(show) {
if (!show) {
delay(800)
if (lesson != null) {
onSwipe(lesson)
}
}
}
}
}
val filteredLessons6 = lessonList.itemSnapshotList.filter { it?.dayOfWeekId == 7 }
if (filteredLessons6.isNotEmpty()) {
Text(text = "Воскресенье", style = TextStyle(fontSize = 24.sp))
}
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = filteredLessons6, key = { it?.uid ?: "" }) { lesson ->
var show by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmValueChange = {
if (it == DismissValue.DismissedToStart ||
it == DismissValue.DismissedToEnd
) {
show = false
true
} else false
}, positionalThreshold = { 200.dp.toPx() }
)
AnimatedVisibility(
show, exit = fadeOut(spring())
) {
if (lesson != null) {
SwipeToDelete(
dismissState = dismissState,
lesson = lesson,
onClick = onClick
)
}
}
LaunchedEffect(show) {
if (!show) {
delay(800)
if (lesson != null) {
onSwipe(lesson)
}
}
}
}
}
}
}
}
@Composable
private fun LessonListItem(
lesson: Lesson, modifier: Modifier = Modifier
) {
var dir = ""
if (lesson.directionId == 3) dir = "Contemporary"
if (lesson.directionId == 1) dir = "K-pop"
if (lesson.directionId == 2) dir = "House"
if (lesson.directionId == 4) dir = "BreakDance"
if (lesson.directionId == 6) dir = "Dancehall"
if (lesson.directionId == 5) dir = "Jazz-funk"
Card(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(0.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column(
modifier = modifier.align(CenterHorizontally)
) {
Text(
text = String.format("%s %s %s", lesson.time, lesson.teacher, dir)
)
}
}
}
//@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
//@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
//@Composable
//fun LessonListPreview() {
// PmudemoTheme {
// Surface(
// color = MaterialTheme.colorScheme.background
// ) {
// LessonList(
// lessonList = (1..20).map { i -> Lesson.getLesson(i) },
// onClick = {}
// ) {}
// }
// }
//}
//
//@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
//@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
//@Composable
//fun LessonEmptyListPreview() {
// PmudemoTheme {
// Surface(
// color = MaterialTheme.colorScheme.background
// ) {
// LessonList(
// lessonList = listOf(),
// onClick = {}
// ) {}
// }
// }
//}

View File

@ -0,0 +1,60 @@
package com.example.myapplication.ui.lesson
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.example.myapplication.dataBase.AppDataContainer
import com.example.myapplication.dataBase.model.Lesson
import com.example.myapplication.dataBase.repository.LessonRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class LessonListViewModel(
private val lessonRepository: LessonRepository
) : ViewModel() {
val lessonListUiState: Flow<PagingData<Lesson>> = lessonRepository.getAllLessons()
suspend fun deleteLesson(lesson: Lesson) {
lessonRepository.deleteLesson(lesson)
}
}
//class LessonListViewModel(private val lessonRepository: LessonRepository): ViewModel() {
// var teacher = mutableStateOf("")
// val classNumber = mutableStateOf("")
// val time = mutableStateOf("")
// val directionId = mutableStateOf("")
// val dayOfWeekId = mutableStateOf("")
// val LessonList = lessonRepository.call().cachedIn(viewModelScope)
// var lesson: Lesson? = null
//
// fun insert() = viewModelScope.launch {
// val lesson = Lesson(
// teacher = teacher.value,
// classNumber = classNumber.value.toInt(),
// time = time.value,
// dayOfWeekId = dayOfWeekId.value.toInt(),
// directionId = directionId.value.toInt()
// )
// lessonRepository.insertLesson(lesson)
// }
//
// fun delete(lesson: Lesson) = viewModelScope.launch {
// lessonRepository.deleteLesson(lesson)
// }
//
// fun getLessonByUid(uid: Int) = viewModelScope.launch {
// lessonRepository.getLessonByUid(uid)
// }
//
// fun update(lesson: Lesson) = viewModelScope.launch {
// lessonRepository.updateLesson(lesson)
// }
//}

View File

@ -0,0 +1,170 @@
package com.example.myapplication.ui.navigation
import android.content.res.Configuration
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.example.myapplication.BrDance
import com.example.myapplication.Contemp
import com.example.myapplication.DirectionView
import com.example.myapplication.Enter
import com.example.myapplication.HomeView
import com.example.myapplication.House
import com.example.myapplication.Kpop
import com.example.myapplication.Profile
import com.example.myapplication.R
import com.example.myapplication.Registration
import com.example.myapplication.ui.About
import com.example.myapplication.ui.edit.LessonEdit
import com.example.myapplication.ui.lesson.LessonList
import com.example.myapplication.ui.theme.PmudemoTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Topbar(
navController: NavHostController,
currentScreen: Screen?
) {
TopAppBar(
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
),
title = {
Text(stringResource(currentScreen?.resourceId ?: R.string.app_name))
},
navigationIcon = {
if (
navController.previousBackStackEntry != null
&& (currentScreen == null || !currentScreen.showInBottomBar)
) {
IconButton(onClick = { navController.navigateUp() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimary
)
}
}
}
)
}
@Composable
fun Navbar(
navController: NavHostController,
currentDestination: NavDestination?,
modifier: Modifier = Modifier
) {
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
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
@Composable
fun Navhost(
navController: NavHostController,
innerPadding: PaddingValues, modifier:
Modifier = Modifier
) {
NavHost(
navController,
startDestination = Screen.HomeView.route,
modifier.padding(innerPadding)
) {
composable(Screen.LessonList.route) { LessonList(navController) }
composable(Screen.About.route) { About() }
composable(Screen.HomeView.route) { HomeView(navController) }
composable(Screen.Registration.route) { Registration(navController) }
composable(Screen.House.route) { House() }
composable(Screen.Contemp.route) { Contemp() }
composable(Screen.BrDance.route) { BrDance() }
composable(Screen.Kpop.route) { Kpop() }
composable(Screen.Enter.route) { Enter(navController) }
composable(Screen.DirectionView.route) { DirectionView(navController) }
composable(Screen.Profile.route) { Profile(navController) }
composable(
Screen.LessonEdit.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) {
LessonEdit(navController)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainNavbar() {
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
val currentScreen = currentDestination?.route?.let { Screen.getItem(it) }
Scaffold(
topBar = {
Topbar(navController, currentScreen)
},
bottomBar = {
if (currentScreen == null || currentScreen.showInBottomBar) {
Navbar(navController, currentDestination)
}
}
) { innerPadding ->
Navhost(navController, innerPadding)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun MainNavbarPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
MainNavbar()
}
}
}

View File

@ -0,0 +1,72 @@
package com.example.myapplication.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.Home
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.myapplication.R
enum class Screen(
val route: String,
@StringRes val resourceId: Int,
val icon: ImageVector = Icons.Filled.Favorite,
val showInBottomBar: Boolean = true
) {
LessonList(
"lesson-list", R.string.home_title, Icons.Filled.DateRange
),
About(
"about", R.string.home_title, Icons.Filled.Info
),
Enter(
"enter", R.string.home_title, Icons.Filled.Face
),
Registration(
"registration", R.string.registration_header, showInBottomBar = false
),
DirectionView(
"directionView", R.string.home_title, Icons.Filled.List
),
House(
"house", R.string.house, showInBottomBar = false
),
Contemp(
"contemp", R.string.contemp, showInBottomBar = false
),
BrDance(
"brdance", R.string.brdance, showInBottomBar = false
),
Kpop(
"kpop", R.string.kpop, showInBottomBar = false
),
HomeView(
"homeView", R.string.home_title, Icons.Filled.Home
),
Profile(
"profile", R.string.home_title, Icons.Filled.Person
),
LessonEdit(
"lesson-edit/{id}", R.string.lesson_view_title, showInBottomBar = false
);
companion object {
val bottomBarItems = listOf(
LessonList,
DirectionView,
HomeView,
Enter,
About
)
fun getItem(route: String): Screen? {
val findRoute = route.split("/").first()
return values().find { value -> value.route.startsWith(findRoute) }
}
}
}

View File

@ -0,0 +1,12 @@
package com.example.myapplication.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Black = Color(0xFF000000)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View File

@ -0,0 +1,73 @@
package com.example.myapplication.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Black,
secondary = PurpleGrey40,
tertiary = Pink40
)
private val LightColorScheme = lightColorScheme(
primary = Black,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun PmudemoTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@ -0,0 +1,34 @@
package com.example.myapplication.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

View File

@ -0,0 +1,28 @@
package com.example.myapplication.ui.user
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.dataBase.model.User
import com.example.myapplication.dataBase.repository.UserRepository
import kotlinx.coroutines.launch
class CurrentUserViewModel(private val userRepository: UserRepository) : ViewModel(){
val argument = mutableStateOf<String?>(null)
private val userid = mutableStateOf<Int?>(null)
var user by mutableStateOf<User?>(null)
fun setArgument(arg: String) {
argument.value = arg
userid.value = arg.toInt()
viewModelScope.launch {
user = userRepository.getUserById(userid.value)
}
}
suspend fun updateUser(user: User) {
userRepository.updateUser(user)
}
}

View File

@ -0,0 +1,20 @@
package com.example.myapplication.ui.user
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.dataBase.model.User
import com.example.myapplication.dataBase.repository.UserRepository
import kotlinx.coroutines.launch
class EntryViewModel(private val userRepository: UserRepository) : ViewModel() {
var userList by mutableStateOf<List<User>>(emptyList())
fun setUserList() {
viewModelScope.launch {
userList=userRepository.getAllUsers()
}
}
}

View File

@ -0,0 +1,25 @@
package com.example.myapplication.ui.user
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.dataBase.model.User
import com.example.myapplication.dataBase.repository.UserRepository
import kotlinx.coroutines.launch
class RegisterViewModel (private val userRepository: UserRepository) : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> get() = _users
fun setUserList() {
viewModelScope.launch {
_users.value = userRepository.getAllUsers()
}
}
suspend fun insertUser(user: User) {
userRepository.createUser(user)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Some files were not shown because too many files have changed in this diff Show More