<file_sep>package edu.rosehulman.moviequotes

import android.content.Context
import android.content.DialogInterface
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.dialog_add.view.*

class MovieQuoteAdapter(private val context: Context) :
    RecyclerView.Adapter<MovieQuoteViewHolder>(),
    FirestoreQuotesManager.Observer
{
    init {
        FirestoreQuotesManager.setObserver(this)
        FirestoreQuotesManager.beginListening()
    }

    fun stopListening() {
        FirestoreQuotesManager.stopListening()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieQuoteViewHolder {
        val view = LayoutInflater.from(context).inflate(R.layout.row_view, null, false)
        return MovieQuoteViewHolder(view, this, context)
    }

    override fun getItemCount() = FirestoreQuotesManager.getNumQuotes()

    override fun onBindViewHolder(holder: MovieQuoteViewHolder, position: Int) {
        val movieQuote = FirestoreQuotesManager.getQuoteAt(position)
        holder.bind(movieQuote)
    }

    fun showAddEditDialog(position: Int) {
        // pos of -1 means add
        val builder = AlertDialog.Builder(context)
        builder.setTitle(if (position < 0) R.string.add_dialog_title else R.string.edit_dialog_title)
        val view = LayoutInflater.from(context).inflate(R.layout.dialog_add, null, false)
        builder.setView(view)
        if (position >= 0) {
            val mq = FirestoreQuotesManager.getQuoteAt(position)
            view.quote_edit_text.setText(mq.quote)
            view.movie_edit_text.setText(mq.movie)
        }
        builder.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
            val quote = view.quote_edit_text.text.toString()
            val movie = view.movie_edit_text.text.toString()
            val movieQuote = MovieQuote(quote, movie)
            if (position < 0) {
                add(movieQuote)
            } else {
                edit(quote, movie, position)
            }
        }
        if (position >= 0) {
            builder.setNeutralButton("Delete") { _, _ ->
                delete(position)

            }
        }
        builder.setNegativeButton(android.R.string.cancel, null)
        builder.create().show()
    }

    fun add(movieQuote: MovieQuote) {
        FirestoreQuotesManager.add(movieQuote)
    }

    private fun edit(quote: String, movie: String, position: Int) {
        FirestoreQuotesManager.edit(quote, movie, position)
    }

    private fun delete(position: Int) {
        FirestoreQuotesManager.delete(position)
    }

    fun select(position: Int) {
        FirestoreQuotesManager.select(position)
    }
}<file_sep>package edu.rosehulman.moviequotes

import android.util.Log
import com.google.firebase.firestore.*

object FirestoreQuotesManager {
    private val movieQuotes = ArrayList<MovieQuote>()
    private lateinit var quotesRef: CollectionReference
    private lateinit var quotesRegistration: ListenerRegistration
    private var observer: Observer? = null

    fun setObserver(observer: Observer) {
        this.observer = observer
    }

    fun beginListening() {
        quotesRef = FirebaseFirestore
            .getInstance()
            .collection(Constants.QUOTES_COLLECTION)

        quotesRegistration = quotesRef
            .orderBy(MovieQuote.CREATED_KEY, Query.Direction.ASCENDING)
            .addSnapshotListener { snapshot: QuerySnapshot?, exception: FirebaseFirestoreException? ->
                if (exception != null) {
                    Log.e(Constants.TAG, "Error: $exception")
                    return@addSnapshotListener
                }
                for (docChange in snapshot!!.documentChanges) {
                    val mq = MovieQuote.from(docChange.document)
                    when (docChange.type) {
                        DocumentChange.Type.ADDED -> {
                            movieQuotes.add(0, mq)
                            observer?.notifyItemInserted(0)
                        }
                        DocumentChange.Type.REMOVED -> {
                            val pos = movieQuotes.indexOfFirst { it.id == mq.id }
                            movieQuotes.removeAt(pos)
                            observer?.notifyItemRemoved(pos)
                        }
                        DocumentChange.Type.MODIFIED -> {
                            val pos = movieQuotes.indexOfFirst { it.id == mq.id }
                            movieQuotes[pos] = mq
                            observer?.notifyItemChanged(pos)
                        }
                    }
                }
            }
    }

    fun stopListening() {
        Log.d(Constants.TAG, "Removing listener")
        quotesRegistration.remove()
    }


    fun getNumQuotes() = movieQuotes.size
    fun getQuoteAt(position: Int) = movieQuotes[position]

    fun add(movieQuote: MovieQuote) {
        quotesRef.add(movieQuote)
    }

    fun edit(quote: String, movie: String, position: Int) {
        movieQuotes[position].quote = quote
        movieQuotes[position].movie = movie
        quotesRef.document(movieQuotes[position].id).set(movieQuotes[position])
    }

    fun delete(position: Int) {
        quotesRef.document(movieQuotes[position].id).delete()
    }

    fun select(position: Int) {
        movieQuotes[position].isSelected = !movieQuotes[position].isSelected
        quotesRef.document(movieQuotes[position].id).set(movieQuotes[position])
    }

    interface Observer {
        // Interestingly, if we use the same names as the
        // RecyclerView.Adapter methods, then our custom MovieQuote adapter
        // already inherits them from RecyclerView.Adapter.
        fun notifyItemInserted(position: Int)
        fun notifyItemRemoved(position: Int)
        fun notifyItemChanged(position: Int)
    }
}<file_sep># MovieQuotesDAOSolution
For class
