Skip to content

Commit 8da2c72

Browse files
committed
Merge branch 'improve-payment-selection'
2 parents 02b13a5 + 071c3d7 commit 8da2c72

18 files changed

+310
-440
lines changed

core/src/main/java/io/snabble/sdk/googlepay/GooglePayHelper.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.google.android.gms.common.api.ApiException
1010
import com.google.android.gms.wallet.*
1111
import com.google.gson.JsonArray
1212
import com.google.gson.JsonObject
13+
import io.snabble.sdk.PaymentMethod
1314
import io.snabble.sdk.Project
1415
import io.snabble.sdk.Snabble
1516
import io.snabble.sdk.utils.GsonHolder
@@ -155,6 +156,11 @@ class GooglePayHelper(
155156
}
156157
}
157158

159+
fun isGooglePayAvailable(): Boolean {
160+
// TODO package manager check for google pay!
161+
return project.availablePaymentMethods.contains(PaymentMethod.GOOGLE_PAY)
162+
}
163+
158164
fun requestPayment(priceToPay: Int): Boolean {
159165
val priceToPayDecimal = priceToPay.toBigDecimal().divide(100.toBigDecimal())
160166
val intent = Intent(context, GooglePayHelperActivity::class.java)

core/src/main/java/io/snabble/sdk/payment/PaymentCredentialsStore.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010

1111
import java.util.ArrayList;
1212
import java.util.Collections;
13+
import java.util.HashSet;
1314
import java.util.List;
1415
import java.util.concurrent.CopyOnWriteArrayList;
1516

1617
import io.snabble.sdk.Environment;
18+
import io.snabble.sdk.PaymentMethod;
19+
import io.snabble.sdk.Project;
1720
import io.snabble.sdk.Snabble;
1821
import io.snabble.sdk.UserPreferences;
1922
import io.snabble.sdk.utils.Dispatch;
@@ -261,6 +264,35 @@ private void validate() {
261264
}
262265
}
263266

267+
public int getCountForProject(Project project) {
268+
int count = 0;
269+
270+
HashSet<PaymentMethod> onlineMethods = new HashSet<>();
271+
for (PaymentMethod paymentMethod : project.getAvailablePaymentMethods()) {
272+
if (paymentMethod.isRequiringCredentials()) {
273+
onlineMethods.add(paymentMethod);
274+
}
275+
}
276+
277+
for (PaymentCredentials pc : data.credentialsList) {
278+
if (pc.getAppId().equals(Snabble.getInstance().getConfig().appId)) {
279+
String projectId = pc.getProjectId();
280+
if (projectId != null) {
281+
if (pc.getProjectId().equals(project.getId()) && onlineMethods.contains(pc.getPaymentMethod())) {
282+
count++;
283+
}
284+
}
285+
}
286+
}
287+
288+
if (project.getGooglePayHelper() != null
289+
&& project.getGooglePayHelper().isGooglePayAvailable()) {
290+
count++;
291+
}
292+
293+
return count;
294+
}
295+
264296
public int getUsablePaymentCredentialsCount() {
265297
int i = 0;
266298
for (PaymentCredentials pc : data.credentialsList) {

ui/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ android {
2525
}
2626
}
2727
}
28+
29+
buildFeatures {
30+
viewBinding = true
31+
}
2832

