Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/bag list #2

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<activity android:name=".itemdetail.ItemDetailActivity"/>
<activity android:name=".additem.AddItemActivity"/>
<activity android:name=".addbag.AddBagActivity"/>
<activity android:name=".baglist.BagListActivity" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
package com.fazemeright.myinventorytracker

import android.graphics.Color
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.databinding.BindingAdapter

@BindingAdapter("app:bgColor")
fun updateBackgroundColor(cardView: CardView, bagName: String) {
cardView.setBackgroundColor(
when (bagName) {
"Red" -> Color.parseColor("#C92214")
"Black AT" -> Color.parseColor("#000000")
else -> Color.parseColor("#0A3D62")
}
)
}

@BindingAdapter("app:setBagName")
fun setBagName(textView: TextView, bagName: String) {
textView.text = bagName
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package com.fazemeright.myinventorytracker.baglist

import android.content.Intent
import android.os.Bundle
import android.view.Menu
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.fazemeright.myinventorytracker.R
import com.fazemeright.myinventorytracker.addbag.AddBagActivity
import com.fazemeright.myinventorytracker.database.InventoryDatabase
import com.fazemeright.myinventorytracker.databinding.ActivityBagListBinding

class BagListActivity : AppCompatActivity() {

private lateinit var searchView: SearchView
private lateinit var viewModel: BagListViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val binding: ActivityBagListBinding =
DataBindingUtil.setContentView(this, R.layout.activity_bag_list)

val application = requireNotNull(this).application

val dataSource = InventoryDatabase.getInstance(application)

val viewModelFactory = BagListViewModelFactory(dataSource, application)

viewModel = ViewModelProviders.of(this, viewModelFactory).get(BagListViewModel::class.java)

binding.viewModel = viewModel

binding.lifecycleOwner = this

supportActionBar?.apply {
setHomeButtonEnabled(true)
setDisplayHomeAsUpEnabled(true)
title = getString(R.string.bag_list)
}

val manager = LinearLayoutManager(this)

binding.bagList.layoutManager = manager

val adapter = BagListAdapter(
BagListAdapter.BagListener(
clickListener = { item ->
viewModel.onBagClicked(item)
},
deleteClickListener = { itemId ->
showConfirmationDialog(itemId)
// TODO("Implement AlertDialog for confirmation before delete")
// viewModel.onDeleteItemClicked(itemId)
}
))

binding.bagList.adapter = adapter

viewModel.bags.observe(this, Observer {
it?.let {
adapter.updateList(it)
}
})

viewModel.searchItems.observe(this, Observer {
it?.let {
adapter.updateList(it)
}
})

/*viewModel.deletedItem.observe(this, Observer { deletedItem ->
// Show a snack bar for undo option
Snackbar.make(
binding.root, // Parent view
"Item deleted from database.", // Message to show
Snackbar.LENGTH_LONG //
).setAction( // Set an action for snack bar
"Undo" // Action button text
) {
// Action button click listener
// Do something when undo action button clicked
viewModel.undoDeleteItem(deletedItem)
}.show()
})*/

viewModel.navigateToAddBagActivity.observe(this, Observer { navigate ->
if (navigate) {
startActivity(Intent(this, AddBagActivity::class.java))
viewModel.onNavigationToAddBagFinished()
}
})

viewModel.navigateToBagDetailActivity.observe(this, Observer { itemInBag ->
itemInBag?.let {
/*val intent = Intent(this, ItemDetailActivity::class.java)
.apply { putExtra("itemInBag", it) }
startActivity(intent) TODO: Navigate to BagDetailActivity*/
viewModel.onNavigationToItemDetailFinished()
}
})

viewModel.bags.observe(this, Observer { bagList ->
bagList?.let {
adapter.updateBagList(bagList)
}
})
}

private fun showConfirmationDialog(itemId: Long) {
// build alert dialog
val dialogBuilder = AlertDialog.Builder(this)

// set message of alert dialog
dialogBuilder
// set title for alert dialog box
.setTitle("Are you sure")
.setMessage("Do you want to delete this entry?")
// if the dialog is cancelable
.setCancelable(false)
// positive button text and action
.setPositiveButton("Yes") { _, _ ->
viewModel.onDeleteItemClicked(itemId)
}
// negative button text and action
.setNegativeButton("No") { dialog, _ ->
dialog.cancel()
}

// create dialog box
val alert = dialogBuilder.create()
// show alert dialog
alert.show()
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.menu_bag_list, menu)
val searchItem = menu!!.findItem(R.id.action_search)
searchView = searchItem.actionView as SearchView
searchView.isSubmitButtonEnabled = true
searchView.queryHint = "Search Inventory for Items"
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextChange(newText: String): Boolean {
viewModel.onSearchClicked(newText)
return true
}

override fun onQueryTextSubmit(query: String): Boolean {
viewModel.onSearchClicked(query)
return true
}
})
return super.onCreateOptionsMenu(menu)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.fazemeright.myinventorytracker.baglist

