taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

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 }