2933
lintOptions {
3034
disable 'LabelFor', 'ContentDescription'

ui/src/main/java/io/snabble/sdk/ui/cart/CheckoutBar.kt

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import android.os.Bundle
1111
import android.util.AttributeSet
1212
import android.view.KeyEvent
1313
import android.view.LayoutInflater
14-
import android.view.View
1514
import android.widget.*
1615
import androidx.appcompat.app.AlertDialog
1716
import androidx.core.view.isVisible
@@ -25,6 +24,7 @@ import io.snabble.sdk.ui.Keyguard
2524
import io.snabble.sdk.ui.R
2625
import io.snabble.sdk.ui.SnabbleUI
2726
import io.snabble.sdk.ui.checkout.CheckoutHelper
27+
import io.snabble.sdk.ui.databinding.SnabbleViewCheckoutBarBinding
2828
import io.snabble.sdk.ui.payment.PaymentInputViewHelper
2929
import io.snabble.sdk.ui.payment.SEPALegalInfoHelper
3030
import io.snabble.sdk.ui.payment.SelectPaymentMethodFragment
@@ -36,14 +36,9 @@ class CheckoutBar @JvmOverloads constructor(
3636
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
3737
) : LinearLayout(context, attrs, defStyleAttr), Checkout.OnCheckoutStateChangedListener {
3838
private lateinit var progressDialog: DelayedProgressDialog
39-
private val paySelector: View
40-
private val paySelectorButton: View
41-
private val payIcon: ImageView
42-
private val payButton: Button
43-
private val googlePayButton: View
44-
private val articleCount: TextView
45-
private val priceSum: TextView
46-
private val priceContainer: FrameLayout
39+
40+
private val binding: SnabbleViewCheckoutBarBinding
41+
4742
private val paymentSelectionHelper by lazy { PaymentSelectionHelper.getInstance() }
4843
private val project by lazy { SnabbleUI.getProject() }
4944
private val cart: ShoppingCart by lazy { project.shoppingCart }
@@ -52,19 +47,13 @@ class CheckoutBar @JvmOverloads constructor(
5247
}
5348

5449
val priceHeight: Int
55-
get() = priceSum.height + priceContainer.marginTop * 2
50+
get() = binding.priceSum.height + binding.sumContainer.marginTop * 2
5651

5752
init {
5853
LayoutInflater.from(context).inflate(R.layout.snabble_view_checkout_bar, this, true)
54+
binding = SnabbleViewCheckoutBarBinding.bind(this)
55+
5956
orientation = VERTICAL
60-
paySelector = findViewById(R.id.payment_selector)
61-
paySelectorButton = findViewById(R.id.payment_selector_button)
62-
payIcon = findViewById(R.id.payment_icon)
63-
payButton = findViewById(R.id.pay)
64-
googlePayButton = findViewById(R.id.google_pay_button)
65-
articleCount = findViewById(R.id.article_count)
66-
priceSum = findViewById(R.id.price_sum)
67-
priceContainer = findViewById(R.id.sum_container)
6857

6958
if (!isInEditMode) {
7059
initBusinessLogic()
@@ -76,21 +65,25 @@ class CheckoutBar @JvmOverloads constructor(
7665
update()
7766
})
7867

79-
paySelectorButton.setOnClickListener {
68+
binding.paymentSelectorButton.setOnClickListener {
8069
paymentSelectionHelper.showDialog(UIUtils.getHostFragmentActivity(context))
8170
}
8271

83-
payButton.setOneShotClickListener {
72+
binding.paymentSelectorButtonBig.setOnClickListener {
73+
paymentSelectionHelper.showDialog(UIUtils.getHostFragmentActivity(context))
74+
}
75+
76+
binding.pay.setOneShotClickListener {
8477
cart.taxation = ShoppingCart.Taxation.UNDECIDED
85-
payButtonClick()
78+
payClick()
8679
}
8780

88-
googlePayButton.setOneShotClickListener {
81+
binding.googlePayButtonLayout.googlePayButton.setOneShotClickListener {
8982
val packageName = "com.google.android.apps.walletnfcrel"
9083
val pm = context.packageManager
9184
try {
9285
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES)
93-
payButtonClick()
86+
payClick()
9487
} catch (e: PackageManager.NameNotFoundException) {
9588
try {
9689
context.startActivity(Intent(Intent.ACTION_VIEW,
@@ -107,7 +100,7 @@ class CheckoutBar @JvmOverloads constructor(
107100

108101
progressDialog = DelayedProgressDialog(context)
109102
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER)
110-
progressDialog.setMessage(getContext().getString(R.string.Snabble_pleaseWait))
103+
progressDialog.setMessage(context.getString(R.string.Snabble_pleaseWait))
111104
progressDialog.setCanceledOnTouchOutside(false)
112105
progressDialog.setCancelable(true)
113106
progressDialog.setOnKeyListener(DialogInterface.OnKeyListener { dialogInterface: DialogInterface, _, keyEvent: KeyEvent ->
@@ -134,7 +127,7 @@ class CheckoutBar @JvmOverloads constructor(
134127
})
135128
}
136129

137-
private fun payButtonClick() {
130+
private fun payClick() {
138131
if (cart.isRestorable) {
139132
cart.restore()
140133
update()
@@ -144,50 +137,62 @@ class CheckoutBar @JvmOverloads constructor(
144137
}
145138

146139
private fun update() {
147-
updatePaySelector()
148-
updatePayButtonAndText()
140+
updatePaymentSelector()
141+
updatePayAndText()
149142
}
150143

151-
private fun updatePaySelector() {
144+
private fun updatePaymentSelector() {
152145
val entry = paymentSelectionHelper.selectedEntry.value
153146
if (entry == null) {
154-
paySelector.visibility = GONE
147+
binding.paymentSelector.visibility = GONE
155148
} else {
156149
val pcs = Snabble.getInstance().paymentCredentialsStore
157150
val hasNoPaymentMethods = pcs.usablePaymentCredentialsCount == 0
158151
val isHidden = project.paymentMethodDescriptors.size == 1 && hasNoPaymentMethods
159-
paySelector.visibility = if (isHidden) GONE else VISIBLE
160-
payIcon.setImageResource(entry.iconResId)
152+
binding.paymentSelector.visibility = if (isHidden) GONE else VISIBLE
153+
binding.paymentIcon.setImageResource(entry.iconResId)
161154
}
162155
}
163156

164-
private fun updatePayButtonAndText() {
157+
private fun updatePayAndText() {
165158
cart.let { cart ->
166159
val quantity = cart.totalQuantity
167160
val price = cart.totalPrice
168161
val articlesText = resources.getQuantityText(R.plurals.Snabble_Shoppingcart_numberOfItems, quantity)
169-
articleCount.text = String.format(articlesText.toString(), quantity)
170-
priceSum.text = project.priceFormatter.format(price)
171-
172-
162+
binding.articleCount.text = String.format(articlesText.toString(), quantity)
163+
binding.priceSum.text = project.priceFormatter.format(price)
173164

174165
val onlinePaymentAvailable = cart.availablePaymentMethods != null && cart.availablePaymentMethods.isNotEmpty()
175-
payButton.isEnabled = price > 0 && (onlinePaymentAvailable || paymentSelectionHelper.selectedEntry.value != null)
166+
binding.pay.isEnabled = price > 0 && (onlinePaymentAvailable || paymentSelectionHelper.selectedEntry.value != null)
176167

177-
val entry = paymentSelectionHelper.selectedEntry.value
178-
if (entry?.paymentMethod == PaymentMethod.GOOGLE_PAY && price > 0) {
179-
payButton.isVisible = false
180-
googlePayButton.isVisible = true
168+
var showBigSelector = paymentSelectionHelper.shouldShowBigSelector()
169+
val showSmallSelector = paymentSelectionHelper.shouldShowSmallSelector()
170+
171+
if (paymentSelectionHelper.shouldShowPayButton()) {
172+
binding.pay.isEnabled = true
173+
if (paymentSelectionHelper.shouldShowGooglePayButton()) {
174+
showBigSelector = false
175+
binding.pay.isVisible = false
176+
binding.googlePayButtonLayout.root.isVisible = !showBigSelector
177+
} else {
178+
binding.pay.isVisible = !showBigSelector
179+
binding.googlePayButtonLayout.root.isVisible = false
180+
}
181181
} else {
182-
payButton.isVisible = true
183-
googlePayButton.isVisible = false
182+
binding.pay.isVisible = true
183+
binding.pay.isEnabled = false
184+
binding.googlePayButtonLayout.root.isVisible = false
184185
}
185186

187+
binding.paymentSelectorButtonBig.isVisible = showBigSelector
188+
binding.paymentSelector.isVisible = showSmallSelector
189+
binding.paymentActive.isVisible = !showBigSelector
190+
186191
if (cart.isRestorable) {
187-
payButton.isEnabled = true
188-
payButton.setText(R.string.Snabble_Shoppingcart_emptyState_restoreButtonTitle)
192+
binding.pay.isEnabled = true
193+
binding.pay.setText(R.string.Snabble_Shoppingcart_emptyState_restoreButtonTitle)
189194
} else {
190-
payButton.setText(I18nUtils.getIdentifierForProject(resources, project, R.string.Snabble_Shoppingcart_buyProducts_now))
195+
binding.pay.setText(I18nUtils.getIdentifierForProject(resources, project, R.string.Snabble_Shoppingcart_buyProducts_now))
191196
}
192197
}
193198
}

ui/src/main/java/io/snabble/sdk/ui/cart/PaymentSelectionDialogFragment.java

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
import java.util.ArrayList;
1919

2020
import io.snabble.sdk.PaymentMethodDescriptor;
21+
import io.snabble.sdk.Snabble;
2122
import io.snabble.sdk.ui.R;
2223
import io.snabble.sdk.ui.SnabbleUI;
24+
import io.snabble.sdk.ui.payment.PaymentInputViewHelper;
2325
import io.snabble.sdk.ui.payment.SelectPaymentMethodFragment;
2426
import io.snabble.sdk.ui.utils.UIUtils;
2527

@@ -47,6 +49,14 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
4749
PaymentSelectionHelper.Entry selectedEntry = (PaymentSelectionHelper.Entry) args.getSerializable(ARG_SELECTED_ENTRY);
4850
ArrayList<PaymentSelectionHelper.Entry> entries = (ArrayList<PaymentSelectionHelper.Entry>) args.getSerializable(ARG_ENTRIES);
4951
if (entries != null) {
52+
boolean hasAnyAddedMethods = false;
53+
for (final PaymentSelectionHelper.Entry entry : entries) {
54+
if (entry.isAdded) {
55+
hasAnyAddedMethods = true;
56+
break;
57+
}
58+
}
59+
5060
for (final PaymentSelectionHelper.Entry entry : entries) {
5161
View v = View.inflate(requireContext(), R.layout.snabble_item_payment_select, null);
5262

@@ -62,7 +72,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
6272
imageView.setVisibility(View.INVISIBLE);
6373
}
6474

65-
if (entry.isAdded) {
75+
if (entry.isAdded || !hasAnyAddedMethods) {
6676
imageView.setColorFilter(null);
6777
} else {
6878
ColorMatrix matrix = new ColorMatrix();
@@ -85,8 +95,13 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
8595

8696
if (entry.isAvailable) {
8797
v.setOnClickListener(v1 -> {
88-
PaymentSelectionHelper.getInstance().select(entry);
89-
dismissAllowingStateLoss();
98+
if (entry.isAdded) {
99+
PaymentSelectionHelper.getInstance().select(entry);
100+
dismissAllowingStateLoss();
101+
} else {
102+
PaymentInputViewHelper.openPaymentInputView(getContext(), entry.paymentMethod, SnabbleUI.getProject().getId());
103+
dismissAllowingStateLoss();
104+
}
90105
});
91106
name.setEnabled(true);
92107
} else {
@@ -105,32 +120,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
105120
}
106121
}
107122

108-
boolean canAdd = false;
109-
for (PaymentMethodDescriptor descriptor : SnabbleUI.getProject().getPaymentMethodDescriptors()) {
110-
if (!descriptor.getPaymentMethod().isOfflineMethod()) {
111-
canAdd = true;
112-
break;
113-
}
114-
}
115-
116-
if (canAdd) {
117-
View v = View.inflate(requireContext(), R.layout.snabble_item_payment_select_add, null);
118-
v.setOnClickListener(v12 -> {
119-
Activity activity = UIUtils.getHostActivity(getContext());
120-
if (activity instanceof FragmentActivity) {
121-
SelectPaymentMethodFragment dialogFragment = new SelectPaymentMethodFragment();
122-
Bundle bundle = new Bundle();
123-
bundle.putString(SelectPaymentMethodFragment.ARG_PROJECT_ID, SnabbleUI.getProject().getId());
124-
dialogFragment.setArguments(bundle);
125-
dialogFragment.show(((FragmentActivity) activity).getSupportFragmentManager(), null);
126-
} else {
127-
throw new RuntimeException("Host activity must be a Fragment Activity");
128-
}
129-
dismissAllowingStateLoss();
130-
});
131-
options.addView(v, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
132-
}
133-
134123
return view;
135124
}
136125

0 commit comments

Comments
 (0)