asfoury/projmag

View on GitHub
app/src/main/java/com/sdp13epfl2021/projmag/activities/ProjectsListActivity.kt

Summary

Maintainability
B
5 hrs
Test Coverage
B
80%
package com.sdp13epfl2021.projmag.activities

import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.CheckBox
import android.widget.ImageButton
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.SwitchCompat
import androidx.recyclerview.widget.RecyclerView
import com.sdp13epfl2021.projmag.MainActivity.MainActivityCompanion.fromLinkString
import com.sdp13epfl2021.projmag.MainActivity.MainActivityCompanion.projectIdString
import com.sdp13epfl2021.projmag.R
import com.sdp13epfl2021.projmag.adapter.ProjectAdapter
import com.sdp13epfl2021.projmag.database.interfaces.CandidatureDatabase
import com.sdp13epfl2021.projmag.database.interfaces.ProjectDatabase
import com.sdp13epfl2021.projmag.database.interfaces.ProjectId
import com.sdp13epfl2021.projmag.database.interfaces.UserdataDatabase
import com.sdp13epfl2021.projmag.model.Candidature
import com.sdp13epfl2021.projmag.model.ImmutableProject
import com.sdp13epfl2021.projmag.model.ProjectFilter
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import javax.inject.Named

/**
 * Displays a list of projects. User can filter based on various criteria and search by name.
 */
@AndroidEntryPoint
class ProjectsListActivity : AppCompatActivity() {

    @Inject
    lateinit var userDB: UserdataDatabase

    @Inject
    lateinit var projectDB: ProjectDatabase

    @Inject
    lateinit var candidatureDatabase: CandidatureDatabase

    @Inject
    @Named("currentUserId")
    lateinit var userId: String


    private lateinit var projectAdapter: ProjectAdapter

    private lateinit var recyclerView: RecyclerView
    private val appliedProjects: MutableList<ProjectId> = ArrayList()
    private val favoriteList: MutableList<ProjectId> = ArrayList()
    private var projectFilter: ProjectFilter = ProjectFilter()
    private var userPref: ProjectFilter = ProjectFilter()
    private var useFilterPref: Boolean = false

