ExchangeAdapter.kt (7510B)
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.wallet.exchanges 18 19 import android.view.LayoutInflater 20 import android.view.View 21 import android.view.View.GONE 22 import android.view.View.VISIBLE 23 import android.view.ViewGroup 24 import android.widget.ImageButton 25 import android.widget.TextView 26 import androidx.appcompat.widget.PopupMenu 27 import androidx.recyclerview.widget.DiffUtil 28 import androidx.recyclerview.widget.RecyclerView 29 import androidx.recyclerview.widget.RecyclerView.Adapter 30 import net.taler.wallet.R 31 import net.taler.wallet.balances.ScopeInfo 32 import net.taler.wallet.exchanges.ExchangeAdapter.ExchangeItemViewHolder 33 34 interface ExchangeClickListener { 35 fun onExchangeSelected(item: ExchangeItem) 36 fun onManualWithdraw(item: ExchangeItem) 37 fun onPeerReceive(item: ExchangeItem) 38 fun onExchangeReload(item: ExchangeItem) 39 fun onExchangeDelete(item: ExchangeItem) 40 fun onExchangeTosAccept(item: ExchangeItem) 41 fun onExchangeTosForget(item: ExchangeItem) 42 fun onExchangeTosView(item: ExchangeItem) 43 fun onExchangeGlobalCurrencyAdd(item: ExchangeItem) 44 fun onExchangeGlobalCurrencyDelete(item: ExchangeItem) 45 } 46 47 internal class ExchangeAdapter( 48 private val selectOnly: Boolean, 49 private val listener: ExchangeClickListener, 50 private val devMode: Boolean, 51 ) : Adapter<ExchangeItemViewHolder>() { 52 53 private var items = emptyList<ExchangeItem>() 54 55 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExchangeItemViewHolder { 56 val view = LayoutInflater.from(parent.context) 57 .inflate(R.layout.list_item_exchange, parent, false) 58 return ExchangeItemViewHolder(view) 59 } 60 61 override fun getItemCount() = items.size 62 63 override fun onBindViewHolder(holder: ExchangeItemViewHolder, position: Int) { 64 holder.bind(items[position]) 65 } 66 67 fun update(newItems: List<ExchangeItem>) { 68 val oldItems = this.items 69 70 val diffCallback = ExchangeDiffCallback(oldItems, newItems) 71 val diffResult = DiffUtil.calculateDiff(diffCallback) 72 diffResult.dispatchUpdatesTo(this) 73 74 items = newItems 75 } 76 77 internal inner class ExchangeItemViewHolder(v: View) : RecyclerView.ViewHolder(v) { 78 private val context = v.context 79 private val urlView: TextView = v.findViewById(R.id.urlView) 80 private val currencyView: TextView = v.findViewById(R.id.currencyView) 81 private val overflowIcon: ImageButton = v.findViewById(R.id.overflowIcon) 82 83 fun bind(item: ExchangeItem) { 84 urlView.text = item.name 85 // If currency is null, it's because we have no data from the exchange... 86 currencyView.text = if (item.currency == null) { 87 context.getString(R.string.exchange_not_contacted) 88 } else { 89 context.getString(R.string.exchange_list_currency, item.currency) 90 } 91 if (selectOnly) { 92 itemView.setOnClickListener { listener.onExchangeSelected(item) } 93 overflowIcon.visibility = GONE 94 } else { 95 itemView.setOnClickListener(null) 96 itemView.isClickable = false 97 // ...thus, we should prevent the user from interacting with it. 98 overflowIcon.visibility = if (item.currency != null) VISIBLE else GONE 99 } 100 overflowIcon.setOnClickListener { openMenu(overflowIcon, item) } 101 } 102 103 private fun openMenu(anchor: View, item: ExchangeItem) = PopupMenu(context, anchor).apply { 104 inflate(R.menu.exchange) 105 if (item.tosStatus.isAccepted()) { 106 menu.findItem(R.id.action_view_tos).isVisible = true 107 menu.findItem(R.id.action_accept_tos).isVisible = false 108 menu.findItem(R.id.action_forget_tos).isVisible = devMode 109 } else { 110 menu.findItem(R.id.action_view_tos).isVisible = false 111 menu.findItem(R.id.action_accept_tos).isVisible = true 112 menu.findItem(R.id.action_forget_tos).isVisible = false 113 } 114 115 if (item.scopeInfo is ScopeInfo.Exchange) { 116 menu.findItem(R.id.action_global_add).isVisible = devMode 117 menu.findItem(R.id.action_global_delete).isVisible = false 118 } else if (item.scopeInfo is ScopeInfo.Global) { 119 menu.findItem(R.id.action_global_add).isVisible = false 120 menu.findItem(R.id.action_global_delete).isVisible = devMode 121 } 122 123 setOnMenuItemClickListener { menuItem -> 124 when (menuItem.itemId) { 125 R.id.action_manual_withdrawal -> { 126 listener.onManualWithdraw(item) 127 true 128 } 129 R.id.action_receive_peer -> { 130 listener.onPeerReceive(item) 131 true 132 } 133 R.id.action_reload -> { 134 listener.onExchangeReload(item) 135 true 136 } 137 R.id.action_view_tos -> { 138 listener.onExchangeTosView(item) 139 true 140 } 141 R.id.action_accept_tos -> { 142 listener.onExchangeTosAccept(item) 143 true 144 } 145 R.id.action_forget_tos -> { 146 listener.onExchangeTosForget(item) 147 true 148 } 149 R.id.action_global_add -> { 150 listener.onExchangeGlobalCurrencyAdd(item) 151 true 152 } 153 R.id.action_global_delete -> { 154 listener.onExchangeGlobalCurrencyDelete(item) 155 true 156 } 157 R.id.action_delete -> { 158 listener.onExchangeDelete(item) 159 true 160 } 161 else -> false 162 } 163 } 164 show() 165 } 166 } 167 } 168 169 internal class ExchangeDiffCallback( 170 private val oldList: List<ExchangeItem>, 171 private val newList: List<ExchangeItem>, 172 ): DiffUtil.Callback() { 173 override fun getOldListSize() = oldList.size 174 175 override fun getNewListSize() = newList.size 176 177 override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 178 val old = oldList[oldItemPosition] 179 val new = newList[newItemPosition] 180 181 return old.exchangeBaseUrl == new.exchangeBaseUrl 182 } 183 184 override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 185 val old = oldList[oldItemPosition] 186 val new = newList[newItemPosition] 187 188 return old == new 189 } 190 }