From ce7b854e8cd9e1c0f8dc731a74c98b877733ee5f Mon Sep 17 00:00:00 2001 From: denizk0461 Date: Thu, 27 Apr 2023 21:58:10 +0200 Subject: [PATCH] StwParser.kt now collects all its data before adding everything to the database all at once, fixing problems where LiveData would update before all transactions were finished; ViewPagers have been updated to use Adapters extending FragmentStateAdapter instead of RecyclerView.Adapter --- .idea/inspectionProfiles/Project_Default.xml | 2 +- .../studip/adapter/CanteenOfferItemAdapter.kt | 9 ++ .../studip/adapter/CanteenOfferPageAdapter.kt | 62 +++-------- .../studip/adapter/StudIPEventPageAdapter.kt | 54 ++------- .../com/denizk0461/studip/data/StwParser.kt | 52 ++++++--- .../java/com/denizk0461/studip/db/AppDAO.kt | 34 +++++- .../com/denizk0461/studip/db/AppRepository.kt | 32 +++++- .../studip/fragment/CanteenFragment.kt | 11 +- .../studip/fragment/CanteenPageFragment.kt | 71 ++++++++++++ .../studip/fragment/EventFragment.kt | 104 +++++++++++++----- .../studip/fragment/EventPageFragment.kt | 60 ++++++++++ .../main/res/layout/item_scrollable_page.xml | 16 --- app/src/main/res/layout/recycler_view.xml | 7 ++ app/src/main/res/values-de/strings.xml | 13 ++- app/src/main/res/values/strings.xml | 10 ++ 15 files changed, 373 insertions(+), 164 deletions(-) create mode 100644 app/src/main/java/com/denizk0461/studip/fragment/CanteenPageFragment.kt create mode 100644 app/src/main/java/com/denizk0461/studip/fragment/EventPageFragment.kt delete mode 100644 app/src/main/res/layout/item_scrollable_page.xml create mode 100644 app/src/main/res/layout/recycler_view.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index f7c3b50..8aac9cd 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -8,10 +8,10 @@ + - diff --git a/app/src/main/java/com/denizk0461/studip/adapter/CanteenOfferItemAdapter.kt b/app/src/main/java/com/denizk0461/studip/adapter/CanteenOfferItemAdapter.kt index 2039729..bf2c9ba 100644 --- a/app/src/main/java/com/denizk0461/studip/adapter/CanteenOfferItemAdapter.kt +++ b/app/src/main/java/com/denizk0461/studip/adapter/CanteenOfferItemAdapter.kt @@ -49,6 +49,15 @@ class CanteenOfferItemAdapter( // Retrieve item for current position val currentItem = offers[position] + /* + * Children of the line container MUST be removed before any are added, as otherwise, during + * recycling, lines will be duplicated when they are scrolled off the visible screen area + * and then scrolled onto the screen again. In other terms, if removeAllViews() wasn't + * called and the user was to scroll down a list and then back up again, the items at the + * top of the list would be added again, creating duplicates. + */ + holder.binding.lineContainer.removeAllViews() + // If an item has been added to mark that there are no offers available, tell the user if (currentItem.category == "NO\$ITEMS") { diff --git a/app/src/main/java/com/denizk0461/studip/adapter/CanteenOfferPageAdapter.kt b/app/src/main/java/com/denizk0461/studip/adapter/CanteenOfferPageAdapter.kt index 82a9432..6f412dd 100644 --- a/app/src/main/java/com/denizk0461/studip/adapter/CanteenOfferPageAdapter.kt +++ b/app/src/main/java/com/denizk0461/studip/adapter/CanteenOfferPageAdapter.kt @@ -1,16 +1,15 @@ package com.denizk0461.studip.adapter -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.denizk0461.studip.databinding.ItemScrollablePageBinding +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.denizk0461.studip.fragment.CanteenPageFragment import com.denizk0461.studip.model.CanteenOfferGroup /** - * Custom RecyclerView adapter for managing multiple pages of canteen offers in a RecyclerView or - * ViewPager. + * Custom ViewPager adapter for managing multiple pages of canteen offers. * + * @param fragmentActivity parent fragment activity * @param offers all offers for a given canteen, grouped by category (not filtered by day at * this point) * @param daysCovered tells for how many days offers are available for. @@ -20,54 +19,23 @@ import com.denizk0461.studip.model.CanteenOfferGroup * @param displayAllergens whether the user wants allergens to be marked */ class CanteenOfferPageAdapter( + fragmentActivity: FragmentActivity, private var offers: List, private var daysCovered: Int, private val onClickListener: CanteenOfferItemAdapter.OnClickListener, private val displayAllergens: Boolean, -) : RecyclerView.Adapter() { - - /** - * View holder class for parent class - * - * @param binding view binding object - */ - class CanteenOfferPageViewHolder(val binding: ItemScrollablePageBinding) : RecyclerView.ViewHolder(binding.root) - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CanteenOfferPageViewHolder = - CanteenOfferPageViewHolder( - ItemScrollablePageBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) - ) +) : FragmentStateAdapter(fragmentActivity) { // Returns the count of days covered override fun getItemCount(): Int = daysCovered - override fun onBindViewHolder(holder: CanteenOfferPageViewHolder, position: Int) { - - holder.binding.pageRecyclerView.apply { - // Set page to horizontally scroll - layoutManager = LinearLayoutManager( - holder.binding.root.context, LinearLayoutManager.VERTICAL, false - ) - - /* - * Create new adapter for every page. Attribute position denotes day that will be set up - * by the newly created adapter (0 = Monday, 4 = Friday) - * TODO check if the filtered list is empty, and tell the user if it is - */ - adapter = CanteenOfferItemAdapter( - offers.filter { it.dateId == position }, - onClickListener, - displayAllergens, - ) - - // Animate creation of new page - scheduleLayoutAnimation() - } - } + override fun createFragment(position: Int): Fragment = + CanteenPageFragment( + offers, + onClickListener, + displayAllergens, + position, + ) /** * Update the entire list of items 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 9945ad7..ab13c8d 100644 --- a/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventPageAdapter.kt +++ b/app/src/main/java/com/denizk0461/studip/adapter/StudIPEventPageAdapter.kt @@ -1,61 +1,31 @@ package com.denizk0461.studip.adapter -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter import com.denizk0461.studip.model.StudIPEvent -import com.denizk0461.studip.databinding.ItemScrollablePageBinding +import com.denizk0461.studip.fragment.EventPageFragment /** - * Custom RecyclerView adapter for managing multiple pages of Stud.IP events in a RecyclerView or - * ViewPager. + * Custom ViewPager adapter for managing multiple pages of Stud.IP events. * + * @param fragmentActivity parent fragment activity * @param events all events (not filtered by day at this point) * @param onClickListener for managing click and long press events */ class StudIPEventPageAdapter( + fragmentActivity: FragmentActivity, private var events: List, private val onClickListener: StudIPEventItemAdapter.OnClickListener, -) : RecyclerView.Adapter() { - - /** - * View holder class for parent class - * - * @param binding view binding object - */ - class EventPageViewHolder(val binding: ItemScrollablePageBinding) : RecyclerView.ViewHolder(binding.root) - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventPageViewHolder = EventPageViewHolder( - ItemScrollablePageBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) - ) +) : FragmentStateAdapter(fragmentActivity) { /* - * Assume that there will always be 5 pages, for 5 days - Monday through Friday. - * TODO is it right to assume that the user only has events on weekdays? + * Assume that there will always be 7 pages, for 7 days - Monday through Sunday. */ - override fun getItemCount(): Int = 5 + override fun getItemCount(): Int = 7 - // Set up page - override fun onBindViewHolder(holder: EventPageViewHolder, position: Int) { - holder.binding.pageRecyclerView.apply { - // Set page to horizontally scroll - layoutManager = LinearLayoutManager(holder.binding.root.context, LinearLayoutManager.VERTICAL, false) - - /* - * Create new adapter for every page. Attribute position denotes day that will be set up - * by the newly created adapter (0 = Monday, 4 = Friday) - */ - adapter = StudIPEventItemAdapter(events, currentDay = position, onClickListener) // TODO check if empty - - // Animate creation of new page - scheduleLayoutAnimation() - } - } + override fun createFragment(position: Int): Fragment = + EventPageFragment(events, position, onClickListener) /** * Update the entire list of items diff --git a/app/src/main/java/com/denizk0461/studip/data/StwParser.kt b/app/src/main/java/com/denizk0461/studip/data/StwParser.kt index 19fc18e..4918626 100644 --- a/app/src/main/java/com/denizk0461/studip/data/StwParser.kt +++ b/app/src/main/java/com/denizk0461/studip/data/StwParser.kt @@ -1,6 +1,5 @@ package com.denizk0461.studip.data -import com.denizk0461.studip.db.AppRepository import com.denizk0461.studip.model.* import org.jsoup.Jsoup import org.jsoup.nodes.Element @@ -63,8 +62,15 @@ class StwParser { Dependencies.repo.nukeOffers() // Fetch offers from the chosen canteen - parseFromPage(link, Dependencies.repo) + val (dates, canteens, categories, items) = parseFromPage(link) + // Save everything all at once into the database + with (Dependencies.repo) { + insertDates(dates) + insertCanteens(canteens) + insertCategories(categories) + insertItems(items) + } // TODO implement onRefresh(Int) // Action call once all fetching activities have finished @@ -75,9 +81,14 @@ class StwParser { * Fetch and parse the plan of a specific canteen. * * @param url link to the canteen to be scraped. Must be a subpage of stw-bremen.de - * @param repo reference to the app repository used to save the items to persistent storage */ - private fun parseFromPage(url: String, repo: AppRepository) { + private fun parseFromPage(url: String): StwResults { + + val dates = mutableListOf() + val canteens = mutableListOf() + val categories = mutableListOf() + val items = mutableListOf() + // Used to store a parsed date string without creating a new variable on every loop var date: String @@ -120,8 +131,8 @@ class StwParser { // Trim off excess line breaks openingHours = openingHours.trim() - // Save the canteen to persistent storage - repo.insert( + // Save the canteen to its list + canteens.add( OfferCanteen( canteenId, doc.getElementsByClass("pane-title")[1].text(), @@ -148,12 +159,8 @@ class StwParser { */ date = "${rawDate[0]}${rawDate[1].monthToNumber()}." - /* - * Save the date to persistent storage. - * TODO this will duplicate date entries for every canteen that is fetched. This is - * inefficient. - */ - repo.insert(OfferDate(dateId, date)) + // Save the date to its list + dates.add(OfferDate(dateId, date)) dateHasItems = false @@ -165,8 +172,8 @@ class StwParser { // Retrieve the category text val categoryTitle = category.getElementsByClass("category-name")[0].text() - // Save the category to persistent storage - repo.insert(OfferCategory(categoryId, dateId, canteenId, categoryTitle)) + // Save the category to its list + categories.add(OfferCategory(categoryId, dateId, canteenId, categoryTitle)) // Iterate through all items in a category category @@ -195,8 +202,8 @@ class StwParser { val filteredText = tableRows[1].getFilteredText() - // Save the item to persistent storage - repo.insert( + // Save the item to its list + items.add( OfferItem( itemId, categoryId, @@ -219,8 +226,8 @@ class StwParser { * is available. Text will then be handled in the adapter class. */ if (!dateHasItems) { - repo.insert(OfferCategory(categoryId, dateId, canteenId, "NO\$ITEMS")) - repo.insert( + categories.add(OfferCategory(categoryId, dateId, canteenId, "NO\$ITEMS")) + items.add( OfferItem( itemId, categoryId, @@ -240,6 +247,8 @@ class StwParser { } // Increment the canteen ID to avoid conflict canteenId += 1 + + return StwResults(dates, canteens, categories, items) } /** @@ -366,6 +375,13 @@ class StwParser { // .replace("

", "") // .replace("

", "") + private data class StwResults( + val dates: List, + val canteens: List, + val categories: List, + val items: List, + ) + // Image links to all dietary preferences used for checking whether a preference is met private val imageLinkPrefFair = "https://www.stw-bremen.de/sites/default/files/images/pictograms/at_small.png" private val imageLinkPrefFish = "https://www.stw-bremen.de/sites/default/files/images/pictograms/fisch.png" diff --git a/app/src/main/java/com/denizk0461/studip/db/AppDAO.kt b/app/src/main/java/com/denizk0461/studip/db/AppDAO.kt index b9cff3a..bed1ea2 100644 --- a/app/src/main/java/com/denizk0461/studip/db/AppDAO.kt +++ b/app/src/main/java/com/denizk0461/studip/db/AppDAO.kt @@ -49,7 +49,7 @@ interface AppDAO { * * @param events objects to be saved to the database */ - @Insert fun insert(events: List) + @Insert fun insertEvents(events: List) /** * Deletes all Stud.IP events from the database. @@ -124,17 +124,31 @@ interface AppDAO { /** * Inserts a date into the database. * - * @param date object to be saved to the database + * @param date object to be saved to the database */ @Insert fun insert(date: OfferDate) + /** + * Inserts a list of dates into the database. + * + * @param dates objects to be saved to the database + */ + @Insert fun insertDates(dates: List) + /** * Inserts a canteen into the database. * - * @param canteen object to be saved to the database + * @param canteen object to be saved to the database */ @Insert fun insert(canteen: OfferCanteen) + /** + * Inserts a list of canteens into the database. + * + * @param canteens objects to be saved to the database + */ + @Insert fun insertCanteens(canteens: List) + /** * Inserts a canteen offer category into the database. * @@ -142,6 +156,13 @@ interface AppDAO { */ @Insert fun insert(category: OfferCategory) + /** + * Inserts a list of categories into the database. + * + * @param categories objects to be saved to the database + */ + @Insert fun insertCategories(categories: List) + /** * Inserts a canteen offer item into the database. * @@ -149,6 +170,13 @@ interface AppDAO { */ @Insert fun insert(item: OfferItem) + /** + * Inserts a list of items into the database. + * + * @param items objects to be saved to the database + */ + @Insert fun insertItems(items: List) + /** * Retrieves the opening hours of the canteen as a string. * diff --git a/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt b/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt index a03bac2..983496e 100644 --- a/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt +++ b/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt @@ -37,7 +37,7 @@ class AppRepository(app: Application) { * * @param events objects to be saved to the database */ - fun insertEvents(events: List) { dao.insert(events) } + fun insertEvents(events: List) { dao.insertEvents(events) } /** * Deletes all Stud.IP events from the database. @@ -173,13 +173,27 @@ class AppRepository(app: Application) { */ fun insert(date: OfferDate) { dao.insert(date) } + /** + * Inserts a list of dates into the database. + * + * @param dates objects to be saved to the database + */ + fun insertDates(dates: List) { dao.insertDates(dates) } + /** * Inserts a canteen into the database. * - * @param canteen object to be saved to the database + * @param canteen object to be saved to the database */ fun insert(canteen: OfferCanteen) { dao.insert(canteen) } + /** + * Inserts a list of canteens into the database. + * + * @param canteens objects to be saved to the database + */ + fun insertCanteens(canteens: List) { dao.insertCanteens(canteens) } + /** * Inserts a canteen offer category into the database. * @@ -187,6 +201,13 @@ class AppRepository(app: Application) { */ fun insert(category: OfferCategory) { dao.insert(category) } + /** + * Inserts a list of categories into the database. + * + * @param categories objects to be saved to the database + */ + fun insertCategories(categories: List) { dao.insertCategories(categories) } + /** * Inserts a canteen offer item into the database. * @@ -194,6 +215,13 @@ class AppRepository(app: Application) { */ fun insert(item: OfferItem) { dao.insert(item) } + /** + * Inserts a list of items into the database. + * + * @param items objects to be saved to the database + */ + fun insertItems(items: List) { dao.insertItems(items) } + // --- settings preferences --- // /** diff --git a/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt index 482f689..ca5d3dd 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt @@ -5,6 +5,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu +import androidx.fragment.app.FragmentActivity import androidx.fragment.app.viewModels import com.denizk0461.studip.R import com.denizk0461.studip.adapter.CanteenOfferItemAdapter @@ -145,7 +146,11 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { // Set up the view pager's adapter viewPagerAdapter = CanteenOfferPageAdapter( - listOf(), 0, this, viewModel.preferenceAllergen + activity as FragmentActivity, + listOf(), + 0, + this, + viewModel.preferenceAllergen, ) // Assign the adapter to the view pager @@ -163,7 +168,9 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { val groupedElements = offers.groupElements().distinct() // Find all dates stored in the database - val newDates = /*groupedElements.map { it.date }.distinct() TODO */viewModel.getDates() +// val newDates = /*groupedElements.map { it.date }.distinct() TODO */viewModel.getDates() + + val newDates = groupedElements.map { it.date }.distinct() // Update the date count stored in this fragment dateSize = newDates.size diff --git a/app/src/main/java/com/denizk0461/studip/fragment/CanteenPageFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/CanteenPageFragment.kt new file mode 100644 index 0000000..e5aad9e --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/fragment/CanteenPageFragment.kt @@ -0,0 +1,71 @@ +package com.denizk0461.studip.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.denizk0461.studip.adapter.CanteenOfferItemAdapter +import com.denizk0461.studip.adapter.StudIPEventPageAdapter +import com.denizk0461.studip.databinding.RecyclerViewBinding +import com.denizk0461.studip.model.CanteenOfferGroup + +/** + * Fragment that is instantiated by [StudIPEventPageAdapter] to display individual days' pages and + * their events. + * + * @param offers all offers for a given canteen, grouped by category (not filtered by day at + * this point) + * @param onClickListener for managing click and long press events + * @param displayAllergens whether the user wants allergens to be marked + * @param currentDay current day + */ +class CanteenPageFragment( + private var offers: List, + private val onClickListener: CanteenOfferItemAdapter.OnClickListener, + private val displayAllergens: Boolean, + private val currentDay: Int, +) : AppFragment() { + + // Nullable view binding reference + private var _binding: RecyclerViewBinding? = null + + /* + * Non-null reference to the view binding. This property is only valid between onCreateView and + * onDestroyView. + */ + private val binding get() = _binding!! + + // Instantiate the view binding + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = RecyclerViewBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.recyclerView.apply { + // Set page to scroll vertically + layoutManager = LinearLayoutManager( + context, LinearLayoutManager.VERTICAL, false + ) + + /* + * Create new adapter for every page. Attribute position denotes day that will be set up + * by the newly created adapter (0 = Monday, 4 = Friday) + * TODO check if the filtered list is empty, and tell the user if it is + */ + val o = offers.filter { it.dateId == currentDay } + + adapter = CanteenOfferItemAdapter( + o, + onClickListener, + displayAllergens, + ) + + // Animate creation of new page + scheduleLayoutAnimation() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt index 4eda2ae..4574a91 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt @@ -1,9 +1,12 @@ package com.denizk0461.studip.fragment import android.os.Bundle +import android.os.Parcelable +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.FragmentActivity import androidx.fragment.app.viewModels import com.denizk0461.studip.R import com.denizk0461.studip.adapter.StudIPEventItemAdapter @@ -39,13 +42,12 @@ class EventFragment : AppFragment() { private var dayOfWeek: Int = 0 // Titles for the view pager's tabs - private val dayStrings = listOf( - R.string.monday, - R.string.tuesday, - R.string.wednesday, - R.string.thursday, - R.string.friday, - ) + private lateinit var weekdays: Array + +// private var viewPagerScrollPosition: Parcelable? = null + private var viewPagerPosition = -1 + + private var hasFragmentStarted = false // Instantiate the view binding override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -56,40 +58,45 @@ class EventFragment : AppFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - /* - * Determine the current day. - * TODO expand to weekend - */ + weekdays = resources.getStringArray(R.array.weekdays) + + // Determine the current day dayOfWeek = when (Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) { Calendar.TUESDAY -> 1 Calendar.WEDNESDAY -> 2 Calendar.THURSDAY -> 3 Calendar.FRIDAY -> 4 - else -> 0 + Calendar.SATURDAY -> 5 + Calendar.SUNDAY -> 6 + else -> 0 // assume Monday } // Set up the view pager's adapter - viewPagerAdapter = StudIPEventPageAdapter(listOf(), object : StudIPEventItemAdapter.OnClickListener { - override fun onClick(event: StudIPEvent) { - // TODO implement functionality or delete - } - override fun onLongClick(event: StudIPEvent): Boolean { - // Open a bottom sheet to edit the event - openBottomSheet(ScheduleUpdateSheet(event, onUpdate = { eventToUpdate -> - viewModel.update(eventToUpdate) - }, onDelete = { eventToDelete -> - viewModel.delete(eventToDelete) - })) - return true - } - }) + viewPagerAdapter = + StudIPEventPageAdapter(activity as FragmentActivity, listOf(), object : StudIPEventItemAdapter.OnClickListener { + override fun onClick(event: StudIPEvent) { + // TODO implement functionality or delete + } + override fun onLongClick(event: StudIPEvent): Boolean { + // Save the current page of the ViewPager + viewPagerPosition = binding.viewPager.currentItem + + // Open a bottom sheet to edit the event + openBottomSheet(ScheduleUpdateSheet(event, onUpdate = { eventToUpdate -> + viewModel.update(eventToUpdate) + }, onDelete = { eventToDelete -> + viewModel.delete(eventToDelete) + })) + return true + } + }) // Assign the adapter to the view pager binding.viewPager.adapter = viewPagerAdapter // Create and attach the object mediating the tabs for the view pager TabLayoutMediator(binding.dayTabLayout, binding.viewPager) { tab, position -> - tab.text = getString(dayStrings[position]) + tab.text = weekdays[position] }.attach() // Set up LiveData observer to refresh the view on update @@ -97,16 +104,53 @@ class EventFragment : AppFragment() { // Update the item list in the view pager's adapter viewPagerAdapter.setNewItems(events) - // Scroll to the current day - switchToCurrentDayView() + if (!hasFragmentStarted) { + // Scroll to the current day, if no page has been stored to be scrolled to + switchToCurrentDayView() + hasFragmentStarted = true +// } else { +// /* +// * If a page was previously saved, scroll to that one instead. This is meant to +// * prevent jumping from +// */ +// binding.viewPager.currentItem = viewPagerPosition + } } } + override fun onPause() { +// viewPagerPosition = binding.viewPager.currentItem +// Log.d("AAA?", "onpause: $viewPagerPosition") + super.onPause() + } + override fun onResume() { super.onResume() + binding.viewPager.currentItem = viewPagerPosition + Log.d("AAA?", "onresume: pos: $viewPagerPosition; vpp: ${binding.viewPager.currentItem}") // Scroll to the current day - switchToCurrentDayView() +// switchToCurrentDayView() + } + + /** + * Called when the layout changes (e.g. device rotation) but NOT when the fragment is selected + * from the bottom navigation view. + */ + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + Log.d("AAA?", "${binding.viewPager.currentItem}") + outState.putInt( + "viewPagerCurrentPage", + binding.viewPager.currentItem + ) + } + + override fun onViewStateRestored(savedInstanceState: Bundle?) { + super.onViewStateRestored(savedInstanceState) + val restoredPage = savedInstanceState?.getInt("viewPagerCurrentPage") ?: 0 + viewPagerPosition = restoredPage + } // Invalidate the view binding diff --git a/app/src/main/java/com/denizk0461/studip/fragment/EventPageFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/EventPageFragment.kt new file mode 100644 index 0000000..a0faecf --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/fragment/EventPageFragment.kt @@ -0,0 +1,60 @@ +package com.denizk0461.studip.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.denizk0461.studip.adapter.StudIPEventPageAdapter +import com.denizk0461.studip.adapter.StudIPEventItemAdapter +import com.denizk0461.studip.databinding.RecyclerViewBinding +import com.denizk0461.studip.model.StudIPEvent + +/** + * Fragment that is instantiated by [StudIPEventPageAdapter] to display individual days' pages and + * their events. + * + * @param events list of all events (still not filtered by day at this point) + * @param currentDay current day that is used to show only events of a given day (0 = Monday, + * 4 = Friday) + * @param onClickListener used for listening to clicks and long presses + */ +class EventPageFragment( + private val events: List, + private val currentDay: Int, + private val onClickListener: StudIPEventItemAdapter.OnClickListener, +) : AppFragment() { + + // Nullable view binding reference + private var _binding: RecyclerViewBinding? = null + + /* + * Non-null reference to the view binding. This property is only valid between onCreateView and + * onDestroyView. + */ + private val binding get() = _binding!! + + // Instantiate the view binding + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = RecyclerViewBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.recyclerView.apply { + // Set page to scroll vertically + layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + + /* + * Create new adapter for every page. Attribute position denotes day that will be set up + * by the newly created adapter (0 = Monday, 4 = Friday) + */ + adapter = StudIPEventItemAdapter(events, currentDay, onClickListener) // TODO check if empty + + // Animate creation of new page + scheduleLayoutAnimation() + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/item_scrollable_page.xml b/app/src/main/res/layout/item_scrollable_page.xml deleted file mode 100644 index ac81f05..0000000 --- a/app/src/main/res/layout/item_scrollable_page.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_view.xml b/app/src/main/res/layout/recycler_view.xml new file mode 100644 index 0000000..38451d7 --- /dev/null +++ b/app/src/main/res/layout/recycler_view.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0421b4f..7fd6f29 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -9,6 +9,16 @@ Samstag Sonntag + + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + Sonntag + + Ein Fehler ist aufgetreten! Speichern @@ -121,9 +131,6 @@ Markiere meinen nächsten Kurs Funktioniert nur wenn Kurse sich nicht überschneiden! - Nutze akademisches Viertel - Wo es passt - Markiere Angebote mit Allergenen mit einem Stern* Wird auch für Zusatzstoffe genutzt diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8da8066..cd39ccb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,6 +9,16 @@ Saturday Sunday + + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + Sunday + + Something went wrong! Save