From 3c65ccddb1af421d624d27dfd2057c377cefa623 Mon Sep 17 00:00:00 2001 From: denizk0461 Date: Sat, 8 Apr 2023 17:30:05 +0200 Subject: [PATCH] Added database persistence ability --- app/build.gradle | 4 +++ .../studip/activity/MainActivity.kt | 5 ++++ .../studip/adapter/StudIPEventAdapter.kt | 23 ++++---------- .../studip/adapter/StudIPEventPageAdapter.kt | 24 +-------------- .../denizk0461/studip/data/Dependencies.kt | 7 +++++ .../com/denizk0461/studip/data/DummyData.kt | 14 ++++----- .../com/denizk0461/studip/data/StudIPEvent.kt | 29 +++++++++++++----- .../java/com/denizk0461/studip/db/EventDAO.kt | 20 +++++++++++++ .../com/denizk0461/studip/db/EventDatabase.kt | 30 +++++++++++++++++++ .../denizk0461/studip/db/EventRepository.kt | 15 ++++++++++ .../{FirstFragment.kt => EventFragment.kt} | 29 +++++++++++------- .../studip/viewmodel/EventViewModel.kt | 13 ++++++++ .../studip/viewmodel/TemplateViewModel.kt | 25 ++++++++++++++++ ...{fragment_first.xml => fragment_event.xml} | 2 +- app/src/main/res/navigation/nav_graph.xml | 4 +-- 15 files changed, 176 insertions(+), 68 deletions(-) create mode 100644 app/src/main/java/com/denizk0461/studip/data/Dependencies.kt create mode 100644 app/src/main/java/com/denizk0461/studip/db/EventDAO.kt create mode 100644 app/src/main/java/com/denizk0461/studip/db/EventDatabase.kt create mode 100644 app/src/main/java/com/denizk0461/studip/db/EventRepository.kt rename app/src/main/java/com/denizk0461/studip/fragment/{FirstFragment.kt => EventFragment.kt} (55%) create mode 100644 app/src/main/java/com/denizk0461/studip/viewmodel/EventViewModel.kt create mode 100644 app/src/main/java/com/denizk0461/studip/viewmodel/TemplateViewModel.kt rename app/src/main/res/layout/{fragment_first.xml => fragment_event.xml} (93%) diff --git a/app/build.gradle b/app/build.gradle index 2c024c4..7926703 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' } android { @@ -47,4 +48,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + + implementation "androidx.room:room-runtime:2.5.1" + kapt "androidx.room:room-compiler:2.5.1" } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/activity/MainActivity.kt b/app/src/main/java/com/denizk0461/studip/activity/MainActivity.kt index aee2a14..baaf0b6 100644 --- a/app/src/main/java/com/denizk0461/studip/activity/MainActivity.kt +++ b/app/src/main/java/com/denizk0461/studip/activity/MainActivity.kt @@ -11,7 +11,9 @@ import androidx.navigation.ui.setupActionBarWithNavController import android.view.Menu import android.view.MenuItem import com.denizk0461.studip.R +import com.denizk0461.studip.data.Dependencies import com.denizk0461.studip.databinding.ActivityMainBinding +import com.denizk0461.studip.db.EventRepository class MainActivity : AppCompatActivity() { @@ -22,9 +24,12 @@ class MainActivity : AppCompatActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) + Dependencies.repo = EventRepository(application) + binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + setSupportActionBar(binding.toolbar) val navController = findNavController(R.id.nav_host_fragment_content_main) diff --git a/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventAdapter.kt b/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventAdapter.kt index f9c23ac..d29398a 100644 --- a/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventAdapter.kt +++ b/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventAdapter.kt @@ -1,12 +1,10 @@ package com.denizk0461.studip.adapter -import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.denizk0461.studip.data.StudIPEvent import com.denizk0461.studip.databinding.ItemEventBinding -import com.denizk0461.studip.databinding.ItemEventPageBinding class StudIPEventAdapter(private val events: List) : RecyclerView.Adapter() { @@ -24,24 +22,13 @@ class StudIPEventAdapter(private val events: List) : RecyclerView.A override fun onBindViewHolder(holder: EventViewHolder, position: Int) { val currentItem = events[position] -// var itemCount = 0 -// events.filter { it.day == position }.forEach { event -> -// val newView = ItemEventBinding.inflate( -// LayoutInflater.from(holder.binding.root.context), -// holder.binding.root, -// false -// ).also { item -> -// Log.d("HELLO", "generating ${event.title}...2") -// itemCount += 1 + holder.binding.textTitle.text = currentItem.title - holder.binding.textLecturers.text = currentItem.lecturer.joinToString(", ") + holder.binding.textLecturers.text = currentItem.lecturer holder.binding.textRoom.text = currentItem.room holder.binding.textTimeslot.text = currentItem.timeslot -// } -// holder.binding.pageLayout.addView(newView.root) -// } -// if (itemCount == 0) { -// // TODO inflate view saying "no events!" or sth -// } + + // TODO inflate view saying "no events!" or sth + } } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventPageAdapter.kt b/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventPageAdapter.kt index cba0cbe..9d30e8b 100644 --- a/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventPageAdapter.kt +++ b/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventPageAdapter.kt @@ -1,13 +1,10 @@ package com.denizk0461.studip.adapter -import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup -import android.widget.LinearLayout import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.denizk0461.studip.data.StudIPEvent -import com.denizk0461.studip.databinding.ItemEventBinding import com.denizk0461.studip.databinding.ItemEventPageBinding class StudIPEventPageAdapter(private val events: List) : RecyclerView.Adapter() { @@ -29,26 +26,7 @@ class StudIPEventPageAdapter(private val events: List) : RecyclerVi holder.binding.pageRecyclerView.apply { layoutManager = LinearLayoutManager(holder.binding.root.context, LinearLayoutManager.VERTICAL, false) adapter = StudIPEventAdapter(events.filter { it.day == position }) // TODO check if empty + scheduleLayoutAnimation() } - -// var itemCount = 0 -// events.filter { it.day == position }.forEach { event -> -// val newView = ItemEventBinding.inflate( -// LayoutInflater.from(holder.binding.root.context), -// holder.binding.root, -// false -// ).also { item -> -// Log.d("HELLO", "generating ${event.title}...2") -// itemCount += 1 -// item.textTitle.text = event.title -// item.textLecturers.text = event.lecturer.joinToString(", ") -// item.textRoom.text = event.room -// item.textTimeslot.text = event.timeslot -// } -// holder.binding.pageLayout.addView(newView.root) -// } -// if (itemCount == 0) { -// // TODO inflate view saying "no events!" or sth -// } } } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/data/Dependencies.kt b/app/src/main/java/com/denizk0461/studip/data/Dependencies.kt new file mode 100644 index 0000000..3bfaa3d --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/data/Dependencies.kt @@ -0,0 +1,7 @@ +package com.denizk0461.studip.data + +import com.denizk0461.studip.db.EventRepository + +object Dependencies { + lateinit var repo: EventRepository +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/data/DummyData.kt b/app/src/main/java/com/denizk0461/studip/data/DummyData.kt index e4cac7a..74ed152 100644 --- a/app/src/main/java/com/denizk0461/studip/data/DummyData.kt +++ b/app/src/main/java/com/denizk0461/studip/data/DummyData.kt @@ -3,12 +3,12 @@ package com.denizk0461.studip.data object DummyData { val events: Array = arrayOf( - StudIPEvent("Literatures", arrayOf("Nittel"), "MZH 1380/1400", 0, 3, 1), - StudIPEvent("Linguistics", arrayOf("Du Bois"), "SFG 1020", 1, 1, 1), - StudIPEvent("System Erde", arrayOf("Zolitschka", "Labuhn-Deroubaix"), "GW2 B1410", 1, 3, 1), - StudIPEvent("Key Moments", arrayOf("Esders-Angermund"), "GW2 B2890", 2, 2, 1), - StudIPEvent("ULS-1", arrayOf("Herrmann"), "GW2 B2890", 2, 3, 1), - StudIPEvent("Gesellschaft und Raum", arrayOf("Lossau", "Mossig"), "GW2 B1820", 3, 1, 1), - StudIPEvent("Gesellschaft und Raum2", arrayOf("Lossau", "Mossig"), "GW2 B1820", 4, 4, 2), + StudIPEvent(1, "Literatures", "Nittel", "MZH 1380/1400", 0, 3, 1), + StudIPEvent(2, "Linguistics", "Du Bois", "SFG 1020", 1, 1, 1), + StudIPEvent(3, "System Erde", "Zolitschka, Labuhn-Deroubaix", "GW2 B1410", 1, 3, 1), + StudIPEvent(4, "Key Moments", "Esders-Angermund", "GW2 B2890", 2, 2, 1), + StudIPEvent(5, "ULS-1", "Herrmann", "GW2 B2890", 2, 3, 1), + StudIPEvent(6, "Gesellschaft und Raum", "Lossau, Mossig", "GW2 B1820", 3, 1, 1), + StudIPEvent(7, "Gesellschaft und Raum2", "Lossau, Mossig", "GW2 B1820", 4, 4, 2), ) } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/data/StudIPEvent.kt b/app/src/main/java/com/denizk0461/studip/data/StudIPEvent.kt index eaba535..fe244e9 100644 --- a/app/src/main/java/com/denizk0461/studip/data/StudIPEvent.kt +++ b/app/src/main/java/com/denizk0461/studip/data/StudIPEvent.kt @@ -1,18 +1,24 @@ package com.denizk0461.studip.data +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey + +@Entity(tableName = "events") data class StudIPEvent( + @PrimaryKey val id: Int, val title: String, // the name of the event - val lecturer: Array, // the lecturer(s) - val room: String, // the room the event takes place in; assumes that the room doesn't change + val lecturer: String, // the lecturer(s) + val room: String, // the room the event takes place in val day: Int, // 0 = Monday, 1 = Tuesday, 2 = Wednesday, 3 = Thursday, 4 = Friday val timeslotStart: Int, // 0 = 08:15, 1 = 10:15, 2 = 12:15, 3 = 14:15, 4 = 16:15, 5 = 18:15 val timeslots: Int, // the amount of timeslots the course takes place over (1 timeslot = 90min) ) { - private val timeSlotStartTimes: List = listOf("08:15", "10:15", "12:15", "14:15", "16:15", "18:15") - private val timeSlotEndTimes: List = listOf("09:45", "11:45", "13:45", "15:45", "17:45", "19:45") + @Ignore private val timeSlotStartTimes: List = listOf("08:15", "10:15", "12:15", "14:15", "16:15", "18:15") + @Ignore private val timeSlotEndTimes: List = listOf("09:45", "11:45", "13:45", "15:45", "17:45", "19:45") - val timeslot: String = + @Ignore val timeslot: String = "${timeSlotStartTimes[timeslotStart]} – ${timeSlotEndTimes[timeslotStart + timeslots - 1]}" override fun equals(other: Any?): Boolean { @@ -26,9 +32,18 @@ data class StudIPEvent( return true } + override fun hashCode(): Int { - var result = title.hashCode() - result = 31 * result + lecturer.contentHashCode() + var result = id + result = 31 * result + title.hashCode() + result = 31 * result + lecturer.hashCode() + result = 31 * result + room.hashCode() + result = 31 * result + day + result = 31 * result + timeslotStart + result = 31 * result + timeslots + result = 31 * result + timeSlotStartTimes.hashCode() + result = 31 * result + timeSlotEndTimes.hashCode() + result = 31 * result + timeslot.hashCode() return result } } diff --git a/app/src/main/java/com/denizk0461/studip/db/EventDAO.kt b/app/src/main/java/com/denizk0461/studip/db/EventDAO.kt new file mode 100644 index 0000000..4a570b3 --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/db/EventDAO.kt @@ -0,0 +1,20 @@ +package com.denizk0461.studip.db + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import com.denizk0461.studip.data.StudIPEvent + +@Dao +interface EventDAO { + + @get:Query("SELECT * FROM events ORDER BY id") + val allEvents: LiveData> + + @Insert + fun insertEvent(event: StudIPEvent) + + @Query("DELETE FROM events") + fun nukeEvents() +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/db/EventDatabase.kt b/app/src/main/java/com/denizk0461/studip/db/EventDatabase.kt new file mode 100644 index 0000000..8c14563 --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/db/EventDatabase.kt @@ -0,0 +1,30 @@ +package com.denizk0461.studip.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import com.denizk0461.studip.data.StudIPEvent + +@Database(entities = [StudIPEvent::class], version = 1) +abstract class EventDatabase : RoomDatabase() { + + abstract fun dao(): EventDAO + + companion object { + private var instance: EventDatabase? = null + + fun getInstance(context: Context): EventDatabase { + if (instance == null) { + synchronized(EventDatabase::class) { + instance = Room.databaseBuilder( + context.applicationContext, + EventDatabase::class.java, + "event_db" + ).build() + } + } + return instance!! + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/db/EventRepository.kt b/app/src/main/java/com/denizk0461/studip/db/EventRepository.kt new file mode 100644 index 0000000..e03705d --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/db/EventRepository.kt @@ -0,0 +1,15 @@ +package com.denizk0461.studip.db + +import android.app.Application +import androidx.lifecycle.LiveData +import com.denizk0461.studip.data.StudIPEvent + +class EventRepository(app: Application) { + + private val dao: EventDAO = EventDatabase.getInstance(app.applicationContext).dao() + + val allEvents: LiveData> = dao.allEvents + + fun nukeEvents() { dao.nukeEvents() } + fun insertEvent(event: StudIPEvent) { dao.insertEvent(event) } +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/fragment/FirstFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt similarity index 55% rename from app/src/main/java/com/denizk0461/studip/fragment/FirstFragment.kt rename to app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt index 61f9f5e..d03a966 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/FirstFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt @@ -5,18 +5,21 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.PagerSnapHelper import com.denizk0461.studip.adapter.StudIPEventPageAdapter import com.denizk0461.studip.data.DummyData -import com.denizk0461.studip.databinding.FragmentFirstBinding +import com.denizk0461.studip.databinding.FragmentEventBinding +import com.denizk0461.studip.viewmodel.EventViewModel /** * A simple [Fragment] subclass as the default destination in the navigation. */ -class FirstFragment : Fragment() { +class EventFragment : Fragment() { - private var _binding: FragmentFirstBinding? = null + private var _binding: FragmentEventBinding? = null // This property is only valid between onCreateView and // onDestroyView. @@ -24,19 +27,25 @@ class FirstFragment : Fragment() { private lateinit var recyclerViewAdapter: StudIPEventPageAdapter private lateinit var recyclerViewLayoutManager: LinearLayoutManager - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - _binding = FragmentFirstBinding.inflate(inflater, container, false) + private val viewModel: EventViewModel by viewModels() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = FragmentEventBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - recyclerViewAdapter = StudIPEventPageAdapter(DummyData.events.toList()) - binding.recyclerView.adapter = recyclerViewAdapter - recyclerViewLayoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - binding.recyclerView.layoutManager = recyclerViewLayoutManager - PagerSnapHelper().attachToRecyclerView(binding.recyclerView) + viewModel.allEvents.observe(viewLifecycleOwner) { events -> + recyclerViewAdapter = StudIPEventPageAdapter(DummyData.events.toList()) + binding.recyclerView.adapter = recyclerViewAdapter + recyclerViewLayoutManager = + LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + binding.recyclerView.layoutManager = recyclerViewLayoutManager + PagerSnapHelper().attachToRecyclerView(binding.recyclerView) + binding.recyclerView.scheduleLayoutAnimation() + } // binding.buttonFirst.setOnClickListener { // findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment) diff --git a/app/src/main/java/com/denizk0461/studip/viewmodel/EventViewModel.kt b/app/src/main/java/com/denizk0461/studip/viewmodel/EventViewModel.kt new file mode 100644 index 0000000..6798ca3 --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/viewmodel/EventViewModel.kt @@ -0,0 +1,13 @@ +package com.denizk0461.studip.viewmodel + +import android.app.Application +import androidx.lifecycle.LiveData +import com.denizk0461.studip.data.StudIPEvent + +class EventViewModel(app: Application) : TemplateViewModel(app) { + + val allEvents: LiveData> = repo.allEvents + + fun insertEvent(event: StudIPEvent) { repo.insertEvent(event) } + fun nukeEvents() { repo.nukeEvents() } +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/viewmodel/TemplateViewModel.kt b/app/src/main/java/com/denizk0461/studip/viewmodel/TemplateViewModel.kt new file mode 100644 index 0000000..900f245 --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/viewmodel/TemplateViewModel.kt @@ -0,0 +1,25 @@ +package com.denizk0461.studip.viewmodel + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import com.denizk0461.studip.data.Dependencies +import com.denizk0461.studip.db.EventRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +open class TemplateViewModel(app: Application) : AndroidViewModel(app) { + + protected val repo: EventRepository = Dependencies.repo + + fun doAsync(function: () -> Unit) { + viewModelScope.launch(Dispatchers.IO) { + function() + } + } + + fun returnBlocking(function: () -> T): T = runBlocking(Dispatchers.IO) { + function() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_first.xml b/app/src/main/res/layout/fragment_event.xml similarity index 93% rename from app/src/main/res/layout/fragment_first.xml rename to app/src/main/res/layout/fragment_event.xml index 0dac67d..8a140a7 100644 --- a/app/src/main/res/layout/fragment_first.xml +++ b/app/src/main/res/layout/fragment_event.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".fragment.FirstFragment"> + tools:context=".fragment.EventFragment"> + tools:layout="@layout/fragment_event">