import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.fazemeright.myinventorytracker.database.BagItem
import com.fazemeright.myinventorytracker.databinding.ListBagItemBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

class BagListAdapter(private val clickListener: BagListener) :
ListAdapter<BagItem,
BagListAdapter.ViewHolder>(ItemListDiffCallback()) {

private lateinit var bagsList: List<BagItem>

private val adapterScope = CoroutineScope(Dispatchers.Default)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}

fun updateList(list: List<BagItem>?) {
Log.d("##DebugData", list.toString())
submitList(list)
}

fun updateBagList(updatedList: List<BagItem>) {
this.bagsList = updatedList
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
holder.bind(item, clickListener)
}

class ViewHolder private constructor(val binding: ListBagItemBinding) :
RecyclerView.ViewHolder(binding.root) {


fun bind(
item: BagItem,
clickListener: BagListener
) {
binding.item = item
binding.clickListener = clickListener
binding.executePendingBindings()
}

companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ListBagItemBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}

class ItemListDiffCallback :
DiffUtil.ItemCallback<BagItem>() {
override fun areItemsTheSame(
oldBag: BagItem,
newBag: BagItem
): Boolean {
return oldBag.bagId == newBag.bagId
}

override fun areContentsTheSame(
oldBag: BagItem,
newBag: BagItem
): Boolean {
return oldBag == newBag
}

}

class BagListener(
val clickListener: (bag: BagItem) -> Unit,
val deleteClickListener: (bagId: Long) -> Unit
) {
fun onClick(bag: BagItem) = clickListener(bag)
fun onDeleteClick(bag: BagItem) = deleteClickListener(bag.bagId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.fazemeright.myinventorytracker.baglist

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.fazemeright.myinventorytracker.database.BagItem
import com.fazemeright.myinventorytracker.database.InventoryDatabase
import com.fazemeright.myinventorytracker.database.InventoryItem
import kotlinx.coroutines.*

/**
* ViewModel for BagListActivity.
*/
class BagListViewModel(
val database: InventoryDatabase,
application: Application
) : AndroidViewModel(application) {

/**
* viewModelJob allows us to cancel all coroutines started by this ViewModel.
*/
private var viewModelJob = Job()
/**
* A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
*
* Because we pass it [viewModelJob], any coroutine started in this uiScope can be cancelled
* by calling `viewModelJob.cancel()`
*
* By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
* the main thread on Android. This is a sensible default because most coroutines started by
* a [BagListViewModel] update the UI after performing some processing.
*/
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)

val searchItems: LiveData<List<BagItem>>
get() = _searchItems

private val _searchItems = MutableLiveData<List<BagItem>>()

val bags = database.bagItemDao.getAllBags()

val navigateToBagDetailActivity = MutableLiveData<BagItem>()

val navigateToAddBagActivity = MutableLiveData<Boolean>()

// val deletedItem = MutableLiveData<InventoryItem>()

init {
onSearchClicked("")
}

fun onSearchClicked(searchText: String) {
uiScope.launch {
fetchSearchResults(searchText)
}
}

private suspend fun fetchSearchResults(searchText: String) {
withContext(Dispatchers.IO) {
updateItems(database.inventoryItemDao.getSearchBags("%$searchText%"))
}
}

private suspend fun updateItems(newItems: List<BagItem>) {
withContext(Dispatchers.Main) {
_searchItems.value = newItems
}
}

fun addBagClicked() {
navigateToAddBagActivity.value = true
}

fun onNavigationToAddBagFinished() {
navigateToAddBagActivity.value = false
}

fun onBagClicked(item: BagItem) {
navigateToBagDetailActivity.value = item
}

fun onNavigationToItemDetailFinished() {
navigateToBagDetailActivity.value = null
}

// TODO: Update all delete, undo and insert functions with BagItem
fun onDeleteItemClicked(itemId: Long) {
uiScope.launch {
// deletedItem.value = deleteItem(itemId)
}
}

/*private suspend fun deleteItem(itemId: Long): InventoryItem? {
return withContext(Dispatchers.IO) {
val deleteItem = database.inventoryItemDao.get(itemId)
deleteItem?.let {
database.inventoryItemDao.deleteItem(it)
}
deleteItem
}
}*/

fun undoDeleteItem(deletedItem: InventoryItem?) {
uiScope.launch {
insertItemBack(deletedItem)
}
}

private suspend fun insertItemBack(deletedItem: InventoryItem?) {
withContext(Dispatchers.IO) {
deletedItem?.let { database.inventoryItemDao.insert(it) }
}
}
}
Loading