From 16257cf1b6c56cf1388b9a1faf14e47936dda6e5 Mon Sep 17 00:00:00 2001 From: abazov73 <92822431+abazov73@users.noreply.github.com> Date: Sat, 18 Nov 2023 17:26:57 +0400 Subject: [PATCH] Third lab --- .idea/kotlinc.xml | 2 +- app/build.gradle.kts | 26 +++- .../converters/LocalDateConverter.kt | 24 ++++ .../mobile_labs/database/AppDatabase.kt | 118 ++++++++++++++++++ .../mobile_labs/event/composeui/Schedule.kt | 29 ++++- .../example/mobile_labs/event/dao/EventDao.kt | 32 +++++ .../example/mobile_labs/event/model/Event.kt | 39 +++--- .../event/model/EventWithPerformance.kt | 12 ++ .../performance/composeui/PerformanceView.kt | 25 ++-- .../performance/composeui/Repertoire.kt | 37 ++++-- .../performance/dao/PerformanceDao.kt | 29 +++++ .../performance/dao/PerformancePersonDao.kt | 26 ++++ .../performance/model/Performance.kt | 64 ++++++---- .../model/PerformancePersonCrossRef.kt | 9 ++ .../model/PerformanceWithPeople.kt | 22 ++++ .../person/composeui/PeopleList.kt | 22 +++- .../mobile_labs/person/dao/PersonDao.kt | 27 ++++ .../mobile_labs/person/model/Person.kt | 35 +++--- .../example/mobile_labs/user/dao/UserDao.kt | 28 +++++ .../example/mobile_labs/user/model/User.kt | 26 ++++ build.gradle.kts | 1 + 21 files changed, 550 insertions(+), 83 deletions(-) create mode 100644 app/src/main/java/com/example/mobile_labs/converters/LocalDateConverter.kt create mode 100644 app/src/main/java/com/example/mobile_labs/database/AppDatabase.kt create mode 100644 app/src/main/java/com/example/mobile_labs/event/dao/EventDao.kt create mode 100644 app/src/main/java/com/example/mobile_labs/event/model/EventWithPerformance.kt create mode 100644 app/src/main/java/com/example/mobile_labs/performance/dao/PerformanceDao.kt create mode 100644 app/src/main/java/com/example/mobile_labs/performance/dao/PerformancePersonDao.kt create mode 100644 app/src/main/java/com/example/mobile_labs/performance/model/PerformancePersonCrossRef.kt create mode 100644 app/src/main/java/com/example/mobile_labs/performance/model/PerformanceWithPeople.kt create mode 100644 app/src/main/java/com/example/mobile_labs/person/dao/PersonDao.kt create mode 100644 app/src/main/java/com/example/mobile_labs/user/dao/UserDao.kt create mode 100644 app/src/main/java/com/example/mobile_labs/user/model/User.kt diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 0fc3113..69e8615 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9749697..eae8623 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") + id("com.google.devtools.ksp") } android { @@ -30,17 +31,17 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "17" } buildFeatures { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.4.3" + kotlinCompilerExtensionVersion = "1.4.5" } packaging { resources { @@ -48,11 +49,16 @@ android { } } } +kotlin { + jvmToolchain(17) +} dependencies { - + //Core implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") + + //UI 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") @@ -61,6 +67,16 @@ dependencies { implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") + + // 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") + + //Tests testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/java/com/example/mobile_labs/converters/LocalDateConverter.kt b/app/src/main/java/com/example/mobile_labs/converters/LocalDateConverter.kt new file mode 100644 index 0000000..ff08e73 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/converters/LocalDateConverter.kt @@ -0,0 +1,24 @@ +package com.example.mobile_labs.converters + +import androidx.room.TypeConverter +import java.time.LocalDate +import java.time.LocalDateTime + + + + +class LocalDateConverter { + @TypeConverter + fun toDate(dateString: String?): LocalDate? { + return if (dateString == null) { + null + } else { + LocalDate.parse(dateString) + } + } + + @TypeConverter + fun toDateString(date: LocalDate?): String? { + return date?.toString() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/database/AppDatabase.kt b/app/src/main/java/com/example/mobile_labs/database/AppDatabase.kt new file mode 100644 index 0000000..f10abbd --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/database/AppDatabase.kt @@ -0,0 +1,118 @@ +package com.example.mobile_labs.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.mobile_labs.event.dao.EventDao +import com.example.mobile_labs.event.model.Event +import com.example.mobile_labs.performance.dao.PerformanceDao +import com.example.mobile_labs.performance.dao.PerformancePersonDao +import com.example.mobile_labs.performance.model.Performance +import com.example.mobile_labs.performance.model.PerformancePersonCrossRef +import com.example.mobile_labs.person.dao.PersonDao +import com.example.mobile_labs.person.model.Person +import com.example.mobile_labs.user.dao.UserDao +import com.example.mobile_labs.user.model.User +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.time.LocalDate + +@Database(entities = [Performance::class, Person::class, Event::class, PerformancePersonCrossRef::class, User::class], version = 1, exportSchema = false) +abstract class AppDatabase : RoomDatabase() { + abstract fun personDao(): PersonDao + abstract fun performanceDao(): PerformanceDao + abstract fun eventDao(): EventDao + abstract fun performancePersonDao(): PerformancePersonDao + abstract fun userDao(): UserDao + + companion object { + private const val DB_NAME: String = "theatre-db" + + @Volatile + private var INSTANCE: AppDatabase? = null + + private suspend fun populateDatabase() { + INSTANCE?.let { database -> + //people + val personDao = database.personDao() + val person1 = Person(1, "Иванов", "Иван", "https://bolshoi.ru/media/members/photos/86_ru_twyyatusbnifnzi_300x300_p.jpg") + val person2 = Person(2, "Ксения", "Дудникова", "https://bolshoi.ru/media/members/photos/1756_ru_wkznwzklgosnbiq_300x300_p.jpg") + val person3 = Person(3, "Евгения", "Сегенюк", "https://bolshoi.ru/media/members/photos/2908_ru_rycynrihdpzvgdj_300x300_p.jpg") + val person4 = Person(4, "Петров", "Петр", "https://bolshoi.ru/media/members/photos/15820_ru_zvewuwadyjuywkh_300x300_p.jpg") + + personDao.insert(person1) + personDao.insert(person2) + personDao.insert(person3) + personDao.insert(person4) + + val performanceDao = database.performanceDao() + val performance1 = Performance(1, "Представление 1", "Описание представления 1", 1, 2, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg") + val performance2 = Performance(2, "Представление 2", "Описание представления 2", 2, 3, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg") + val performance3 = Performance(3, "Представление 3", "Описание представления 1", 3, 4, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg") + + performanceDao.insert(performance1) + performanceDao.insert(performance2) + performanceDao.insert(performance3) + + val performancePersonDao = database.performancePersonDao() + val performancePerson1 = PerformancePersonCrossRef(1, 1) + val performancePerson2 = PerformancePersonCrossRef(1, 2) + val performancePerson3 = PerformancePersonCrossRef(1, 3) + val performancePerson4 = PerformancePersonCrossRef(1, 4) + + performancePersonDao.insert(performancePerson1) + performancePersonDao.insert(performancePerson2) + performancePersonDao.insert(performancePerson3) + performancePersonDao.insert(performancePerson4) + + val eventDao = database.eventDao() + val event1 = Event(1, LocalDate.parse("2023-10-23"), 1) + val event2 = Event(2, LocalDate.parse("2023-10-11"), 2) + val event3 = Event(3, LocalDate.parse("2023-10-15"), 3) + val event4 = Event(4, LocalDate.parse("2023-10-27"), 1) + val event5 = Event(5, LocalDate.parse("2023-10-03"), 2) + val event6 = Event(6, LocalDate.parse("2023-10-09"), 3) + val event7 = Event(7, LocalDate.parse("2023-11-03"), 2) + val event8 = Event(8, LocalDate.parse("2023-11-09"), 3) + + eventDao.insert(event1) + eventDao.insert(event2) + eventDao.insert(event3) + eventDao.insert(event4) + eventDao.insert(event5) + eventDao.insert(event6) + eventDao.insert(event7) + eventDao.insert(event8) + + val userDao = database.userDao() + val user1 = User(1, "test@example.com", "1234", "John", "Doe") + val user2 = User(2, "test@example.com", "1234", "John", "Doe") + 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 : RoomDatabase.Callback() { + override fun onCreate(db: SupportSQLiteDatabase) { + super.onCreate(db) + CoroutineScope(Dispatchers.IO).launch { + populateDatabase() + } + } + }) + .build() + .also { INSTANCE = it } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/event/composeui/Schedule.kt b/app/src/main/java/com/example/mobile_labs/event/composeui/Schedule.kt index f384205..c243017 100644 --- a/app/src/main/java/com/example/mobile_labs/event/composeui/Schedule.kt +++ b/app/src/main/java/com/example/mobile_labs/event/composeui/Schedule.kt @@ -15,30 +15,51 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavController import coil.compose.AsyncImage import com.example.mobile_labs.composeui.navigation.Screen -import com.example.mobile_labs.event.model.getTestEvents +import com.example.mobile_labs.database.AppDatabase +import com.example.mobile_labs.event.model.EventWithPerformance +import com.example.mobile_labs.person.model.Person import com.example.mobile_labs.ui.theme.Mobile_LabsTheme +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.time.LocalDate import java.time.format.DateTimeFormatter +import java.util.Calendar @Composable fun Schedule(navController: NavController?) { + val context = LocalContext.current + val events = remember { mutableStateListOf() } + LaunchedEffect(Unit) { + withContext(Dispatchers.IO) { + val dateNow = LocalDate.now().toString() + AppDatabase.getInstance(context).eventDao().getAllWithPerformance(dateNow).collect { data -> + events.clear() + events.addAll(data) + } + } + } Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .padding(all = 10.dp) .verticalScroll(rememberScrollState())) { - getTestEvents().forEachIndexed() { _, event -> - val performanceId = Screen.PerformanceView.route.replace("{id}", event.performance.id.toString()) + events.forEachIndexed() { _, event -> + val performanceId = Screen.PerformanceView.route.replace("{id}", event.performance.performance_uid.toString()) Row(Modifier.padding(all = 10.dp)) { - Text(modifier = Modifier.padding(all = 5.dp), textAlign = TextAlign.Center, text = event.date.format(DateTimeFormatter.ofPattern("dd.MM"))) + Text(modifier = Modifier.padding(all = 5.dp), textAlign = TextAlign.Center, text = event.event.date.format(DateTimeFormatter.ofPattern("dd.MM"))) AsyncImage(model = event.performance.previewImageURL, contentDescription = "performance preview image", contentScale = ContentScale.Crop, diff --git a/app/src/main/java/com/example/mobile_labs/event/dao/EventDao.kt b/app/src/main/java/com/example/mobile_labs/event/dao/EventDao.kt new file mode 100644 index 0000000..6525c85 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/event/dao/EventDao.kt @@ -0,0 +1,32 @@ +package com.example.mobile_labs.event.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import com.example.mobile_labs.event.model.Event +import com.example.mobile_labs.event.model.EventWithPerformance +import com.example.mobile_labs.performance.model.Performance +import kotlinx.coroutines.flow.Flow + +@Dao +interface EventDao { + @Query("select * from events order by date asc") + fun getAll(): Flow> + + @Query("select * from events where events.date > :dateFrom order by date asc") + fun getAllWithPerformance(dateFrom: String): Flow> + + @Query("select * from events where events.uid = :uid") + suspend fun getByUid(uid: Int): EventWithPerformance + + @Insert + suspend fun insert(event: Event) + + @Update + suspend fun update(event: Event) + + @Delete + suspend fun delete(event: Event) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/event/model/Event.kt b/app/src/main/java/com/example/mobile_labs/event/model/Event.kt index ce7c878..ce3a46a 100644 --- a/app/src/main/java/com/example/mobile_labs/event/model/Event.kt +++ b/app/src/main/java/com/example/mobile_labs/event/model/Event.kt @@ -1,27 +1,32 @@ package com.example.mobile_labs.event.model +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey +import androidx.room.TypeConverter +import androidx.room.TypeConverters +import com.example.mobile_labs.converters.LocalDateConverter import com.example.mobile_labs.performance.model.Performance import com.example.mobile_labs.person.model.Person import java.io.Serializable import java.time.LocalDate +@TypeConverters(LocalDateConverter::class) +@Entity(tableName = "events", foreignKeys = [ + ForeignKey( + entity = Performance::class, + parentColumns = ["performance_uid"], + childColumns = ["performance_id"], + onDelete = ForeignKey.RESTRICT, + onUpdate = ForeignKey.RESTRICT, + ), +]) data class Event( + @PrimaryKey(autoGenerate = true) + val uid: Int?, val date: LocalDate, - val performance: Performance, -) : Serializable + @ColumnInfo(name = "performance_id") + val performanceId: Int?, +) -fun getTestEvents(): List { - val director = Person("Иванов", "Иван", "https://bolshoi.ru/media/members/photos/86_ru_twyyatusbnifnzi_300x300_p.jpg"); - val author = Person("Петров", "Петр", "https://bolshoi.ru/media/members/photos/86_ru_twyyatusbnifnzi_300x300_p.jpg"); - val actors = listOf(director, author); - val performances = listOf( - Performance(0,"Представление #1", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(1,"Представление #2", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(2,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - ) - return listOf( - Event(LocalDate.parse("2023-10-15"), performances[0]), - Event(LocalDate.parse("2023-10-16"), performances[1]), - Event(LocalDate.parse("2023-10-17"), performances[2]) - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/event/model/EventWithPerformance.kt b/app/src/main/java/com/example/mobile_labs/event/model/EventWithPerformance.kt new file mode 100644 index 0000000..715e743 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/event/model/EventWithPerformance.kt @@ -0,0 +1,12 @@ +package com.example.mobile_labs.event.model + +import androidx.room.Embedded +import androidx.room.Relation +import com.example.mobile_labs.performance.model.Performance + +data class EventWithPerformance ( + @Embedded + val event: Event, + @Relation(entity = Performance::class, parentColumn = "performance_id", entityColumn = "performance_uid") + val performance: Performance, +) \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/performance/composeui/PerformanceView.kt b/app/src/main/java/com/example/mobile_labs/performance/composeui/PerformanceView.kt index 248eb99..be1f0b9 100644 --- a/app/src/main/java/com/example/mobile_labs/performance/composeui/PerformanceView.kt +++ b/app/src/main/java/com/example/mobile_labs/performance/composeui/PerformanceView.kt @@ -17,6 +17,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -24,6 +25,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -32,16 +34,25 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavController import coil.compose.AsyncImage import com.example.mobile_labs.composeui.navigation.Screen -import com.example.mobile_labs.performance.model.getTestPerformances import com.example.mobile_labs.ui.theme.Mobile_LabsTheme import com.example.mobile_labs.R +import com.example.mobile_labs.database.AppDatabase +import com.example.mobile_labs.performance.model.PerformanceWithPeople +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext @OptIn(ExperimentalMaterial3Api::class) @Composable fun PerformanceView(id: Int) { - var performance = getTestPerformances()[id] + val context = LocalContext.current + val (performaceWithPeople, setPerformanceWithPeople) = remember { mutableStateOf(null) } + LaunchedEffect(Unit) { + withContext(Dispatchers.IO) { + setPerformanceWithPeople(AppDatabase.getInstance(context).performanceDao().getByUid(id)) + } + } var actorsText = "" - performance.actors.forEach { + performaceWithPeople?.actors?.forEach { if (actorsText != "") actorsText += "\n" actorsText += "${it.last_name} ${it.first_name}" } @@ -50,10 +61,10 @@ fun PerformanceView(id: Int) { modifier = Modifier .padding(all = 10.dp) .verticalScroll(rememberScrollState())) { - Text(text = "${performance.author.last_name} ${performance.author.first_name}", textAlign = TextAlign.Center) - Text(text = performance.title, textAlign = TextAlign.Center, fontSize = 30.sp) - AsyncImage(model = performance.imageURL, contentDescription = "performance image") - OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = performance.description, onValueChange = {}, readOnly = true, + Text(text = "${performaceWithPeople?.author?.last_name} ${performaceWithPeople?.author?.first_name}", textAlign = TextAlign.Center) + Text(text = performaceWithPeople?.performance?.title ?: "", textAlign = TextAlign.Center, fontSize = 30.sp) + AsyncImage(model = performaceWithPeople?.performance?.imageURL, contentDescription = "performance image") + OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = performaceWithPeople?.performance?.description ?: "", onValueChange = {}, readOnly = true, label = { Text(stringResource(id = R.string.performance_description)) } diff --git a/app/src/main/java/com/example/mobile_labs/performance/composeui/Repertoire.kt b/app/src/main/java/com/example/mobile_labs/performance/composeui/Repertoire.kt index 6438da9..453bfb2 100644 --- a/app/src/main/java/com/example/mobile_labs/performance/composeui/Repertoire.kt +++ b/app/src/main/java/com/example/mobile_labs/performance/composeui/Repertoire.kt @@ -1,6 +1,7 @@ package com.example.mobile_labs.performance.composeui import android.content.res.Configuration +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -16,6 +17,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf @@ -28,30 +30,51 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavController import coil.compose.AsyncImage import com.example.mobile_labs.composeui.navigation.Screen +import com.example.mobile_labs.database.AppDatabase import com.example.mobile_labs.performance.model.Performance -import com.example.mobile_labs.performance.model.getTestPerformances +import com.example.mobile_labs.person.model.Person import com.example.mobile_labs.ui.theme.Mobile_LabsTheme - -var performances by mutableStateOf(getTestPerformances()) +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext @OptIn(ExperimentalMaterial3Api::class) @Composable fun Repertoire(navController: NavController?) { + val context = LocalContext.current + val performances = remember { mutableStateListOf() } + var performancesFiltered = remember { mutableStateListOf() } var title by remember { mutableStateOf("") } + LaunchedEffect(Unit) { + withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).performanceDao().getAll().collect { data -> + performances.clear() + performances.addAll(data) + performancesFiltered.addAll(data) + } + } + } Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .padding(all = 10.dp) .verticalScroll(rememberScrollState())) { - TextField(value = title, label = {Text(text = "Поиск...")}, onValueChange = { title = it; performances = - if (title != "") getTestPerformances().filter { performance -> performance.title.contains(title) } else getTestPerformances() }) - performances.forEachIndexed() { index, performance -> - val performanceId = Screen.PerformanceView.route.replace("{id}", performance.id.toString()) + TextField(value = title, label = {Text(text = "Поиск...")}, onValueChange = { title = it; + if (title != "") { + val data = performances.filter { performance -> performance.title.contains(title) } + performancesFiltered.clear() + performancesFiltered.addAll(data) + } else { + performancesFiltered.clear() + performancesFiltered.addAll(performances) + } }) + performancesFiltered.forEachIndexed() { index, performance -> + val performanceId = Screen.PerformanceView.route.replace("{id}", performance.performance_uid.toString()) Row(Modifier.padding(all = 10.dp)) { AsyncImage(model = performance.previewImageURL, contentDescription = "performance preview image", diff --git a/app/src/main/java/com/example/mobile_labs/performance/dao/PerformanceDao.kt b/app/src/main/java/com/example/mobile_labs/performance/dao/PerformanceDao.kt new file mode 100644 index 0000000..ff1d8b4 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/performance/dao/PerformanceDao.kt @@ -0,0 +1,29 @@ +package com.example.mobile_labs.performance.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import com.example.mobile_labs.performance.model.Performance +import com.example.mobile_labs.performance.model.PerformanceWithPeople +import com.example.mobile_labs.person.model.Person +import kotlinx.coroutines.flow.Flow + +@Dao +interface PerformanceDao { + @Query("select * from performances order by title collate nocase asc") + fun getAll(): Flow> + + @Query("select * from performances where performances.performance_uid = :uid") + suspend fun getByUid(uid: Int): PerformanceWithPeople + + @Insert + suspend fun insert(performance: Performance) + + @Update + suspend fun update(performance: Performance) + + @Delete + suspend fun delete(performance: Performance) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/performance/dao/PerformancePersonDao.kt b/app/src/main/java/com/example/mobile_labs/performance/dao/PerformancePersonDao.kt new file mode 100644 index 0000000..cf1ee34 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/performance/dao/PerformancePersonDao.kt @@ -0,0 +1,26 @@ +package com.example.mobile_labs.performance.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import com.example.mobile_labs.performance.model.Performance +import com.example.mobile_labs.performance.model.PerformancePersonCrossRef +import com.example.mobile_labs.performance.model.PerformanceWithPeople +import kotlinx.coroutines.flow.Flow + +@Dao +interface PerformancePersonDao { + @Query("select * from performance_person") + fun getAll(): Flow> + + @Insert + suspend fun insert(performancePersonCrossRef: PerformancePersonCrossRef) + + @Update + suspend fun update(performancePersonCrossRef: PerformancePersonCrossRef) + + @Delete + suspend fun delete(performancePersonCrossRef: PerformancePersonCrossRef) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/performance/model/Performance.kt b/app/src/main/java/com/example/mobile_labs/performance/model/Performance.kt index d3b5c34..4d89099 100644 --- a/app/src/main/java/com/example/mobile_labs/performance/model/Performance.kt +++ b/app/src/main/java/com/example/mobile_labs/performance/model/Performance.kt @@ -1,34 +1,52 @@ package com.example.mobile_labs.performance.model +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey import com.example.mobile_labs.person.model.Person import java.io.Serializable +@Entity(tableName = "performances", foreignKeys = [ + ForeignKey( + entity = Person::class, + parentColumns = ["uid"], + childColumns = ["director_id"], + onDelete = ForeignKey.RESTRICT, + onUpdate = ForeignKey.RESTRICT, + ), + ForeignKey( + entity = Person::class, + parentColumns = ["uid"], + childColumns = ["author_id"], + onDelete = ForeignKey.RESTRICT, + onUpdate = ForeignKey.RESTRICT, + ), + ] +) data class Performance( - val id: Int, + @PrimaryKey(autoGenerate = true) + val performance_uid: Int?, val title: String, val description: String, - val author: Person, - val director: Person, - val actors: List, + @ColumnInfo(name = "author_id", index = true) + val authorId: Int?, + @ColumnInfo(name = "director_id", index = true) + val directorId: Int?, + @ColumnInfo(name = "image_url") val imageURL: String, + @ColumnInfo(name = "preview_image_url") val previewImageURL: String, -) : Serializable +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as Performance + if (performance_uid != other.performance_uid) return false + return true + } -fun getTestPerformances(): List { - val director = Person("Иванов", "Иван", "https://bolshoi.ru/media/members/photos/86_ru_twyyatusbnifnzi_300x300_p.jpg"); - val author = Person("Петров", "Петр", "https://bolshoi.ru/media/members/photos/86_ru_twyyatusbnifnzi_300x300_p.jpg"); - val actors = listOf(director, author); - return listOf( - Performance(0,"Представление #1", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(1,"Представление #2", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(2,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(3,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(4,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(5,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(6,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(7,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(8,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(9,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - Performance(10,"Представление #3", "Netus quis congue nascetur ullamcorper nibh, nostra iaculis turpis!", author, director, actors, "https://img.freepik.com/free-photo/empty-stage-with-few-props-red-seats_181624-57595.jpg?w=1380&t=st=1696959739~exp=1696960339~hmac=2107448a1d874f1315b5cf246c71df70dc653707c7c8be4a38581d3b73f842ba", "https://www.theatreinparis.com/uploads/images/article/theatre-de-l-athenee-dr.jpg"), - ) -} \ No newline at end of file + override fun hashCode(): Int { + return performance_uid ?: -1 + } +} diff --git a/app/src/main/java/com/example/mobile_labs/performance/model/PerformancePersonCrossRef.kt b/app/src/main/java/com/example/mobile_labs/performance/model/PerformancePersonCrossRef.kt new file mode 100644 index 0000000..3dcdb40 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/performance/model/PerformancePersonCrossRef.kt @@ -0,0 +1,9 @@ +package com.example.mobile_labs.performance.model + +import androidx.room.Entity + +@Entity(tableName = "performance_person", primaryKeys = ["performance_uid", "uid"]) +data class PerformancePersonCrossRef( + val performance_uid: Int, + val uid: Int, +) \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/performance/model/PerformanceWithPeople.kt b/app/src/main/java/com/example/mobile_labs/performance/model/PerformanceWithPeople.kt new file mode 100644 index 0000000..5ba6849 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/performance/model/PerformanceWithPeople.kt @@ -0,0 +1,22 @@ +package com.example.mobile_labs.performance.model + +import androidx.room.ColumnInfo +import androidx.room.Embedded +import androidx.room.Junction +import androidx.room.Relation +import com.example.mobile_labs.person.model.Person + +data class PerformanceWithPeople ( + @Embedded + val performance: Performance, + @Relation(entity = Person::class, parentColumn = "author_id", entityColumn = "uid") + val author: Person, + @Relation(entity = Person::class, parentColumn = "director_id", entityColumn = "uid") + val director: Person, + @Relation( + parentColumn = "performance_uid", + entityColumn = "uid", + associateBy = Junction(PerformancePersonCrossRef::class), + ) + val actors: List +) \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/person/composeui/PeopleList.kt b/app/src/main/java/com/example/mobile_labs/person/composeui/PeopleList.kt index 87ebb24..343545e 100644 --- a/app/src/main/java/com/example/mobile_labs/person/composeui/PeopleList.kt +++ b/app/src/main/java/com/example/mobile_labs/person/composeui/PeopleList.kt @@ -15,26 +15,42 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavController import coil.compose.AsyncImage import com.example.mobile_labs.composeui.navigation.Screen +import com.example.mobile_labs.database.AppDatabase import com.example.mobile_labs.performance.composeui.Repertoire -import com.example.mobile_labs.performance.model.getTestPerformances -import com.example.mobile_labs.person.model.getTestPerson +import com.example.mobile_labs.person.model.Person import com.example.mobile_labs.ui.theme.Mobile_LabsTheme +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext @Composable fun PeopleList(navController: NavController?) { + val context = LocalContext.current + val people = remember { mutableStateListOf() } + LaunchedEffect(Unit) { + withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).personDao().getAll().collect { data -> + people.clear() + people.addAll(data) + } + } + } Column(Modifier.padding(all = 10.dp)) { LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 100.dp)) { - items(getTestPerson()) { + items(people) { Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .width(300.dp) .height(200.dp)) { diff --git a/app/src/main/java/com/example/mobile_labs/person/dao/PersonDao.kt b/app/src/main/java/com/example/mobile_labs/person/dao/PersonDao.kt new file mode 100644 index 0000000..e7d0962 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/person/dao/PersonDao.kt @@ -0,0 +1,27 @@ +package com.example.mobile_labs.person.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import com.example.mobile_labs.person.model.Person +import kotlinx.coroutines.flow.Flow + +@Dao +interface PersonDao { + @Query("select * from people order by last_name collate nocase asc") + fun getAll(): Flow> + + @Query("select * from people where people.uid = :uid") + suspend fun getByUid(uid: Int): Person + + @Insert + suspend fun insert(person: Person) + + @Update + suspend fun update(person: Person) + + @Delete + suspend fun delete(person: Person) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/person/model/Person.kt b/app/src/main/java/com/example/mobile_labs/person/model/Person.kt index c2694e5..32934cf 100644 --- a/app/src/main/java/com/example/mobile_labs/person/model/Person.kt +++ b/app/src/main/java/com/example/mobile_labs/person/model/Person.kt @@ -1,26 +1,29 @@ package com.example.mobile_labs.person.model +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey import java.io.Serializable +@Entity(tableName = "people") data class Person( + @PrimaryKey(autoGenerate = true) + val uid: Int?, val last_name: String, val first_name: String, + @ColumnInfo(name = "image_url") val imageURL: String, -) : Serializable +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as Person + if (uid != other.uid) return false + return true + } -fun getTestPerson(): List { - return listOf( - Person("Иванов", "Иван", "https://bolshoi.ru/media/members/photos/86_ru_twyyatusbnifnzi_300x300_p.jpg"), - Person("Ксения", "Дудникова", "https://bolshoi.ru/media/members/photos/1756_ru_wkznwzklgosnbiq_300x300_p.jpg"), - Person("Евгения", "Сегенюк", "https://bolshoi.ru/media/members/photos/2908_ru_rycynrihdpzvgdj_300x300_p.jpg"), - Person("Петров", "Петр", "https://bolshoi.ru/media/members/photos/15820_ru_zvewuwadyjuywkh_300x300_p.jpg"), - Person("Иванов", "Иван", "https://bolshoi.ru/media/members/photos/86_ru_twyyatusbnifnzi_300x300_p.jpg"), - Person("Ксения", "Дудникова", "https://bolshoi.ru/media/members/photos/1756_ru_wkznwzklgosnbiq_300x300_p.jpg"), - Person("Евгения", "Сегенюк", "https://bolshoi.ru/media/members/photos/2908_ru_rycynrihdpzvgdj_300x300_p.jpg"), - Person("Петров", "Петр", "https://bolshoi.ru/media/members/photos/15820_ru_zvewuwadyjuywkh_300x300_p.jpg"), - Person("Иванов", "Иван", "https://bolshoi.ru/media/members/photos/86_ru_twyyatusbnifnzi_300x300_p.jpg"), - Person("Ксения", "Дудникова", "https://bolshoi.ru/media/members/photos/1756_ru_wkznwzklgosnbiq_300x300_p.jpg"), - Person("Евгения", "Сегенюк", "https://bolshoi.ru/media/members/photos/2908_ru_rycynrihdpzvgdj_300x300_p.jpg"), - Person("Петров", "Петр", "https://bolshoi.ru/media/members/photos/15820_ru_zvewuwadyjuywkh_300x300_p.jpg"), - ) + override fun hashCode(): Int { + return uid ?: -1 + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/user/dao/UserDao.kt b/app/src/main/java/com/example/mobile_labs/user/dao/UserDao.kt new file mode 100644 index 0000000..9529bc9 --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/user/dao/UserDao.kt @@ -0,0 +1,28 @@ +package com.example.mobile_labs.user.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import com.example.mobile_labs.person.model.Person +import com.example.mobile_labs.user.model.User +import kotlinx.coroutines.flow.Flow + +@Dao +interface UserDao { + @Query("select * from users order by last_name collate nocase asc") + fun getAll(): Flow> + + @Query("select * from users where users.uid = :uid") + suspend fun getByUid(uid: Int): User + + @Insert + suspend fun insert(user: User) + + @Update + suspend fun update(user: User) + + @Delete + suspend fun delete(user: User) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mobile_labs/user/model/User.kt b/app/src/main/java/com/example/mobile_labs/user/model/User.kt new file mode 100644 index 0000000..5db216b --- /dev/null +++ b/app/src/main/java/com/example/mobile_labs/user/model/User.kt @@ -0,0 +1,26 @@ +package com.example.mobile_labs.user.model + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "users") +data class User( + @PrimaryKey(autoGenerate = true) + val uid: Int?, + val email: String, + val password: String, + val last_name: String, + val first_name: String, +) { + 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 + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 9861456..bd1eb8a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,4 +2,5 @@ plugins { id("com.android.application") version "8.1.1" apply false id("org.jetbrains.kotlin.android") version "1.8.10" apply false + id("com.google.devtools.ksp") version "1.8.20-1.0.11" apply false } \ No newline at end of file