From 8ddc9b106494c8dacc4b08f8a8826e04f1934032 Mon Sep 17 00:00:00 2001 From: meiron03 Date: Sun, 21 Apr 2024 13:29:42 -0400 Subject: [PATCH] Add Retrofit2 and fix laundry (#612) * Start adding retrofit 2. Also include rewrite for laundry. * Cleanup * More cleanup and move updateMachines into the correct place. * Code cleanup and null checks. * Rename functions in StudentLifeRf2 --- .../labs/pennmobile/LaundryFragment.kt | 226 ++++--------- .../pennmobile/LaundrySettingsFragment.kt | 145 +++------ .../pennapps/labs/pennmobile/MainActivity.kt | 38 ++- .../adapters/LaundrySettingsAdapter.java | 307 ------------------ .../adapters/LaundrySettingsAdapter.kt | 165 ++++++++++ .../labs/pennmobile/api/Serializer.java | 22 -- .../labs/pennmobile/api/StudentLife.java | 13 - .../labs/pennmobile/api/StudentLifeRf2.kt | 32 ++ .../pennmobile/classes/HomepageViewModel.kt | 3 +- .../pennmobile/classes/LaundryPreferences.kt | 10 + .../classes/LaundryRoomFavorites.kt | 6 + .../pennmobile/viewmodels/LaundryViewModel.kt | 253 +++++++++++++++ 12 files changed, 605 insertions(+), 615 deletions(-) delete mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/LaundrySettingsAdapter.java create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/LaundrySettingsAdapter.kt create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/StudentLifeRf2.kt create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/LaundryPreferences.kt create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/LaundryRoomFavorites.kt create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/viewmodels/LaundryViewModel.kt diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/LaundryFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/LaundryFragment.kt index 26de7e3fa..f6378599b 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/LaundryFragment.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/LaundryFragment.kt @@ -1,59 +1,52 @@ package com.pennapps.labs.pennmobile +import StudentLifeRf2 import android.content.Context import android.content.SharedPreferences import android.os.Bundle import androidx.preference.PreferenceManager -import android.util.Log import android.view.* import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction +import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.LinearLayoutManager -import com.google.firebase.crashlytics.FirebaseCrashlytics import com.pennapps.labs.pennmobile.adapters.LaundryRoomAdapter -import com.pennapps.labs.pennmobile.api.StudentLife import com.pennapps.labs.pennmobile.classes.LaundryRoom import com.pennapps.labs.pennmobile.classes.LaundryUsage import com.pennapps.labs.pennmobile.components.collapsingtoolbar.ToolbarBehavior import com.pennapps.labs.pennmobile.databinding.FragmentLaundryBinding import com.pennapps.labs.pennmobile.utils.Utils +import com.pennapps.labs.pennmobile.viewmodels.LaundryViewModel import kotlinx.android.synthetic.main.loading_panel.* -import kotlinx.android.synthetic.main.loading_panel.view.* -import kotlinx.android.synthetic.main.no_results.* import java.util.* class LaundryFragment : Fragment() { - private lateinit var mActivity: MainActivity - private lateinit var mStudentLife: StudentLife + private lateinit var mStudentLife: StudentLifeRf2 private lateinit var mContext: Context - private var sp: SharedPreferences? = null + private lateinit var sharedPreferences: SharedPreferences // list of favorite laundry rooms private var laundryRooms = ArrayList() // data for laundry room usage - private var roomsData: List = ArrayList() - - private var laundryRoomsResult = ArrayList() - private var roomsDataResult: MutableList = ArrayList() + private var roomsData: ArrayList = ArrayList() private var mAdapter: LaundryRoomAdapter? = null - private var count: Int = 0 - private var numRooms: Int = 0 - private var _binding : FragmentLaundryBinding? = null private val binding get() = _binding!! + private val laundryViewModel : LaundryViewModel by activityViewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mStudentLife = MainActivity.studentLifeInstance + mStudentLife = MainActivity.studentLifeInstanceRf2 mActivity = activity as MainActivity mContext = mActivity + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mActivity) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -62,27 +55,11 @@ class LaundryFragment : Fragment() { initAppBar() - // get num rooms to display - sp = PreferenceManager.getDefaultSharedPreferences(mContext) - numRooms = sp?.getInt(mContext.getString(R.string.num_rooms_pref), 100) ?: 0 - count = 0 - for (i in 0 until numRooms) { - if (sp!!.getBoolean(i.toString(), false)) { - count += 1 - } - } - binding.favoriteLaundryList.layoutManager = LinearLayoutManager(mContext) - binding.laundryMachineRefresh.setOnRefreshListener { updateRooms() } - binding.laundryMachineRefresh.setColorSchemeResources(R.color.color_accent, R.color.color_primary) - - // no rooms chosen - if (count == 0) { - view.loadingPanel?.visibility = View.GONE - binding.laundryHelpText.visibility = View.VISIBLE - mAdapter = LaundryRoomAdapter(mContext, laundryRooms, roomsData, false) - binding.favoriteLaundryList.adapter = mAdapter + binding.laundryMachineRefresh.setOnRefreshListener { + updateMachines() } + binding.laundryMachineRefresh.setColorSchemeResources(R.color.color_accent, R.color.color_primary) return view } @@ -95,151 +72,70 @@ class LaundryFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mActivity.removeTabs() - numRooms = sp?.getInt(mContext.getString(R.string.num_rooms_pref), 100) ?: 0 - - // get num rooms to display - count = 0 - for (i in 0 until numRooms) { - if (sp?.getBoolean(i.toString(), false) == true) { - count += 1 - } - } mActivity.setTitle(R.string.laundry) - mActivity.setSelectedTab(MainActivity.LAUNDRY) + + mAdapter = LaundryRoomAdapter(mContext, laundryRooms, roomsData, false) + binding.favoriteLaundryList.adapter = mAdapter + loadingPanel?.visibility = View.VISIBLE - updateRooms() - } - private fun initAppBar() { - (binding.appbarHome.layoutParams as CoordinatorLayout.LayoutParams).behavior = ToolbarBehavior() - binding.titleView.text = getString(R.string.laundry) - binding.dateView.text = Utils.getCurrentSystemTime() - binding.laundryPreferences.setOnClickListener { - val fragmentManager = mActivity.supportFragmentManager - fragmentManager.beginTransaction() - .replace(R.id.content_frame, LaundrySettingsFragment()) - .addToBackStack("Laundry Settings Fragment") - .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) - .commit() + laundryViewModel.favoriteRooms.observe(viewLifecycleOwner) { favorites -> + binding.laundryMachineRefresh.isRefreshing = false + + laundryRooms.clear() + roomsData.clear() + + laundryRooms.addAll(favorites.favoriteRooms) + roomsData.addAll(favorites.roomsData) + + for (pos in 0 until LaundryViewModel.maxNumRooms) { + mAdapter!!.notifyItemChanged(pos) + } + + loadingPanel?.visibility = View.GONE + binding.laundryHelpText.visibility = View.INVISIBLE } + + updateMachines() } - private fun updateRooms() { + private fun getOnline() : Boolean { //displays banner if not connected if (!isOnline(context)) { binding.internetConnectionLaundry.setBackgroundColor(resources.getColor(R.color.darkRedBackground)) binding.internetConnectionMessageLaundry.text = getString(R.string.internet_error) binding.internetConnectionLaundry.visibility = View.VISIBLE - } else { - binding.internetConnectionLaundry.visibility = View.GONE - } - - laundryRooms = ArrayList() - roomsData = ArrayList() - roomsDataResult = ArrayList() - laundryRoomsResult = ArrayList() - - count = 0 - for (i in 0 until numRooms) { - if (sp?.getBoolean(i.toString(), false) == true) { - count += 1 - } - } - - // add data - for (i in 0 until numRooms) { - if (sp!!.getBoolean(i.toString(), false)) { - addAvailability(i) - addRoom(i) - } - } - - // no rooms chosen - if (count == 0) { + binding.laundryHelpText.visibility = View.INVISIBLE + binding.laundryMachineRefresh.isRefreshing = false loadingPanel?.visibility = View.GONE - binding.laundryHelpText.visibility = View.VISIBLE - mAdapter = LaundryRoomAdapter(mContext, laundryRooms, roomsData, false) - binding.favoriteLaundryList.adapter = mAdapter + return false } - } - - @Synchronized - private fun addRoomToList(room: LaundryRoom) { - laundryRoomsResult.add(room) - } - @Synchronized - private fun addUsageToList(usage: LaundryUsage) { - roomsDataResult.add(usage) + binding.internetConnectionLaundry.visibility = View.GONE + return true } - - private fun addRoom(i: Int) { - mStudentLife.room(i)?.subscribe({ room -> - room.id = i - addRoomToList(room) - - if (laundryRoomsResult.size == count) { - - // sort laundry rooms data by hall name - roomsDataResult.sortWith { usage1, usage2 -> usage2.id - usage1.id } - - // sort laundry rooms by name - laundryRoomsResult.sortWith { room1, room2 -> room2.id - room1.id } - - // update UI - mActivity.runOnUiThread { - roomsData = roomsDataResult - laundryRooms = laundryRoomsResult - mAdapter = LaundryRoomAdapter(mContext, laundryRooms, roomsData, false) - try { - binding.favoriteLaundryList.adapter = mAdapter - binding.laundryHelpText.visibility = View.INVISIBLE - binding.laundryMachineRefresh.isRefreshing = false - } catch (e: Exception) { - FirebaseCrashlytics.getInstance().recordException(e) - } - no_results?.visibility = View.GONE - loadingPanel?.visibility = View.GONE - } - } - }, { - mActivity.runOnUiThread { - try { - binding.laundryHelpText.visibility = View.GONE - binding.laundryMachineRefresh.isRefreshing = false - } catch (e: Exception) { - FirebaseCrashlytics.getInstance().recordException(e) - } - loadingPanel?.visibility = View.GONE - no_results?.visibility = View.VISIBLE - Log.e("Laundry", "Error getting laundry data: " + it.stackTrace) - } - }) + private fun updateMachines() { + if (!getOnline()) { + return + } + mActivity.mNetworkManager.getAccessToken { + val bearerToken = "Bearer " + sharedPreferences + .getString(getString(R.string.access_token), "").toString() + laundryViewModel.getFavorites(mStudentLife, bearerToken) + } } - private fun addAvailability(i: Int) { - mStudentLife.usage(i)?.subscribe({ usage -> - usage.id = i - addUsageToList(usage) - }, { - // in case usage data not available - set chart to 0 - val newUsage = LaundryUsage() - newUsage.setWasherData() - newUsage.setDryerData() - newUsage.id = i - roomsDataResult.add(newUsage) - - mActivity.runOnUiThread { - loadingPanel?.visibility = View.GONE - no_results?.visibility = View.VISIBLE - try { - binding.laundryHelpText.visibility = View.GONE - binding.laundryMachineRefresh.isRefreshing = false - } catch (e: Exception) { - FirebaseCrashlytics.getInstance().recordException(e) - } - } - }) + private fun initAppBar() { + (binding.appbarHome.layoutParams as CoordinatorLayout.LayoutParams).behavior = ToolbarBehavior() + binding.titleView.text = getString(R.string.laundry) + binding.dateView.text = Utils.getCurrentSystemTime() + binding.laundryPreferences.setOnClickListener { + val fragmentManager = mActivity.supportFragmentManager + fragmentManager.beginTransaction() + .replace(R.id.content_frame, LaundrySettingsFragment()) + .addToBackStack("Laundry Settings Fragment") + .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + .commit() + } } - -} +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/LaundrySettingsFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/LaundrySettingsFragment.kt index fba8611e7..933451238 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/LaundrySettingsFragment.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/LaundrySettingsFragment.kt @@ -1,46 +1,41 @@ package com.pennapps.labs.pennmobile +import StudentLifeRf2 import android.content.Context import android.content.SharedPreferences import android.os.Bundle import android.view.* -import android.widget.Button -import android.widget.RelativeLayout import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.preference.PreferenceManager import com.google.firebase.crashlytics.FirebaseCrashlytics import com.pennapps.labs.pennmobile.adapters.LaundrySettingsAdapter -import com.pennapps.labs.pennmobile.api.StudentLife -import com.pennapps.labs.pennmobile.classes.LaundryRoomSimple import com.pennapps.labs.pennmobile.databinding.FragmentLaundrySettingsBinding +import com.pennapps.labs.pennmobile.viewmodels.LaundryViewModel import kotlinx.android.synthetic.main.include_main.* import kotlinx.android.synthetic.main.loading_panel.* import kotlinx.android.synthetic.main.no_results.* -import java.util.ArrayList -import java.util.HashMap class LaundrySettingsFragment : Fragment() { private lateinit var mActivity: MainActivity - private lateinit var mStudentLife: StudentLife + private lateinit var mStudentLife: StudentLifeRf2 private lateinit var mContext: Context - internal var mHelpLayout: RelativeLayout? = null - - private var sp: SharedPreferences? = null - private var mButton: Button? = null - - private var numRooms: Int = 0 private var _binding : FragmentLaundrySettingsBinding? = null private val binding get() = _binding!! + private val laundryViewModel : LaundryViewModel by activityViewModels() + private lateinit var sharedPreferences: SharedPreferences override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mStudentLife = MainActivity.studentLifeInstance + mStudentLife = MainActivity.studentLifeInstanceRf2 mActivity = activity as MainActivity + + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mActivity) + mContext = mActivity mActivity.closeKeyboard() - //setHasOptionsMenu(true) mActivity.toolbar.visibility = View.VISIBLE mActivity.hideBottomBar() } @@ -49,95 +44,40 @@ class LaundrySettingsFragment : Fragment() { _binding = FragmentLaundrySettingsBinding.inflate(inflater, container, false) val view = binding.root - // set up shared preferences - sp = PreferenceManager.getDefaultSharedPreferences(mContext) - - // reset laundry rooms button - mButton = binding.laundryRoomReset - mButton?.setOnClickListener { - // remove shared preferences - val editor = sp?.edit() - editor?.putInt(getString(R.string.num_rooms_selected_pref), 0) - editor?.apply() - - for (i in 0 until numRooms) { - editor?.remove(i.toString())?.apply() - } - } - // set up back button mActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true) - getHalls() return view } - - private fun getHalls() { - mStudentLife.laundryRooms() - .subscribe({ rooms -> - mActivity.runOnUiThread { - numRooms = rooms.size - // save number of rooms - val editor = sp?.edit() - editor?.putInt(getString(R.string.num_rooms_pref), numRooms) - editor?.apply() - - val hashMap = HashMap>() - val hallList = ArrayList() - - var i = 0 - // go through all the rooms - while (i < numRooms) { - - // new list for the rooms in the hall - var roomList: MutableList = ArrayList() - - // if hall name already exists, get the list of rooms and add to that - var hallName = rooms[i].location ?: "" - - if (hallList.contains(hallName)) { - roomList = hashMap[hallName] as MutableList - hashMap.remove(hallName) - hallList.remove(hallName) - } - - while (hallName == rooms[i].location) { - roomList.add(rooms[i]) - - i += 1 - if (i >= rooms.size) { - break - } - } - - // name formatting for consistency - if (hallName == "Lauder College House") { - hallName = "Lauder" - } - - // add the hall name to the list - hallList.add(hallName) - hashMap[hallName] = roomList - } - - val mAdapter = LaundrySettingsAdapter(mContext, hashMap, hallList) - try { - binding.laundryBuildingExpandableList.setAdapter(mAdapter) - } catch (e: Exception) { - FirebaseCrashlytics.getInstance().recordException(e) - } - - loadingPanel?.visibility = View.GONE - no_results?.visibility = View.GONE - } - }, { - mActivity.runOnUiThread { - loadingPanel?.visibility = View.GONE - no_results?.visibility = View.VISIBLE - mHelpLayout?.visibility = View.GONE - } - }) + private fun attachAdapter() { + val mAdapter = LaundrySettingsAdapter(mContext, laundryViewModel) + try { + binding.laundryBuildingExpandableList.setAdapter(mAdapter) + } catch (e: Exception) { + FirebaseCrashlytics.getInstance().recordException(e) + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + loadingPanel?.visibility = View.VISIBLE + + // if this value is already true, then simply attach adapter + if (laundryViewModel.loadedRooms.value != null && laundryViewModel.loadedRooms.value!!) { + attachAdapter() + loadingPanel?.visibility = View.GONE + no_results?.visibility = View.GONE + } else { + // otherwise, wait until the network request is done + laundryViewModel.loadedRooms.observe(viewLifecycleOwner) { loaded -> + if (loaded) { + attachAdapter() + loadingPanel?.visibility = View.GONE + no_results?.visibility = View.GONE + } + } + laundryViewModel.getHalls(mStudentLife) + } } override fun onResume() { @@ -149,8 +89,15 @@ class LaundrySettingsFragment : Fragment() { override fun onDestroyView() { super.onDestroyView() + if (laundryViewModel.existsDiff()) { + mActivity.mNetworkManager.getAccessToken { + val bearerToken = "Bearer " + sharedPreferences + .getString(getString(R.string.access_token), "").toString() + laundryViewModel.setFavoritesFromToggled(mStudentLife, bearerToken) + } + } mActivity.supportActionBar?.setDisplayHomeAsUpEnabled(false) mActivity.toolbar.visibility = View.GONE _binding = null } -} +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/MainActivity.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/MainActivity.kt index ab906a805..6968af358 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/MainActivity.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/MainActivity.kt @@ -1,5 +1,6 @@ package com.pennapps.labs.pennmobile +import StudentLifeRf2 import android.content.Context import android.content.SharedPreferences import android.content.pm.PackageManager @@ -42,7 +43,7 @@ import com.pennapps.labs.pennmobile.api.StudentLife import com.pennapps.labs.pennmobile.classes.* import com.pennapps.labs.pennmobile.components.sneaker.Sneaker import com.pennapps.labs.pennmobile.utils.Utils -import com.squareup.okhttp.OkHttpClient +import com.squareup.okhttp.OkHttpClient as SquareOkHttpClient import eightbitlab.com.blurview.RenderScriptBlur import kotlinx.android.synthetic.main.custom_sneaker_view.view.* import kotlinx.android.synthetic.main.include_main.* @@ -52,7 +53,11 @@ import retrofit.android.AndroidLog import retrofit.client.OkClient import retrofit.converter.GsonConverter import java.util.concurrent.TimeUnit - +import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import okhttp3.OkHttpClient class MainActivity : AppCompatActivity() { private var tabShowed = false @@ -270,6 +275,7 @@ class MainActivity : AppCompatActivity() { val GSR_ID = R.id.nav_gsr private var mStudentLife: StudentLife? = null + private var mStudentLifeRf2: StudentLifeRf2? = null private var mPlatform: Platform? = null private var mCampusExpress: CampusExpress? = null @@ -307,6 +313,28 @@ class MainActivity : AppCompatActivity() { return mPlatform!! } + val studentLifeInstanceRf2: StudentLifeRf2 + get() { + if (mStudentLifeRf2 == null) { + val okHttpClient = OkHttpClient.Builder() + .connectTimeout(35, TimeUnit.SECONDS) + .readTimeout(35, TimeUnit.SECONDS) + .writeTimeout(35, TimeUnit.SECONDS) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://pennmobile.org/api/") + .client(okHttpClient) + .addConverterFactory(ScalarsConverterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .build() + mStudentLifeRf2 = retrofit.create(StudentLifeRf2::class.java) + } + return mStudentLifeRf2!! + } + + @JvmStatic val studentLifeInstance: StudentLife get() { @@ -317,11 +345,7 @@ class MainActivity : AppCompatActivity() { gsonBuilder.registerTypeAdapter(DiningHall::class.java, MenuSerializer()) // gets room gsonBuilder.registerTypeAdapter(object : TypeToken() {}.type, LaundryRoomSerializer()) - // gets laundry room list - gsonBuilder.registerTypeAdapter(object : TypeToken?>() {}.type, LaundryRoomListSerializer()) gsonBuilder.registerTypeAdapter(object : TypeToken?>() {}.type, GsrLocationSerializer()) - // gets laundry usage - gsonBuilder.registerTypeAdapter(object : TypeToken() {}.type, LaundryUsageSerializer()) // gets laundry preferences (used only for testing) gsonBuilder.registerTypeAdapter(object : TypeToken?>() {}.type, LaundryPrefSerializer()) gsonBuilder.registerTypeAdapter(object : TypeToken?>() {}.type, FlingEventSerializer()) @@ -332,7 +356,7 @@ class MainActivity : AppCompatActivity() { // gets posts gsonBuilder.registerTypeAdapter(object: TypeToken?>() {}.type, PostsSerializer()) val gson = gsonBuilder.create() - val okHttpClient = OkHttpClient() + val okHttpClient = SquareOkHttpClient() okHttpClient.setConnectTimeout(35, TimeUnit.SECONDS) // Connection timeout okHttpClient.setReadTimeout(35, TimeUnit.SECONDS) // Read timeout okHttpClient.setWriteTimeout(35, TimeUnit.SECONDS) // Write timeout diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/LaundrySettingsAdapter.java b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/LaundrySettingsAdapter.java deleted file mode 100644 index 0635520ce..000000000 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/LaundrySettingsAdapter.java +++ /dev/null @@ -1,307 +0,0 @@ -package com.pennapps.labs.pennmobile.adapters; - -import android.content.Context; -import android.content.SharedPreferences; -import androidx.preference.PreferenceManager; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseExpandableListAdapter; -import android.widget.ImageView; -import android.widget.Switch; -import android.widget.TextView; - -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.LaundryRequest; -import com.pennapps.labs.pennmobile.classes.LaundryRoomSimple; -import com.pennapps.labs.pennmobile.api.OAuth2NetworkManager; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -import retrofit.ResponseCallback; -import retrofit.RetrofitError; -import retrofit.client.Response; -import rx.functions.Action1; - -/** - * Created by Jackie on 2017-10-13. - */ - -public class LaundrySettingsAdapter extends BaseExpandableListAdapter { - private final List laundryHalls; - private final HashMap> laundryRooms; - private final Context mContext; - private final SharedPreferences sp; - private final String s; - private final List switches = new ArrayList<>(); - private final int maxNumRooms = 3; - private StudentLife studentLife; - private String bearerToken; - - - public LaundrySettingsAdapter(Context context, HashMap> laundryRooms, List laundryHalls) { - this.mContext = context; - this.laundryHalls = laundryHalls; - this.laundryRooms = laundryRooms; - sp = PreferenceManager.getDefaultSharedPreferences(mContext); - s = mContext.getString(R.string.num_rooms_selected_pref); - MainActivity mainActivity = (MainActivity) mContext; - bearerToken = "Bearer " + sp.getString(mainActivity.getString(R.string.access_token), ""); - - studentLife = MainActivity.getStudentLifeInstance(); - - // first time - if (sp.getInt(s, -1) == -1) { - SharedPreferences.Editor editor = sp.edit(); - editor.putInt(s, 0); - editor.apply(); - } - } - - @Override - public int getGroupCount() { - return laundryHalls.size(); - } - - @Override - public int getChildrenCount(int i) { - return laundryRooms.get(laundryHalls.get(i)).size(); - } - - @Override - public Object getGroup(int i) { - return laundryHalls.get(i); - } - - @Override - public Object getChild(int i, int i1) { - return laundryRooms.get(laundryHalls.get(i)).get(i1); - } - - @Override - public long getGroupId(int i) { - return i; - } - - @Override - public long getChildId(int i, int i1) { - return i1; - } - - @Override - public boolean hasStableIds() { - return false; - } - - @Override - // view for the laundry buildings - public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) { - - final String laundryHallName = (String) this.getGroup(i); - - if (view == null) { - LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(R.layout.laundry_settings_parent_item, null); - } - - TextView textView = view.findViewById(R.id.laundry_building_name); - textView.setText(laundryHallName); - ImageView imageView = view.findViewById(R.id.laundry_building_dropdown); - - final Switch buildingSwitch = view.findViewById(R.id.laundry_building_favorite_switch); - - // if there is only one laundry room in the building, don't have dropdown - if (laundryRooms.get(laundryHallName).size() == 1) { - buildingSwitch.setVisibility(View.VISIBLE); - imageView.setVisibility(View.GONE); - - final LaundryRoomSimple laundryRoom = laundryRooms.get(laundryHallName).get(0); - - // set the Switch to the correct on or off - buildingSwitch.setChecked(sp.getBoolean(Integer.toString(laundryRoom.id), false)); - - // add the switch to the list - to aid with disabling - if (!switches.contains(buildingSwitch)) { - switches.add(buildingSwitch); - } - - // max number of rooms - if (sp.getInt(s, -1) >= maxNumRooms) { - buildingSwitch.setEnabled(buildingSwitch.isChecked()); - } - - buildingSwitch.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - boolean isChecked = buildingSwitch.isChecked(); - SharedPreferences.Editor editor = sp.edit(); - String id = Integer.toString(laundryRoom.id); - editor.putBoolean(id, isChecked); - editor.apply(); - - // update the numRoomSelected - if (isChecked) { - editor.putString(id + mContext.getString(R.string.location), laundryRoom.location); - editor.putInt(s, sp.getInt(s, -1) + 1); - editor.apply(); - } else { - editor.putInt(s, sp.getInt(s, -1) - 1); - editor.apply(); - } - updateSwitches(); - sendPreferencesData(); - } - }); - - } else { - buildingSwitch.setVisibility(View.GONE); - imageView.setImageResource(R.drawable.ic_expand); - imageView.setVisibility(View.VISIBLE); - } - - // if expanded - if (b) { - imageView.setImageResource(R.drawable.ic_collapse); - } - - return view; - } - - @Override - public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) { - - final LaundryRoomSimple laundryRoom = (LaundryRoomSimple) this.getChild(i, i1); - - if (view == null) { - LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(R.layout.laundry_settings_child_item, null); - } - - TextView textView = view.findViewById(R.id.laundry_room_name); - String name = laundryRoom.name; - textView.setText(name); - - - final Switch favoriteSwitch = view.findViewById(R.id.laundry_favorite_switch); - - // set the Switch to the correct on or off - favoriteSwitch.setChecked(sp.getBoolean(Integer.toString(laundryRoom.id), false)); - - // add the switch to the list - to aid with disabling - if (!switches.contains(favoriteSwitch)) { - switches.add(favoriteSwitch); - } - - // max number of rooms - if (sp.getInt(s, -1) >= maxNumRooms) { - favoriteSwitch.setEnabled(favoriteSwitch.isChecked()); - } - - favoriteSwitch.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - boolean isChecked = favoriteSwitch.isChecked(); - SharedPreferences.Editor editor = sp.edit(); - String id = Integer.toString(laundryRoom.id); - editor.putBoolean(id, isChecked); - editor.apply(); - - // update the numRoomSelected - if (isChecked) { - editor.putString(id + mContext.getString(R.string.location), laundryRoom.location); - editor.putInt(s, sp.getInt(s, -1) + 1); - editor.apply(); - } else { - editor.putInt(s, sp.getInt(s, -1) - 1); - editor.apply(); - } - - updateSwitches(); - sendPreferencesData(); - } - }); - - return view; - } - - @Override - public boolean isChildSelectable(int i, int i1) { - return false; - } - - private void updateSwitches() { - - // maximum 3 rooms selected - disable all other switches - if (sp.getInt(s, -1) >= maxNumRooms) { - Iterator iter = switches.iterator(); - while (iter.hasNext()) { - Switch nextSwitch = iter.next(); - if (!nextSwitch.isChecked()) { - nextSwitch.setEnabled(false); - } - } - } - // less than 3 rooms selected - all switches enabled - else { - Iterator iter = switches.iterator(); - while (iter.hasNext()) { - Switch nextSwitch = iter.next(); - nextSwitch.setEnabled(true); - } - } - } - - private void getPreferencesData() { - // warning, network call is unsafe - studentLife = MainActivity.getStudentLifeInstance(); - studentLife.getLaundryPref(bearerToken).subscribe(new Action1>() { - @Override - public void call(List integers) { - } - }, new Action1() { - @Override - public void call(Throwable throwable) { - } - }); - } - - private void sendPreferencesData() { - final List favoriteLaundryRooms = new ArrayList<>(); - for (int i = 0; i < sp.getInt(mContext.getString(R.string.num_rooms_pref), 100); i++) { - if (sp.getBoolean(Integer.toString(i), false)) { - favoriteLaundryRooms.add(i); - } - } - - if (favoriteLaundryRooms.isEmpty()) { - return; - } - - MainActivity mainActivity = (MainActivity) mContext; - - OAuth2NetworkManager oauth = new OAuth2NetworkManager(mainActivity); - oauth.getAccessToken(() -> { - bearerToken = "Bearer " + sp.getString(mainActivity.getString(R.string.access_token), ""); - studentLife.sendLaundryPref(bearerToken, new LaundryRequest(favoriteLaundryRooms), - new ResponseCallback() { - @Override - public void success(Response response) { - Log.i("Laundry", "Saved laundry preferences"); - } - - @Override - public void failure(RetrofitError error) { - Log.e("Laundry", "Error saving laundry preferences: " + error, error); - } - }); - return null; - } - ); - } -} diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/LaundrySettingsAdapter.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/LaundrySettingsAdapter.kt new file mode 100644 index 000000000..47c67b44f --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/adapters/LaundrySettingsAdapter.kt @@ -0,0 +1,165 @@ +package com.pennapps.labs.pennmobile.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseExpandableListAdapter +import android.widget.ImageView +import android.widget.Switch +import android.widget.TextView +import com.pennapps.labs.pennmobile.R +import com.pennapps.labs.pennmobile.classes.LaundryRoomSimple +import com.pennapps.labs.pennmobile.viewmodels.LaundryViewModel + +/** + * Created by Jackie on 2017-10-13. + * Modified by Aaron on 2024-03-20. + */ +class LaundrySettingsAdapter( + private val mContext: Context, + private val dataModel: LaundryViewModel +) : BaseExpandableListAdapter() { + private val switches: MutableList = ArrayList() + + init { + dataModel.setToggled() + } + + override fun getGroupCount(): Int { + return dataModel.getGroupCount() + } + + override fun getChildrenCount(i: Int): Int { + return dataModel.getChildrenCount(i) + } + + override fun getGroup(i: Int): Any { + return dataModel.getGroup(i) + } + + override fun getChild(i: Int, i1: Int): Any { + return dataModel.getChild(i, i1) + } + + override fun getGroupId(i: Int): Long { + return i.toLong() + } + + override fun getChildId(i: Int, i1: Int): Long { + return i1.toLong() + } + + override fun hasStableIds(): Boolean { + return false + } + + // view for the laundry buildings + override fun getGroupView(i: Int, b: Boolean, origView: View?, viewGroup: ViewGroup): View { + val laundryHallName = getGroup(i) as String + val view : View = if (origView == null) { + val inflater = + mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + inflater.inflate(R.layout.laundry_settings_parent_item, null) + } else { + origView + } + val textView = view.findViewById(R.id.laundry_building_name) + textView.text = laundryHallName + val imageView = view.findViewById(R.id.laundry_building_dropdown) + val buildingSwitch = view.findViewById(R.id.laundry_building_favorite_switch) + + // if there is only one laundry room in the building, don't have dropdown + if (dataModel.getRooms(laundryHallName)!!.size == 1) { + buildingSwitch.visibility = View.VISIBLE + imageView.visibility = View.GONE + val laundryRoom = dataModel.getRooms(laundryHallName)!![0] + val roomId: Int = laundryRoom.id!! + // set the Switch to the correct on or off + buildingSwitch.isChecked = dataModel.isChecked(roomId) + + // add the switch to the list - to aid with disabling + if (!switches.contains(buildingSwitch)) { + switches.add(buildingSwitch) + } + + // max number of rooms + if (dataModel.isFull()) { + buildingSwitch.isEnabled = buildingSwitch.isChecked + } + buildingSwitch.setOnClickListener { + if(dataModel.toggle(roomId)) { + updateSwitches() + } + } + } else { + buildingSwitch.visibility = View.GONE + imageView.setImageResource(R.drawable.ic_expand) + imageView.visibility = View.VISIBLE + } + + // if expanded + if (b) { + imageView.setImageResource(R.drawable.ic_collapse) + } + return view + } + + override fun getChildView(i: Int, i1: Int, b: Boolean, origView: View?, viewGroup: ViewGroup): View { + val laundryRoom = getChild(i, i1) as LaundryRoomSimple + val view : View = if (origView == null) { + val inflater = + mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + inflater.inflate(R.layout.laundry_settings_child_item, null) + } else { + origView + } + val textView = view.findViewById(R.id.laundry_room_name) + val name = laundryRoom.name + textView.text = name + val favoriteSwitch = view.findViewById(R.id.laundry_favorite_switch) + + val roomId : Int = laundryRoom.id!! + + // set the Switch to the correct on or off + favoriteSwitch.isChecked = dataModel.isChecked(roomId) + + // add the switch to the list - to aid with disabling + if (!switches.contains(favoriteSwitch)) { + switches.add(favoriteSwitch) + } + + // max number of rooms + if (dataModel.isFull()) { + favoriteSwitch.isEnabled = favoriteSwitch.isChecked + } + favoriteSwitch.setOnClickListener { + if (dataModel.toggle(roomId)) { + updateSwitches() + } + } + return view + } + + override fun isChildSelectable(i: Int, i1: Int): Boolean { + return false + } + + private fun updateSwitches() { + if (dataModel.isFull()) { + val iter: Iterator = switches.iterator() + while (iter.hasNext()) { + val nextSwitch = iter.next() + if (!nextSwitch.isChecked) { + nextSwitch.isEnabled = false + } + } + } else { + val iter: Iterator = switches.iterator() + while (iter.hasNext()) { + val nextSwitch = iter.next() + nextSwitch.isEnabled = true + } + } + } +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/Serializer.java b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/Serializer.java index 64f93c2c2..2d5df66cc 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/Serializer.java +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/Serializer.java @@ -142,17 +142,6 @@ public LaundryRoom deserialize(JsonElement je, Type type, JsonDeserializationCon } } - // gets laundry room list - public static class LaundryRoomListSerializer implements JsonDeserializer> { - @Override - public List deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) - throws JsonParseException { - JsonElement content = je.getAsJsonArray(); - return new Gson().fromJson(content, new TypeToken>() { - }.getType()); - } - } - // gets gsr locations public static class GsrLocationSerializer implements JsonDeserializer> { @Override @@ -173,17 +162,6 @@ public List deserialize(JsonElement je, Type type, JsonDeserializat return locations; } } - - // gets laundry usage - public static class LaundryUsageSerializer implements JsonDeserializer { - @Override - public LaundryUsage deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) - throws JsonParseException { - JsonElement content = je.getAsJsonObject(); - return new Gson().fromJson(content, new TypeToken() { - }.getType()); - } - } // gets laundry pref data from server public static class LaundryPrefSerializer implements JsonDeserializer> { diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/StudentLife.java b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/StudentLife.java index 15f3413e5..89be0e7c8 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/StudentLife.java +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/StudentLife.java @@ -81,9 +81,6 @@ Observable getDiningPreferences( @Header("Authorization") String bearerToken ); - @GET("/laundry/halls/ids") - Observable> laundryRooms(); - @GET("/laundry/hall/{id}") Observable room( @Path("id") int id); @@ -115,10 +112,6 @@ void bookGSR( @Field("room_name") String roomName, Callback callback); - @GET("/laundry/usage/{id}") - Observable usage( - @Path("id") int id); - @GET("/events/fling") Observable> getFlingEvents(); @@ -155,12 +148,6 @@ void saveAccount( Observable> getLaundryPref( @Header("Authorization") String bearerToken); - @POST("/laundry/preferences/") - void sendLaundryPref( - @Header("Authorization") String bearerToken, - @Body LaundryRequest rooms, - Callback callback); - @Headers({"Content-Type: application/json"}) @POST("/dining/preferences/") void sendDiningPref( diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/StudentLifeRf2.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/StudentLifeRf2.kt new file mode 100644 index 000000000..daf7aa1f7 --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/StudentLifeRf2.kt @@ -0,0 +1,32 @@ +import com.pennapps.labs.pennmobile.classes.LaundryPreferences +import com.pennapps.labs.pennmobile.classes.LaundryRequest +import com.pennapps.labs.pennmobile.classes.LaundryRoom +import com.pennapps.labs.pennmobile.classes.LaundryRoomSimple +import com.pennapps.labs.pennmobile.classes.LaundryUsage +import okhttp3.ResponseBody +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.POST +import retrofit2.http.Path + +interface StudentLifeRf2 { + @GET("laundry/halls/ids") + suspend fun laundryRooms(): Response> + + @GET("laundry/hall/{id}") + suspend fun room(@Path("id") id: Int): Response + + @GET("laundry/usage/{id}") + suspend fun usage(@Path("id") id: Int): Response + + @GET("laundry/preferences") + suspend fun getLaundryPref(@Header("Authorization") bearerToken: String): Response + + @POST("laundry/preferences/") + suspend fun sendLaundryPref( + @Header("Authorization") bearerToken: String, + @Body rooms: LaundryRequest + ): Response +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/HomepageViewModel.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/HomepageViewModel.kt index f59e607df..3e3201d03 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/HomepageViewModel.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/HomepageViewModel.kt @@ -98,7 +98,6 @@ class HomepageViewModel : HomepageDataModel, ViewModel() { setNewsBlurView(false) setPostBlurView(false) } - } /** @@ -114,7 +113,7 @@ class HomepageViewModel : HomepageDataModel, ViewModel() { for (i in 0 until NUM_CELLS) { if (prevList[i] != homepageCells[i]) { - updatedIndices.add(i); + updatedIndices.add(i) Log.i(UPDATE_TAG, "updated index ${i}") } else { Log.i(UPDATE_TAG, "saved an update at index ${i}") diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/LaundryPreferences.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/LaundryPreferences.kt new file mode 100644 index 000000000..3aacf7bdf --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/LaundryPreferences.kt @@ -0,0 +1,10 @@ +package com.pennapps.labs.pennmobile.classes + +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName + +class LaundryPreferences { + @SerializedName("rooms") + @Expose + val rooms: List? = null +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/LaundryRoomFavorites.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/LaundryRoomFavorites.kt new file mode 100644 index 000000000..a59c4a515 --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/classes/LaundryRoomFavorites.kt @@ -0,0 +1,6 @@ +package com.pennapps.labs.pennmobile.classes + +class LaundryRoomFavorites { + val favoriteRooms = ArrayList() + val roomsData : ArrayList = ArrayList() +} \ No newline at end of file diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/viewmodels/LaundryViewModel.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/viewmodels/LaundryViewModel.kt new file mode 100644 index 000000000..9760b8de2 --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/viewmodels/LaundryViewModel.kt @@ -0,0 +1,253 @@ +package com.pennapps.labs.pennmobile.viewmodels + +import StudentLifeRf2 +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.pennapps.labs.pennmobile.classes.LaundryRequest +import com.pennapps.labs.pennmobile.classes.LaundryRoom +import com.pennapps.labs.pennmobile.classes.LaundryRoomFavorites +import com.pennapps.labs.pennmobile.classes.LaundryRoomSimple +import com.pennapps.labs.pennmobile.classes.LaundryUsage +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlin.coroutines.CoroutineContext + +class LaundryViewModel : ViewModel() { + companion object { + const val maxNumRooms = 3 + } + + private val laundryRooms: HashMap> = HashMap() + private val laundryHalls: ArrayList = ArrayList() + + private val _loadedRooms = MutableLiveData(false) + val loadedRooms : LiveData + get() = _loadedRooms + + private val _favoriteRooms = MutableLiveData(LaundryRoomFavorites()) + + private val curToggled : MutableSet = HashSet() + + val favoriteRooms : LiveData + get() = _favoriteRooms + + private val favoritesMutex = Mutex() + private fun updateFavorites() { + // kinda clown, but rebind so the observer can observe a change + _favoriteRooms.postValue(_favoriteRooms.value) + } + private suspend fun replaceFavorites(rooms: List, usages: List) { + favoritesMutex.withLock { + _favoriteRooms.value?.favoriteRooms?.clear() + _favoriteRooms.value?.roomsData?.clear() + + _favoriteRooms.value?.favoriteRooms?.addAll(rooms) + _favoriteRooms.value?.roomsData?.addAll(usages) + } + } + + private suspend fun populateFavorites(context: CoroutineContext, + studentLife: StudentLifeRf2, + favoriteIdList: List) { + val rooms = ArrayList() + val usages = ArrayList() + + for (roomId in favoriteIdList) { + var addRoomSuccess = false + var addUsageSuccess = false + val addRoom = CoroutineScope(context).launch { + val roomResponse = studentLife.room(roomId) + addRoomSuccess = if (roomResponse.isSuccessful) { + val room = roomResponse.body()!! + room.id = roomId + rooms.add(room) + true + } else { + Log.i("Laundry", "Failed to get room data") + false + } + } + val addUsage = CoroutineScope(context).launch { + val usageResponse = studentLife.usage(roomId) + addUsageSuccess = if (usageResponse.isSuccessful) { + usages.add(usageResponse.body()!!) + true + } else { + Log.i("Laundry", "Failed to get usage data") + false + } + } + + addRoom.join() + addUsage.join() + + if (addUsageSuccess && !addRoomSuccess) { + rooms.removeLast() + } + if (!addUsageSuccess && addRoomSuccess) { + usages.removeLast() + } + } + replaceFavorites(rooms, usages) + updateFavorites() + } + fun getFavorites(studentLife: StudentLifeRf2, bearerToken : String) { + CoroutineScope(Dispatchers.IO).launch { + val favoriteIdList : MutableList = mutableListOf() + val response = studentLife.getLaundryPref(bearerToken) + if (response.isSuccessful) { + val prefs = response.body()!!.rooms + for (room in prefs!!) { + favoriteIdList.add(room) + } + } else { + Log.i("Laundry", "Failed to get preferences") + } + populateFavorites(coroutineContext, studentLife, favoriteIdList) + } + } + + fun getHalls(studentLife: StudentLifeRf2) { + if (_loadedRooms.value!!) { + return + } + CoroutineScope(Dispatchers.IO).launch { + val roomsResponse = studentLife.laundryRooms() + if (roomsResponse.isSuccessful) { + val rooms = roomsResponse.body()!! + laundryRooms.clear() + laundryHalls.clear() + + val numRooms = rooms.size + var i = 0 + // go through all the rooms + while (i < numRooms) { + + // new list for the rooms in the hall + var roomList: MutableList = ArrayList() + + // if hall name already exists, get the list of rooms and add to that + var hallName = rooms[i].location ?: "" + + if (laundryHalls.contains(hallName)) { + roomList = laundryRooms[hallName] as MutableList + laundryRooms.remove(hallName) + laundryHalls.remove(hallName) + } + + while (hallName == rooms[i].location) { + roomList.add(rooms[i]) + + i += 1 + if (i >= rooms.size) { + break + } + } + + // name formatting for consistency + if (hallName == "Lauder College House") { + hallName = "Lauder" + } + + // add the hall name to the list + laundryHalls.add(hallName) + laundryRooms[hallName] = roomList + } + } else { + Log.i("Laundry", "Failed to get laundry rooms") + } + + _loadedRooms.postValue(true) + } + } + fun existsDiff() : Boolean { + var diff = false + runBlocking { + favoritesMutex.withLock { + if (_favoriteRooms.value == null) { + return@runBlocking + } + if (_favoriteRooms.value!!.favoriteRooms.size != curToggled.size) { + diff = true + return@runBlocking + } + for (room in _favoriteRooms.value!!.favoriteRooms) { + if (!curToggled.contains(room.id)) { + diff = true + return@runBlocking + } + } + } + } + return diff + } + + private suspend fun sendPreferences(studentLife: StudentLifeRf2, bearerToken: String, + favoriteIdList: List) { + val laundryRequest = LaundryRequest(favoriteIdList) + val response = studentLife.sendLaundryPref(bearerToken, laundryRequest) + if (response.isSuccessful) { + Log.i("Laundry Preferences", "Successfully updated preferences") + } else { + Log.i("Laundry Preferences", "Error updating preferences") + } + } + fun setFavoritesFromToggled(studentLife: StudentLifeRf2, bearerToken: String) { + CoroutineScope(Dispatchers.IO).launch { + // make a copy of the set + val favoriteIdList = curToggled.toList() + populateFavorites(coroutineContext, studentLife, favoriteIdList) + + // send + sendPreferences(studentLife, bearerToken, favoriteIdList) + } + } + + fun setToggled() { + curToggled.clear() + runBlocking { + favoritesMutex.withLock { + for (room in _favoriteRooms.value!!.favoriteRooms) { + curToggled.add(room.id) + } + } + } + } + + // returns true if there is a change state + fun toggle(roomId: Int) : Boolean { + val origState = curToggled.size >= maxNumRooms + if (curToggled.contains(roomId)) { + curToggled.remove(roomId) + + } else { + curToggled.add(roomId) + } + return (curToggled.size >= maxNumRooms).xor(origState) + } + fun getGroupCount() : Int { + return laundryHalls.size + } + fun getChildrenCount(i: Int): Int { + return laundryRooms[laundryHalls[i]]!!.size + } + fun getGroup(i: Int): Any { + return laundryHalls[i] + } + fun getChild(i: Int, i1: Int): Any { + return laundryRooms[laundryHalls[i]]!![i1] + } + fun getRooms(hallName: String): List? { + return laundryRooms[hallName] + } + fun isChecked(roomId: Int) : Boolean { + return curToggled.contains(roomId) + } + fun isFull() = (curToggled.size >= maxNumRooms) +} \ No newline at end of file