    /**
     * Creates and displays list of projects.
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_projects_list)

        updateAppliedProjects()

        userDB.getListOfFavoriteProjects({
            favoriteList.addAll(it)
        }, {})

        // if app was opened from deep link, extract relevant information to open the right project
        val fromLink = intent.getBooleanExtra(fromLinkString, false)
        var projectId = ""
        if (fromLink) {
            projectId = intent.getStringExtra(projectIdString) ?: ""
        }


        recyclerView = findViewById(R.id.recycler_view_project)

        projectAdapter =
            ProjectAdapter(this, projectDB, recyclerView, fromLink, projectId)
        recyclerView.adapter = projectAdapter



        recyclerView.setHasFixedSize(false)

        setUpFab()
        addListenersToAppliedProjects()
    }

    private fun addListenersToAppliedProjects() {
        appliedProjects.forEach {
            candidatureDatabase.addListener(it) { _: ProjectId, list: List<Candidature> ->
                val ownCandidatureThatChanged: Candidature? =
                    list.find { candidature -> candidature.userId == userId }
                if (ownCandidatureThatChanged?.state == Candidature.State.Accepted) {
                    val otherCandidatures =
                        appliedProjects.filter { projectId -> (ownCandidatureThatChanged.projectId != projectId) }
                    otherCandidatures.forEach { otherCandidatureId ->
                        candidatureDatabase.removeCandidature(
                            otherCandidatureId,
                            userId,
                            {},
                            {}
                        )
                        userDB.applyUnapply(false, otherCandidatureId, {}, {})
                    }
                }
            }
        }

    }

    private fun setUpFab() {
        // get the fab and make it go to the Form activity
        val fab: View = findViewById(R.id.fab)
        fab.setOnClickListener {
            val intent = Intent(this, ProjectCreationActivity::class.java)
            startActivity(intent)
        }

        if (!UserTypeChoice.isProfessor) {
            fab.visibility = View.INVISIBLE
        }
    }

    /**
     * Adds search button and functionality, filter button, user button to menu.
     */
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_project_list, menu)
        val item = menu?.findItem(R.id.searchButton)
        val searchView: SearchView = item?.actionView as SearchView
        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String?): Boolean {
                return false
            }

            override fun onQueryTextChange(newText: String?): Boolean {

                projectAdapter.getFilter(projectFilter).filter(newText)

                return false
            }
        })

        return super.onCreateOptionsMenu(menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean =
        when (item.itemId) {
            R.id.profileButton -> {
                val intent = Intent(this, ProfileEditPageActivity::class.java)
                startActivity(intent)
                true
            }

            R.id.filterButton -> {
                openFilterDialog()
                true
            }

            else -> super.onOptionsItemSelected(item)
        }

    override fun onResume() {
        updateAppliedProjects()
        updateFavoriteProjects()
        updatePreferences()
        super.onResume()
    }


    /**
     * Update the list of projects, which the user applied to, from the Database
     */
    private fun updateAppliedProjects() {
        userDB.getListOfAppliedToProjects({ list ->
            appliedProjects.clear()
            appliedProjects.addAll(list)
        }, {})
    }

    private fun updateFavoriteProjects() {
        userDB.getListOfFavoriteProjects({ list ->
            favoriteList.clear()
            favoriteList.addAll(list)
        }, {})
    }


    /**
     * TODO Remove once the tests have been fixed
     */
    fun getItemAdapter(): ProjectAdapter {
        return projectAdapter
    }

    /**
     * TODO Remove once the tests have been fixed
     */
    fun getRecyclerView(): RecyclerView {
        return recyclerView
    }

    /**
     * Update the current ProjectFilter with the given value,
     * and updates its applicationCheck function
     *
     * @param pf the new ProjectFilter
     */
    private fun setProjectFilter(pf: ProjectFilter?) {
        pf?.apply {
            setApplicationCheck { checkIfApplied(it) }
            setFavoriteCheck { checkIfFavorite(it) }
            setOwnCheck { checkIfOwn(it) }
            projectFilter = this
        }
    }


    /**
     * Opens a dialog with filtering options for the project list
     */
    private fun openFilterDialog() {
        val builder = AlertDialog.Builder(this)
        val view = constructDialogView()
        builder
            .setView(view)
            .setNeutralButton(getString(R.string.clear)) { _, _ ->

                projectFilter = ProjectFilter()
                projectAdapter.filter.filter("")

            }
            .setNegativeButton(getString(R.string.cancel)) { _, _ -> }
            .setPositiveButton(getString(R.string.ok)) { _, _ ->
                filter(view)
            }.show()
    }

    /**
     * The filter view with initialised parameters
     *
     * @return Dialog view
     */
    @SuppressLint("InflateParams")
    private fun constructDialogView(): View {

        val pf = projectFilter
        val view = layoutInflater.inflate(R.layout.project_filter_settings, null)

        val applied = view.findViewById<CheckBox>(R.id.filter_applied)
        val own = view.findViewById<CheckBox>(R.id.filter_own)

        if (UserTypeChoice.isProfessor) {
            applied.visibility = View.INVISIBLE
        } else {
            own.visibility = View.INVISIBLE
        }

        view.findViewById<CheckBox>(R.id.filter_bachelor).isChecked = pf.bachelor
        view.findViewById<CheckBox>(R.id.filter_master).isChecked = pf.master
        applied.isChecked = pf.applied
        own.isChecked = pf.own
        view.findViewById<CheckBox>(R.id.filter_favorites).isChecked = pf.favorite

        view.findViewById<ImageButton>(R.id.filter_settings_button).setOnClickListener {
            startActivity(Intent(this, PreferencesActivity::class.java))
        }

        setUpPreferencesSwitch(view)

        return view
    }

    private fun setUpPreferencesSwitch(view: View) {
        view.findViewById<SwitchCompat>(R.id.filter_preferences_switch).apply {
            setOnCheckedChangeListener { _, isChecked ->
                useFilterPref = isChecked

                updatePreferences()

                view.findViewById<View>(R.id.filter_preferences_layout).visibility =
                    if (isChecked) View.GONE else View.VISIBLE
            }
            isChecked = useFilterPref
        }
    }

    /**
     * Update the project filter, adapter and the list, with the
     * data given in the view.
     *
     * @param view The dialog view with data, given by the user
     */
    private fun filter(view: View) {
        val bachelor = view.findViewById<CheckBox>(R.id.filter_bachelor).isChecked
        val master = view.findViewById<CheckBox>(R.id.filter_master).isChecked
        val applied = view.findViewById<CheckBox>(R.id.filter_applied).isChecked
        val favorite = view.findViewById<CheckBox>(R.id.filter_favorites).isChecked
        val own = view.findViewById<CheckBox>(R.id.filter_own).isChecked

        setProjectFilter(
            if (useFilterPref) {
                userPref
            } else {
                ProjectFilter(
                    bachelor = bachelor,
                    master = master,
                    applied = applied,
                    favorite = favorite,
                    own = own
                )
            }
        )
        projectAdapter.getFilter(projectFilter).filter("")

    }

    /**
     * Check if the user applied to the given project
     *
     * @param project the project to check
     * @return `true` if the user applied, `false` otherwise
     */
    private fun checkIfApplied(project: ImmutableProject): Boolean =
        appliedProjects.contains(project.id)

    /**
     * Checks if the project is contained in the current user favorite list
     *
     * @param project the project to check
     * @return true if the project is in the favorites, false else
     */
    private fun checkIfFavorite(project: ImmutableProject): Boolean =
        favoriteList.contains(project.id)

    /**
     * Checks if the project was made by the current user
     *
     * @param project the project to check
     * @return true if the project was made by the user, false else
     */
    private fun checkIfOwn(project: ImmutableProject): Boolean {
        return project.authorId == userId
    }


    /**
     * Fetch the user preference from Database and update.
     */
    private fun updatePreferences() {
        userDB.getPreferences(
            { pf -> pf?.let { userPref = it } },
            {}
        )
    }

}