[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-android] branch master updated (9a1f300 -> d3cc4bb)
From: |
gnunet |
Subject: |
[taler-wallet-android] branch master updated (9a1f300 -> d3cc4bb) |
Date: |
Wed, 11 Mar 2020 13:58:28 +0100 |
This is an automated email from the git hooks/post-receive script.
torsten-grote pushed a change to branch master
in repository wallet-android.
from 9a1f300 Do not require telephony features aka run on tablet devices
new 6b7b807 Support base64 encoded images in rfc2397 data URLs for
product images
new 88decdd Show a different product layout when there's only one product
new d3cc4bb Show product images in full size if you click on them
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../java/net/taler/wallet/payment/ContractTerms.kt | 6 +-
.../net/taler/wallet/payment/PaymentManager.kt | 21 +++++-
.../net/taler/wallet/payment/ProductAdapter.kt | 35 +++++++++-
...ccessfulFragment.kt => ProductImageFragment.kt} | 31 +++++----
.../taler/wallet/payment/PromptPaymentFragment.kt | 10 ++-
.../fragment_product_image.xml} | 16 ++---
app/src/main/res/layout/list_item_product.xml | 31 +++++++--
...ry_payment.xml => list_item_product_single.xml} | 77 ++++++++++------------
8 files changed, 146 insertions(+), 81 deletions(-)
copy app/src/main/java/net/taler/wallet/payment/{PaymentSuccessfulFragment.kt
=> ProductImageFragment.kt} (63%)
copy app/src/main/res/{menu/pending_operations.xml =>
layout/fragment_product_image.xml} (69%)
copy app/src/main/res/layout/{history_payment.xml =>
list_item_product_single.xml} (59%)
diff --git a/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt
b/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt
index a9f75ed..da91dea 100644
--- a/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt
+++ b/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt
@@ -29,20 +29,22 @@ data class ContractTerms(
)
interface Product {
- val id: String
+ val id: String?
val description: String
val price: Amount
val location: String?
+ val image: String?
}
@JsonIgnoreProperties("totalPrice")
data class ContractProduct(
@JsonProperty("product_id")
- override val id: String,
+ override val id: String?,
override val description: String,
override val price: Amount,
@JsonProperty("delivery_location")
override val location: String?,
+ override val image: String?,
val quantity: Int
) : Product {
diff --git a/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt
b/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt
index a00a686..ee0edaf 100644
--- a/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt
+++ b/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt
@@ -26,6 +26,9 @@ import net.taler.wallet.Amount
import net.taler.wallet.TAG
import net.taler.wallet.backend.WalletBackendApi
import org.json.JSONObject
+import java.net.MalformedURLException
+
+val REGEX_PRODUCT_IMAGE =
Regex("^data:image/(jpeg|png);base64,([A-Za-z0-9+/=]+)$")
class PaymentManager(
private val walletBackendApi: WalletBackendApi,
@@ -61,7 +64,12 @@ class PaymentManager(
}
else -> {
val status = result.getString("status")
- mPayStatus.postValue(getPayStatusUpdate(status, result))
+ try {
+ mPayStatus.postValue(getPayStatusUpdate(status,
result))
+ } catch (e: Exception) {
+ Log.e(TAG, "Error getting PayStatusUpdate", e)
+ mPayStatus.postValue(PayStatus.Error(e.message ?:
"unknown error"))
+ }
}
}
}
@@ -80,7 +88,16 @@ class PaymentManager(
}
private fun getContractTerms(json: JSONObject): ContractTerms {
- return mapper.readValue(json.getString("contractTermsRaw"))
+ val terms: ContractTerms =
mapper.readValue(json.getString("contractTermsRaw"))
+ // validate product images
+ terms.products.forEach { product ->
+ product.image?.let { image ->
+ if (REGEX_PRODUCT_IMAGE.matchEntire(image) == null) {
+ throw MalformedURLException("Invalid image data URL for
${product.description}")
+ }
+ }
+ }
+ return terms
}
@UiThread
diff --git a/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt
b/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt
index 3a0dca6..4b1b062 100644
--- a/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt
+++ b/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt
@@ -16,25 +16,40 @@
package net.taler.wallet.payment
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory.decodeByteArray
+import android.util.Base64
import android.view.LayoutInflater
import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
import android.view.ViewGroup
+import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import net.taler.wallet.R
import net.taler.wallet.payment.ProductAdapter.ProductViewHolder
+internal interface ProductImageClickListener {
+ fun onImageClick(image: Bitmap)
+}
-internal class ProductAdapter : RecyclerView.Adapter<ProductViewHolder>() {
+internal class ProductAdapter(private val listener: ProductImageClickListener)
:
+ RecyclerView.Adapter<ProductViewHolder>() {
private val items = ArrayList<ContractProduct>()
override fun getItemCount() = items.size
+ override fun getItemViewType(position: Int): Int {
+ return if (itemCount == 1) 1 else 0
+ }
+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
ProductViewHolder {
- val view =
-
LayoutInflater.from(parent.context).inflate(R.layout.list_item_product, parent,
false)
+ val res =
+ if (viewType == 1) R.layout.list_item_product_single else
R.layout.list_item_product
+ val view = LayoutInflater.from(parent.context).inflate(res, parent,
false)
return ProductViewHolder(view)
}
@@ -50,11 +65,25 @@ internal class ProductAdapter :
RecyclerView.Adapter<ProductViewHolder>() {
internal inner class ProductViewHolder(v: View) : ViewHolder(v) {
private val quantity: TextView = v.findViewById(R.id.quantity)
+ private val image: ImageView = v.findViewById(R.id.image)
private val name: TextView = v.findViewById(R.id.name)
private val price: TextView = v.findViewById(R.id.price)
fun bind(product: ContractProduct) {
quantity.text = product.quantity.toString()
+ if (product.image == null) {
+ image.visibility = GONE
+ } else {
+ image.visibility = VISIBLE
+ // product.image was validated before, so non-null below
+ val match = REGEX_PRODUCT_IMAGE.matchEntire(product.image)!!
+ val decodedString = Base64.decode(match.groups[2]!!.value,
Base64.DEFAULT)
+ val bitmap = decodeByteArray(decodedString, 0,
decodedString.size)
+ image.setImageBitmap(bitmap)
+ if (itemCount > 1) image.setOnClickListener {
+ listener.onImageClick(bitmap)
+ }
+ }
name.text = product.description
price.text = product.totalPrice.toString()
}
diff --git
a/app/src/main/java/net/taler/wallet/payment/PaymentSuccessfulFragment.kt
b/app/src/main/java/net/taler/wallet/payment/ProductImageFragment.kt
similarity index 63%
copy from
app/src/main/java/net/taler/wallet/payment/PaymentSuccessfulFragment.kt
copy to app/src/main/java/net/taler/wallet/payment/ProductImageFragment.kt
index 2084c45..02414a6 100644
--- a/app/src/main/java/net/taler/wallet/payment/PaymentSuccessfulFragment.kt
+++ b/app/src/main/java/net/taler/wallet/payment/ProductImageFragment.kt
@@ -16,34 +16,37 @@
package net.taler.wallet.payment
+import android.graphics.Bitmap
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-import androidx.navigation.fragment.findNavController
-import kotlinx.android.synthetic.main.fragment_payment_successful.*
+import androidx.fragment.app.DialogFragment
+import kotlinx.android.synthetic.main.fragment_product_image.*
import net.taler.wallet.R
-import net.taler.wallet.fadeIn
-/**
- * Fragment that shows the success message for a payment.
- */
-class PaymentSuccessfulFragment : Fragment() {
+class ProductImageFragment private constructor() : DialogFragment() {
+
+ companion object {
+ private const val IMAGE = "image"
+
+ fun new(image: Bitmap) = ProductImageFragment().apply {
+ arguments = Bundle().apply {
+ putParcelable(IMAGE, image)
+ }
+ }
+ }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
- return inflater.inflate(R.layout.fragment_payment_successful,
container, false)
+ return inflater.inflate(R.layout.fragment_product_image, container,
false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- successImageView.fadeIn()
- successTextView.fadeIn()
- backButton.setOnClickListener {
- findNavController().navigateUp()
- }
+ val bitmap = arguments!!.getParcelable<Bitmap>(IMAGE)
+ productImageView.setImageBitmap(bitmap)
}
}
diff --git
a/app/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
b/app/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
index 2f7807a..5a53556 100644
--- a/app/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
+++ b/app/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
@@ -17,6 +17,7 @@
package net.taler.wallet.payment
import android.annotation.SuppressLint
+import android.graphics.Bitmap
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -41,11 +42,11 @@ import net.taler.wallet.fadeOut
/**
* Show a payment and ask the user to accept/decline.
*/
-class PromptPaymentFragment : Fragment() {
+class PromptPaymentFragment : Fragment(), ProductImageClickListener {
private val model: WalletViewModel by activityViewModels()
private val paymentManager by lazy { model.paymentManager }
- private val adapter = ProductAdapter()
+ private val adapter = ProductAdapter(this)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -158,4 +159,9 @@ class PromptPaymentFragment : Fragment() {
totalView.fadeIn()
}
+ override fun onImageClick(image: Bitmap) {
+ val f = ProductImageFragment.new(image)
+ f.show(parentFragmentManager, "image")
+ }
+
}
diff --git a/app/src/main/res/menu/pending_operations.xml
b/app/src/main/res/layout/fragment_product_image.xml
similarity index 69%
copy from app/src/main/res/menu/pending_operations.xml
copy to app/src/main/res/layout/fragment_product_image.xml
index 980ea66..9f65d4d 100644
--- a/app/src/main/res/menu/pending_operations.xml
+++ b/app/src/main/res/layout/fragment_product_image.xml
@@ -14,11 +14,11 @@
~ GNU Taler; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
-->
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
- <item
- android:id="@+id/retry_pending"
- android:orderInCategory="100"
- android:title="@string/menu_retry_pending_operations"
- app:showAsAction="never" />
-</menu>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/productImageView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:ignore="ContentDescription">
+
+</ImageView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_item_product.xml
b/app/src/main/res/layout/list_item_product.xml
index 606022e..fe6ba23 100644
--- a/app/src/main/res/layout/list_item_product.xml
+++ b/app/src/main/res/layout/list_item_product.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of GNU Taler
~ (C) 2020 Taler Systems S.A.
~
@@ -20,9 +19,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="8dp"
- android:paddingTop="8dp"
- android:paddingEnd="8dp">
+ android:padding="8dp">
<TextView
android:id="@+id/quantity"
@@ -30,27 +27,49 @@
android:layout_height="wrap_content"
android:gravity="end"
android:minWidth="24dp"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.0"
tools:text="31" />
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintDimensionRatio="H,4:3"
+ app:layout_constraintEnd_toStartOf="@+id/name"
+ app:layout_constraintStart_toEndOf="@+id/quantity"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintWidth_max="64dp"
+ tools:ignore="ContentDescription"
+ tools:srcCompat="@tools:sample/avatars"
+ tools:visibility="visible" />
+
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/price"
- app:layout_constraintStart_toEndOf="@+id/quantity"
+ app:layout_constraintStart_toEndOf="@+id/image"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.0"
tools:text="A product item that in some cases could have a very
long name" />
<TextView
android:id="@+id/price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.0"
tools:text="23.42" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/history_payment.xml
b/app/src/main/res/layout/list_item_product_single.xml
similarity index 59%
copy from app/src/main/res/layout/history_payment.xml
copy to app/src/main/res/layout/list_item_product_single.xml
index dd135e7..a4578b3 100644
--- a/app/src/main/res/layout/history_payment.xml
+++ b/app/src/main/res/layout/list_item_product_single.xml
@@ -19,69 +19,58 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="16dp"
- android:layout_marginBottom="8dp"
- android:background="?attr/selectableItemBackground">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="32dp"
- android:layout_height="32dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:srcCompat="@drawable/history_withdrawn"
- app:tint="?android:colorControlNormal"
- tools:ignore="ContentDescription" />
+ android:padding="8dp">
<TextView
- android:id="@+id/title"
- style="@style/HistoryTitle"
- android:layout_width="0dp"
+ android:id="@+id/quantity"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- app:layout_constraintEnd_toStartOf="@+id/amountPaidWithFees"
- app:layout_constraintStart_toEndOf="@+id/icon"
+ app:layout_constraintEnd_toStartOf="@+id/name"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintHorizontal_chainStyle="packed"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- tools:text="Lots of books with very long titles" />
+ app:layout_constraintVertical_bias="0.0"
+ tools:text="31" />
- <TextView
- android:id="@+id/summary"
- android:layout_width="wrap_content"
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
- app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/amountPaidWithFees"
- app:layout_constraintHorizontal_bias="0.0"
- app:layout_constraintStart_toEndOf="@+id/icon"
- app:layout_constraintTop_toBottomOf="@+id/title"
- app:layout_constraintVertical_bias="0.0"
- tools:text="@string/history_event_payment_sent" />
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/name"
+ tools:ignore="ContentDescription"
+ tools:srcCompat="@tools:sample/avatars"
+ tools:visibility="visible" />
<TextView
- android:id="@+id/amountPaidWithFees"
+ android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/red"
- android:textSize="16sp"
- app:layout_constraintBottom_toTopOf="@+id/time"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toTopOf="@+id/image"
app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/price"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/quantity"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
- tools:text="0.2 TESTKUDOS" />
+ app:layout_goneMarginEnd="0dp"
+ tools:text="A product item that can have a very long name that
wraps over two lines" />
<TextView
- android:id="@+id/time"
+ android:id="@+id/price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="14sp"
+ android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- tools:text="23 min ago" />
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.0"
+ tools:text="23.42" />
</androidx.constraintlayout.widget.ConstraintLayout>
--
To stop receiving notification emails like this one, please contact
address@hidden.
- [taler-wallet-android] branch master updated (9a1f300 -> d3cc4bb),
gnunet <=