ProductsFragment.kt (6645B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2020 Taler Systems S.A. 4 * 5 * GNU Taler is free software; you can redistribute it and/or modify it under the 6 * terms of the GNU General Public License as published by the Free Software 7 * Foundation; either version 3, or (at your option) any later version. 8 * 9 * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 package net.taler.merchantpos.order 18 19 import android.os.Bundle 20 import android.view.LayoutInflater 21 import android.view.View 22 import android.view.View.GONE 23 import android.view.View.INVISIBLE 24 import android.view.View.VISIBLE 25 import android.view.ViewGroup 26 import android.widget.ImageView 27 import android.widget.TextView 28 import com.google.android.material.card.MaterialCardView 29 import androidx.fragment.app.Fragment 30 import androidx.fragment.app.activityViewModels 31 import androidx.recyclerview.widget.AsyncListDiffer 32 import androidx.recyclerview.widget.DiffUtil.ItemCallback 33 import androidx.recyclerview.widget.GridLayoutManager 34 import androidx.recyclerview.widget.RecyclerView.Adapter 35 import androidx.recyclerview.widget.RecyclerView.ViewHolder 36 import net.taler.lib.android.base64Bitmap 37 import net.taler.merchantpos.MainViewModel 38 import net.taler.merchantpos.R 39 import net.taler.merchantpos.config.ConfigProduct 40 import net.taler.merchantpos.databinding.FragmentProductsBinding 41 import net.taler.merchantpos.order.ProductAdapter.ProductViewHolder 42 43 interface ProductSelectionListener { 44 fun onProductSelected(product: ConfigProduct) 45 } 46 47 class ProductsFragment : Fragment(), ProductSelectionListener { 48 49 private val viewModel: MainViewModel by activityViewModels() 50 private val orderManager by lazy { viewModel.orderManager } 51 private val adapter = ProductAdapter(this) 52 53 private lateinit var ui: FragmentProductsBinding 54 55 override fun onCreateView( 56 inflater: LayoutInflater, 57 container: ViewGroup?, 58 savedInstanceState: Bundle? 59 ): View? { 60 ui = FragmentProductsBinding.inflate(inflater, container, false) 61 return ui.root 62 } 63 64 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 65 ui.productsList.apply { 66 adapter = this@ProductsFragment.adapter 67 layoutManager = GridLayoutManager(requireContext(), 3) 68 } 69 70 orderManager.products.observe(viewLifecycleOwner, { products -> 71 if (products == null) { 72 adapter.setItems(emptyList()) 73 } else { 74 adapter.setItems(products) 75 } 76 ui.progressBar.visibility = INVISIBLE 77 }) 78 } 79 80 override fun onProductSelected(product: ConfigProduct) { 81 orderManager.addProduct(orderManager.currentOrderId.value!!, product) 82 viewModel.configManager.refreshInventory() 83 } 84 85 } 86 87 private class ProductAdapter( 88 private val listener: ProductSelectionListener 89 ) : Adapter<ProductViewHolder>() { 90 init { 91 setHasStableIds(true) 92 } 93 94 private val itemCallback = object : ItemCallback<ConfigProduct>() { 95 override fun areItemsTheSame(oldItem: ConfigProduct, newItem: ConfigProduct): Boolean { 96 return oldItem.stableKey == newItem.stableKey 97 } 98 99 override fun areContentsTheSame(oldItem: ConfigProduct, newItem: ConfigProduct): Boolean { 100 return oldItem.displayName == newItem.displayName && 101 oldItem.displayDescription == newItem.displayDescription && 102 oldItem.displayPrice == newItem.displayPrice && 103 oldItem.image == newItem.image && 104 oldItem.availableToSell == newItem.availableToSell && 105 oldItem.remainingStock == newItem.remainingStock 106 } 107 } 108 private val differ = AsyncListDiffer(this, itemCallback) 109 110 override fun getItemCount() = differ.currentList.size 111 112 override fun getItemId(position: Int): Long { 113 return differ.currentList[position].stableKey.hashCode().toLong() 114 } 115 116 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder { 117 val view = 118 LayoutInflater.from(parent.context).inflate(R.layout.list_item_product, parent, false) 119 return ProductViewHolder(view) 120 } 121 122 override fun onBindViewHolder(holder: ProductViewHolder, position: Int) { 123 holder.bind(differ.currentList[position]) 124 } 125 126 fun setItems(items: List<ConfigProduct>) { 127 differ.submitList(items.toList()) 128 } 129 130 inner class ProductViewHolder(private val v: View) : ViewHolder(v) { 131 private val name: TextView = v.findViewById(R.id.name) 132 private val description: TextView = v.findViewById(R.id.description) 133 private val price: TextView = v.findViewById(R.id.price) 134 private val image: ImageView = v.findViewById(R.id.image) 135 private val unavailable: TextView = v.findViewById(R.id.unavailableLabel) 136 private val card: MaterialCardView = v as MaterialCardView 137 138 fun bind(product: ConfigProduct) { 139 name.text = product.displayName 140 val productDescription = product.displayDescription 141 if (productDescription == null) { 142 description.visibility = GONE 143 } else { 144 description.visibility = VISIBLE 145 description.text = productDescription 146 } 147 price.text = product.displayPrice 148 149 // base64 encoded image 150 val bitmap = product.image?.base64Bitmap 151 if (bitmap == null) { 152 image.visibility = GONE 153 } else { 154 image.visibility = VISIBLE 155 image.setImageBitmap(bitmap) 156 } 157 158 unavailable.visibility = if (product.availableToSell) GONE else VISIBLE 159 unavailable.text = when { 160 product.availableToSell -> "" 161 product.remainingStock == 0 -> v.context.getString(R.string.product_out_of_stock) 162 else -> v.context.getString(R.string.product_unavailable) 163 } 164 card.isEnabled = product.availableToSell 165 v.isEnabled = product.availableToSell 166 v.alpha = if (product.availableToSell) 1f else 0.5f 167 v.setOnClickListener { 168 if (product.availableToSell) listener.onProductSelected(product) 169 } 170 } 171 } 172 173 }