From d1df8f3a3258707088d0a2a8f896e3fa8b4c62cb Mon Sep 17 00:00:00 2001 From: meiron03 Date: Sat, 23 Sep 2023 03:28:46 -0400 Subject: [PATCH] Added functionality for favorites (not yet connected to backend). Also updated UI for the fitness room list items. --- .../labs/pennmobile/FitnessHolderFragment.kt | 6 +- .../pennmobile/FitnessPreferencesFragment.kt | 63 ++++++++ .../labs/pennmobile/PottruckFragment.kt | 69 +++++++- .../pennmobile/adapters/FitnessAdapter.kt | 113 +++++++++++-- .../adapters/FitnessHeaderAdapter.kt | 34 ++++ .../adapters/FitnessPagerAdapter.kt | 2 +- .../adapters/FitnessPreferenceAdapter.kt | 56 +++++++ .../pennmobile/adapters/MainPagerAdapter.kt | 6 +- .../classes/FitnessAdapterDataModel.kt | 12 ++ .../classes/FitnessPreferenceViewModel.kt | 79 +++++++++ .../src/main/res/drawable/icons8_done_24.png | Bin 0 -> 272 bytes .../src/main/res/drawable/rounded_dialog.xml | 6 + .../src/main/res/layout/fitness_list_item.xml | 153 ++++++++++++++---- .../layout/fitness_preference_list_item.xml | 28 ++++ .../main/res/layout/fitness_section_title.xml | 25 +++ .../layout/fragment_fitness_preferences.xml | 68 ++++++++ .../src/main/res/layout/fragment_pottruck.xml | 83 ++++++++-- 17 files changed, 735 insertions(+), 68 deletions(-) create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/FitnessPreferencesFragment.kt create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessHeaderAdapter.kt create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessPreferenceAdapter.kt create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/FitnessAdapterDataModel.kt create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/FitnessPreferenceViewModel.kt create mode 100644 PennMobile/src/main/res/drawable/icons8_done_24.png create mode 100644 PennMobile/src/main/res/drawable/rounded_dialog.xml create mode 100644 PennMobile/src/main/res/layout/fitness_preference_list_item.xml create mode 100644 PennMobile/src/main/res/layout/fitness_section_title.xml create mode 100644 PennMobile/src/main/res/layout/fragment_fitness_preferences.xml diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/FitnessHolderFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/FitnessHolderFragment.kt index 6d1acfb3..e0884fed 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/FitnessHolderFragment.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/FitnessHolderFragment.kt @@ -5,10 +5,8 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView -import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.Fragment -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.appbar.AppBarLayout import com.google.android.material.tabs.TabLayoutMediator import com.pennapps.labs.pennmobile.adapters.FitnessPagerAdapter @@ -50,7 +48,7 @@ class FitnessHolderFragment: Fragment() { if (position == 0) { tab.text = "Pottruck" } else { - tab.text = "Other Facilities" + tab.text = "Favorites" } }.attach() } @@ -60,7 +58,7 @@ class FitnessHolderFragment: Fragment() { * fills in the textViews for the title/date */ private fun initAppBar() { - val appBarLayout : AppBarLayout = mView.findViewById(R.id.appbar_home_holder); + val appBarLayout : AppBarLayout = mView.findViewById(R.id.appbar_home_holder) val titleView : TextView = mView.findViewById(R.id.title_view) val dateView : TextView = mView.findViewById(R.id.date_view) diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/FitnessPreferencesFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/FitnessPreferencesFragment.kt new file mode 100644 index 00000000..ad321c06 --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/FitnessPreferencesFragment.kt @@ -0,0 +1,63 @@ +package com.pennapps.labs.pennmobile + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.RecyclerView +import com.pennapps.labs.pennmobile.adapters.FitnessPreferenceAdapter +import com.pennapps.labs.pennmobile.classes.FitnessPreferenceViewModel + +interface CloseListener { + fun updateAdapters() +} + +class FitnessPreferencesFragment(private val dataModel: FitnessPreferenceViewModel, + private val listener: CloseListener) : DialogFragment() { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_fitness_preferences, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val prefAdapter = FitnessPreferenceAdapter(dataModel) + + val recyclerView: RecyclerView = view.findViewById(R.id.fitness_preference_recycler_view) + recyclerView.adapter = prefAdapter + + val cancelText : TextView = view.findViewById(R.id.fitness_fragment_pref_cancel) + cancelText.setOnClickListener { + dataModel.restorePreferences() + dialog?.dismiss() + } + + val saveText : TextView = view.findViewById(R.id.fitness_fragment_pref_save) + saveText.setOnClickListener { + dataModel.updatePositionMap() + dataModel.updateRemotePreferences() + listener.updateAdapters() + + dialog?.dismiss() + } + } + + override fun onStart() { + super.onStart() + val dialog = dialog + + if (dialog != null) { + val width = ViewGroup.LayoutParams.MATCH_PARENT + val height = ViewGroup.LayoutParams.MATCH_PARENT + dialog.window!!.setLayout(width, height) + } + } + +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/PottruckFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/PottruckFragment.kt index af353eb3..b6f7687b 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/PottruckFragment.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/PottruckFragment.kt @@ -5,15 +5,24 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.TextView import androidx.appcompat.widget.Toolbar +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import com.google.android.material.appbar.AppBarLayout import com.pennapps.labs.pennmobile.adapters.FitnessAdapter +import com.pennapps.labs.pennmobile.adapters.FitnessHeaderAdapter import com.pennapps.labs.pennmobile.api.StudentLife +import com.pennapps.labs.pennmobile.classes.FitnessPreferenceViewModel +import com.pennapps.labs.pennmobile.components.collapsingtoolbar.ToolbarBehavior +import com.pennapps.labs.pennmobile.utils.Utils + class PottruckFragment : Fragment() { private lateinit var mActivity : MainActivity @@ -24,6 +33,12 @@ class PottruckFragment : Fragment() { private lateinit var recyclerView : RecyclerView private lateinit var loadingPanel : View + private lateinit var dataModel : FitnessPreferenceViewModel + private lateinit var favoritesAdapter : FitnessAdapter + private lateinit var otherAdapter : FitnessAdapter + private lateinit var favoriteHeaderAdapter : FitnessHeaderAdapter + private lateinit var otherHeaderAdapter : FitnessHeaderAdapter + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mStudentLife = MainActivity.studentLifeInstance @@ -48,12 +63,16 @@ class PottruckFragment : Fragment() { swipeRefresh.setColorSchemeResources(R.color.color_accent, R.color.color_primary) recyclerView.layoutManager = LinearLayoutManager(mActivity, LinearLayoutManager.VERTICAL, false) - swipeRefresh.setOnRefreshListener { getFitnessRooms() } + swipeRefresh.setOnRefreshListener { getFitnessRooms(view) } - getFitnessRooms() + // populate the title/date of the app bar + initAppBar() + + // populate recyclerview + getFitnessRooms(view) } - private fun getFitnessRooms() { + private fun getFitnessRooms(view: View) { //displays banner if not connected if (!getConnected()) return @@ -63,11 +82,36 @@ class PottruckFragment : Fragment() { Log.i("Fitness Room${room.roomId}", "${room.roomName}") } val sortedRooms = fitnessRooms.sortedBy {it.roomName} + + dataModel = FitnessPreferenceViewModel(sortedRooms) + mActivity.runOnUiThread { - val adapter = FitnessAdapter(sortedRooms) - recyclerView.adapter = adapter + + favoritesAdapter = FitnessAdapter(true, dataModel) + otherAdapter = FitnessAdapter(false, dataModel) + + favoriteHeaderAdapter = FitnessHeaderAdapter("Favorites") + otherHeaderAdapter = FitnessHeaderAdapter("Other Facilities") + + val concatenated = ConcatAdapter(favoriteHeaderAdapter, favoritesAdapter, + otherHeaderAdapter, otherAdapter) + + recyclerView.adapter = concatenated loadingPanel.visibility = View.GONE swipeRefresh.isRefreshing = false + + // set click listener for favorites button + val fitnessPref : ImageView = view.findViewById(R.id.fitness_preferences) + fitnessPref.setOnClickListener { + dataModel.savePreferences() + val prefDialog = FitnessPreferencesFragment(dataModel, object: CloseListener{ + override fun updateAdapters() { + favoritesAdapter.notifyDataSetChanged() + otherAdapter.notifyDataSetChanged() + } + }) + prefDialog.show(mActivity.supportFragmentManager, "Fitness Preferences Dialog") + } } }, { Log.e("PottruckFragment", "Error getting fitness rooms", it) @@ -98,4 +142,19 @@ class PottruckFragment : Fragment() { connectionToolbar.visibility = View.GONE return true } + + /** + * Initialize the app bar of the fragment and + * fills in the textViews for the title/date + */ + private fun initAppBar() { + val appBarLayout : AppBarLayout = mView.findViewById(R.id.appbar_home_holder) + val titleView : TextView = mView.findViewById(R.id.title_view) + val dateView : TextView = mView.findViewById(R.id.date_view) + + (appBarLayout.layoutParams as CoordinatorLayout.LayoutParams).behavior = ToolbarBehavior() + + titleView.text = getString(R.string.fitness) + dateView.text = Utils.getCurrentSystemTime() + } } \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessAdapter.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessAdapter.kt index 5fd48703..2abbf585 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessAdapter.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessAdapter.kt @@ -10,7 +10,11 @@ import android.view.ViewGroup import android.view.animation.Animation import android.view.animation.LinearInterpolator import android.view.animation.RotateAnimation -import android.widget.* +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.ProgressBar +import android.widget.TextView +import android.widget.Toast import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat @@ -27,6 +31,7 @@ import com.github.mikephil.charting.formatter.IndexAxisValueFormatter import com.pennapps.labs.pennmobile.MainActivity import com.pennapps.labs.pennmobile.R import com.pennapps.labs.pennmobile.api.StudentLife +import com.pennapps.labs.pennmobile.classes.FitnessAdapterDataModel import com.pennapps.labs.pennmobile.classes.FitnessRoom import com.pennapps.labs.pennmobile.classes.FitnessRoomUsage import com.pennapps.labs.pennmobile.classes.RoundedBarChartRenderer @@ -36,13 +41,14 @@ import java.time.LocalTime import java.time.ZonedDateTime import java.time.format.DateTimeFormatter -class FitnessAdapter(private val fitnessRooms: List) : +class FitnessAdapter(private val isFavorite : Boolean, private val dataModel: FitnessAdapterDataModel) : RecyclerView.Adapter() { private lateinit var mActivity: Activity private lateinit var mContext : Context private lateinit var mStudentLife : StudentLife - class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + + class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { val mainView : ConstraintLayout val roomView : TextView val statusView : TextView @@ -51,6 +57,7 @@ class FitnessAdapter(private val fitnessRooms: List) : val progressBar : ProgressBar val arrowView : ImageView + val timeCapacityView : TextView val lastUpdatedView : TextView val capacityViewCircle : com.google.android.material.progressindicator.CircularProgressIndicator val capacityView : TextView @@ -69,6 +76,7 @@ class FitnessAdapter(private val fitnessRooms: List) : hoursView = view.findViewById(R.id.item_fitness_hours) arrowView = view.findViewById(R.id.fitness_more_indicator) + timeCapacityView = view.findViewById(R.id.timeCapacity) lastUpdatedView = view.findViewById(R.id.item_pottruck_last_updated) capacityViewCircle = view.findViewById(R.id.item_pottruck_capacity_circle) capacityView = view.findViewById(R.id.item_pottruck_capacity) @@ -204,11 +212,13 @@ class FitnessAdapter(private val fitnessRooms: List) : } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.fitness_list_item, parent, false) mContext = parent.context mActivity = mContext as MainActivity mStudentLife = MainActivity.studentLifeInstance + + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.fitness_list_item, parent, false) + return ViewHolder(view) } @@ -231,19 +241,21 @@ class FitnessAdapter(private val fitnessRooms: List) : } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val room = fitnessRooms[position] + + val room = dataModel.getRoom(isFavorite, position) holder.roomView.text = room.roomName // check if the room is currently open // NOT time zone safe val currentTime = LocalTime.now() - // Sunday -> 0, Monday -> 1, etc. - val dayOfWeek = ZonedDateTime.now().dayOfWeek.value + // dayOfWeek gives Sunday -> 0, Monday -> 1, etc. + // but we want 0 -> monday + val dayOfWeek = (ZonedDateTime.now().dayOfWeek.value + 6) % 7 // the open and close time lists start with monday - val openTimeString = room.openTimeList?.get((dayOfWeek + 6) % 7) - val closeTimeString = room.closeTimeList?.get((dayOfWeek + 6) % 7) + val openTimeString = room.openTimeList?.get(dayOfWeek) + val closeTimeString = room.closeTimeList?.get(dayOfWeek) val openTime = LocalTime.parse(openTimeString) val closeTime = LocalTime.parse(closeTimeString) @@ -262,35 +274,104 @@ class FitnessAdapter(private val fitnessRooms: List) : val hours = "${openTime.format(timeFormatter)} - ${closeTime.format(timeFormatter)}" holder.hoursView.text = hours + // format and assign the times for the rest of the week + // definitely not the best way to do this, but I am lazy + + val mfIndex = if (dayOfWeek < 5) dayOfWeek else 0 + val mfOpenTime = LocalTime.parse(room.openTimeList?.get(mfIndex)) + val mfCloseTime = LocalTime.parse(room.closeTimeList?.get(mfIndex)) + val mfHours = "${mfOpenTime.format(timeFormatter)} - ${mfCloseTime.format(timeFormatter)}" + + val saturdayIndex = 5 + val saturdayOpenTime = LocalTime.parse(room.openTimeList?.get(saturdayIndex)) + val saturdayCloseTime = LocalTime.parse(room.closeTimeList?.get(saturdayIndex)) + val saturdayHours = "${saturdayOpenTime.format(timeFormatter)} - ${saturdayCloseTime.format(timeFormatter)}" + + val sundayIndex = 6 + val sundayOpenTime = LocalTime.parse(room.openTimeList?.get(sundayIndex)) + val sundayCloseTime = LocalTime.parse(room.closeTimeList?.get(sundayIndex)) + val sundayHours = "${sundayOpenTime.format(timeFormatter)} - ${sundayCloseTime.format(timeFormatter)}" + + + (holder.view.findViewById(R.id.fitness_sunday_time) as TextView).text = sundayHours + (holder.view.findViewById(R.id.fitness_mf_time) as TextView).text = mfHours + (holder.view.findViewById(R.id.fitness_sat_time) as TextView).text = saturdayHours + + val blue = Color.parseColor("#1280F0") + + if (dayOfWeek < 5) { + (holder.view.findViewById(R.id.fitness_mf) as TextView).setTextColor(blue) + (holder.view.findViewById(R.id.fitness_mf_time) as TextView).setTextColor(blue) + } else if (dayOfWeek == 5) { + (holder.view.findViewById(R.id.fitness_sat) as TextView).setTextColor(blue) + (holder.view.findViewById(R.id.fitness_sat_time) as TextView).setTextColor(blue) + } else { + (holder.view.findViewById(R.id.fitness_sunday) as TextView).setTextColor(blue) + (holder.view.findViewById(R.id.fitness_sunday_time) as TextView).setTextColor(blue) + + } + // make progress bar invisible holder.progressBar.visibility = View.INVISIBLE // get image from url Glide.with(mContext).load(room.imageURL).into(holder.imageView) + var busyness : String // update the capacity if (room.capacity == null) { + busyness = "N/A" holder.capacityView.text = "N/A" holder.capacityViewCircle.progress = 0 } else { val capacityInt = room.capacity!!.toInt() val capacity = "$capacityInt%" - holder.capacityView.text = capacity + if (capacityInt == 0) { + busyness = "Empty" + } else if (capacityInt < 10) { + busyness = "Not very busy" + } else if (capacityInt < 30) { + busyness = "Slightly busy" + } else if (capacityInt < 60) { + busyness = "Pretty busy" + } else if (capacityInt < 90) { + busyness = "Extremely busy" + } else { + busyness = "Packed" + } + holder.capacityViewCircle.progress = capacityInt + holder.capacityView.text = capacity } + // update the time and capacity + var curHour = currentTime.hour + + val ampm = if (curHour >= 12) "PM" else "AM" + if (curHour > 12) curHour -= 12 + if (curHour == 0) curHour += 12 + + val timeCap = "$curHour $ampm: $busyness" + holder.timeCapacityView.text = HtmlCompat.fromHtml(timeCap, HtmlCompat.FROM_HTML_MODE_COMPACT) + // update the time for last updated val lastUpdateTime = ZonedDateTime.parse(room.lastUpdated) val duration = Duration.between(lastUpdateTime, ZonedDateTime.now()) - val hourDiff = duration.toHours() - val minuteDiff = duration.toMinutes() % 60 + val updHours = duration.toHours() - val lastUpd = "Last Updated: $hourDiff hours and $minuteDiff minutes ago" + val lastUpd = if (updHours > 2L) { + "Updated $updHours hours ago" + } else if (updHours == 1L) { + "Updated $updHours hour ago" + } else { + val updMinutes = duration.toMinutes() + "Updated $updMinutes minutes ago" + } - holder.lastUpdatedView.text = HtmlCompat.fromHtml(lastUpd, HtmlCompat.FROM_HTML_MODE_COMPACT) + holder.lastUpdatedView.text = lastUpd holder.mainView.bringToFront() // garbage code starts here ------------------------------------ @@ -304,5 +385,5 @@ class FitnessAdapter(private val fitnessRooms: List) : } } - override fun getItemCount() = fitnessRooms.size + override fun getItemCount() = dataModel.getNumber(isFavorite) } diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessHeaderAdapter.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessHeaderAdapter.kt new file mode 100644 index 00000000..1ac7323f --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessHeaderAdapter.kt @@ -0,0 +1,34 @@ +package com.pennapps.labs.pennmobile.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.pennapps.labs.pennmobile.R + +class FitnessHeaderAdapter(private val text : String) : RecyclerView.Adapter() { + class ViewHolder(view: View) :RecyclerView.ViewHolder(view) { + val headerView : TextView + init { + headerView = view.findViewById(R.id.fitness_section_title_text) + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.fitness_section_title, parent, false) + + return ViewHolder(view) + } + + override fun getItemCount(): Int { + return 1 + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.headerView.text = text + } + +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessPagerAdapter.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessPagerAdapter.kt index a587d532..1017e216 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessPagerAdapter.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessPagerAdapter.kt @@ -11,6 +11,6 @@ class FitnessPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { if (position == 0) { return PottruckFragment() } - return DiningFragment() + return PottruckFragment() } } \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessPreferenceAdapter.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessPreferenceAdapter.kt new file mode 100644 index 00000000..3867a72b --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/FitnessPreferenceAdapter.kt @@ -0,0 +1,56 @@ +package com.pennapps.labs.pennmobile.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.pennapps.labs.pennmobile.R +import com.pennapps.labs.pennmobile.classes.FitnessAdapterDataModel + +class FitnessPreferenceAdapter(private val dataModel: FitnessAdapterDataModel) + : RecyclerView.Adapter() { + class ViewHolder(view: View) :RecyclerView.ViewHolder(view) { + val v : View + val imv : ImageView + val textView : TextView + + init { + v = view + imv = v.findViewById(R.id.fitness_preference_select) + textView = v.findViewById(R.id.fitness_preference_text) + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.fitness_preference_list_item, parent, false) + + return ViewHolder(view) + } + + override fun getItemCount(): Int { + return dataModel.getTot() + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val room = dataModel.getRoomAll(position) + holder.textView.text = room.roomName + + val rid : Int = room.roomId!! + + if (dataModel.isFavorite(rid)) { + holder.imv.visibility = View.VISIBLE + } + + holder.v.setOnClickListener { + val b = dataModel.flipState(rid) + if (b) { + holder.imv.visibility = View.VISIBLE + } else { + holder.imv.visibility = View.INVISIBLE + } + } + } +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/MainPagerAdapter.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/MainPagerAdapter.kt index a5126361..715baa74 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/MainPagerAdapter.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/MainPagerAdapter.kt @@ -8,8 +8,8 @@ import com.pennapps.labs.pennmobile.DiningHolderFragment import com.pennapps.labs.pennmobile.GsrTabbedFragment import com.pennapps.labs.pennmobile.HomeFragment import com.pennapps.labs.pennmobile.LaundryFragment +import com.pennapps.labs.pennmobile.PottruckFragment import com.pennapps.labs.pennmobile.more_fragments.MoreFragment -import com.pennapps.labs.pennmobile.FitnessHolderFragment class MainPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle?) : FragmentStateAdapter(fragmentManager, lifecycle!!) { override fun createFragment(position: Int): Fragment { @@ -17,8 +17,8 @@ class MainPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle?) HOME_POSITION-> HomeFragment() DINING_POSITION-> DiningHolderFragment() GSR_POSITION-> GsrTabbedFragment() - LAUNDRY_POSITION-> LaundryFragment() - MORE_POSITION-> FitnessHolderFragment() + LAUNDRY_POSITION-> PottruckFragment() + MORE_POSITION-> MoreFragment() else -> HomeFragment() } } diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/FitnessAdapterDataModel.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/FitnessAdapterDataModel.kt new file mode 100644 index 00000000..ca1f3f0d --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/FitnessAdapterDataModel.kt @@ -0,0 +1,12 @@ +package com.pennapps.labs.pennmobile.classes + +interface FitnessAdapterDataModel { + fun flipState(roomId: Int) : Boolean + fun getNumber(isFavorite: Boolean) : Int + fun getRoom(isFavorite: Boolean, position: Int) : FitnessRoom + + fun getTot() : Int + fun getRoomAll(roomId: Int) : FitnessRoom + + fun isFavorite(roomId: Int) : Boolean +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/FitnessPreferenceViewModel.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/FitnessPreferenceViewModel.kt new file mode 100644 index 00000000..78ba319c --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/FitnessPreferenceViewModel.kt @@ -0,0 +1,79 @@ +package com.pennapps.labs.pennmobile.classes + +import android.util.Log + +class FitnessPreferenceViewModel(private val roomList: List) : FitnessAdapterDataModel { + + private val roomTot = roomList.size + + // hashset of the favorite room ids + private val favoriteRooms : HashSet = hashSetOf() + private val prevFavoriteRooms : HashSet = hashSetOf() + + // hashmap that maps position --> position in array + private val positionMap : Array = (0 until roomTot).toList().toTypedArray() + + override fun flipState(roomId: Int) : Boolean { + if (favoriteRooms.contains(roomId)) { + favoriteRooms.remove(roomId) + return false + } + favoriteRooms.add(roomId) + return true + } + + override fun getNumber(isFavorite: Boolean): Int { + if (isFavorite) { + return favoriteRooms.size + } + return roomTot - favoriteRooms.size + } + + override fun getRoom(isFavorite: Boolean, position: Int): FitnessRoom { + if (isFavorite) { + return roomList[positionMap[position]] + } + return roomList[positionMap[position + favoriteRooms.size]] + } + + override fun getTot(): Int { + return roomTot + } + + override fun getRoomAll(roomId: Int): FitnessRoom { + return roomList[roomId] + } + + override fun isFavorite(roomId: Int): Boolean { + return favoriteRooms.contains(roomId) + } + + fun updatePositionMap() { + val numFavorites = favoriteRooms.size + var curFavIndex = 0 + var curOtherIndex = 0 + for (i in 0 until roomTot) { + if (favoriteRooms.contains(roomList[i].roomId)) { + positionMap[curFavIndex++] = i + } else { + positionMap[numFavorites + curOtherIndex++] = i + } + } + } + + fun savePreferences() { + prevFavoriteRooms.clear() + prevFavoriteRooms.addAll(favoriteRooms) + } + + fun restorePreferences() { + favoriteRooms.clear() + favoriteRooms.addAll(prevFavoriteRooms) + } + + + fun updateRemotePreferences() { + + } + +} \ No newline at end of file diff --git a/PennMobile/src/main/res/drawable/icons8_done_24.png b/PennMobile/src/main/res/drawable/icons8_done_24.png new file mode 100644 index 0000000000000000000000000000000000000000..01391b391884300907ee6ce072557a11324b05bb GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-#UW1@ z$B>FSZ?Ej;I%FVn?Bo9YC&Ct8eO)h>JyKZr=x-2<`THMR#BxKq(k)6da%Mlc#BcQV z^!pY8F4uoExB`k=`5Vm(${YC`1b#4h->!&%sC~P_|6%s+3ib-leH(kXi|*UnyItg+ zs_1D;e<1nYfuSV$f$sy&ALl~iJPapHPw;Yn*dFh1GM#r}qKE8E$8t@BUJ zSjlj)K1JlP{HM1*=X{c!S8?r6(EPRQ&zkbQQxOj`?&dtMI&oDru0 + + + + \ No newline at end of file diff --git a/PennMobile/src/main/res/layout/fitness_list_item.xml b/PennMobile/src/main/res/layout/fitness_list_item.xml index 138b49d0..ffd60689 100644 --- a/PennMobile/src/main/res/layout/fitness_list_item.xml +++ b/PennMobile/src/main/res/layout/fitness_list_item.xml @@ -160,15 +160,43 @@ - + android:layout_height="match_parent"> + + + + + + + + + @@ -186,25 +214,78 @@ app:cardCornerRadius="8dp" app:layout_constraintRight_toRightOf="@+id/linear_layout_extras"> - + + + + + + + + + + + + + + + + + + - + + + - + android:orientation="vertical" + android:layout_gravity="center"> + + + + + + diff --git a/PennMobile/src/main/res/layout/fitness_preference_list_item.xml b/PennMobile/src/main/res/layout/fitness_preference_list_item.xml new file mode 100644 index 00000000..84f119a6 --- /dev/null +++ b/PennMobile/src/main/res/layout/fitness_preference_list_item.xml @@ -0,0 +1,28 @@ + + + + + + \ No newline at end of file diff --git a/PennMobile/src/main/res/layout/fitness_section_title.xml b/PennMobile/src/main/res/layout/fitness_section_title.xml new file mode 100644 index 00000000..e3abea3d --- /dev/null +++ b/PennMobile/src/main/res/layout/fitness_section_title.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/PennMobile/src/main/res/layout/fragment_fitness_preferences.xml b/PennMobile/src/main/res/layout/fragment_fitness_preferences.xml new file mode 100644 index 00000000..8d4299ce --- /dev/null +++ b/PennMobile/src/main/res/layout/fragment_fitness_preferences.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PennMobile/src/main/res/layout/fragment_pottruck.xml b/PennMobile/src/main/res/layout/fragment_pottruck.xml index ccc206c4..934ac44f 100644 --- a/PennMobile/src/main/res/layout/fragment_pottruck.xml +++ b/PennMobile/src/main/res/layout/fragment_pottruck.xml @@ -1,12 +1,67 @@ - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + android:layout_height="match_parent" + android:layout_marginTop="20dp"> + + + + - \ No newline at end of file + \ No newline at end of file