Skip to content

Commit da81bab

Browse files
committed
Merge branch 'main' into improve_docs
# Conflicts: # core/src/main/java/io/snabble/sdk/ShoppingCart.java # core/src/main/java/io/snabble/sdk/coupons/Coupons.kt
2 parents 95778cc + 536ff43 commit da81bab

36 files changed

+1165
-132
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Changelog
22
All notable changes to this project will be documented in this file.
33

4+
## [0.65.0]
5+
6+
### Added
7+
- Added support for displaying coupons
8+
- Added CouponFragment to display a single coupon
9+
- Added CouponDetailActivity to display a single coupon in an Activity
10+
- Added CouponOverviewView to display a collection of coupons depending on the project
11+
412
## [0.64.1]
513

614
### Fixed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ allprojects {
3131
}
3232

3333
project.ext {
34-
sdkVersion='0.64.1'
34+
sdkVersion='0.65.0'
3535
versionCode=1
3636

3737
compileSdkVersion=31
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.snabble.sdk
2+
3+
import android.graphics.Color
4+
import androidx.annotation.ColorInt
5+
6+
object ColorUtils {
7+
@JvmStatic
8+
fun parseColor(color: String?, @ColorInt default: Int) =
9+
color?.let {
10+
Color.parseColor(when {
11+
"^[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?$".toRegex().matches(color) -> {
12+
// add missing prefix
13+
"#$color"
14+
}
15+
"^#?[0-9a-fA-F]{3}$".toRegex().matches(color) -> {
16+
// convert 3 digit color to 6 digits
17+
color.removePrefix("#").toCharArray()
18+
.joinToString(separator = "", prefix = "#") { "$it$it" }
19+
}
20+
else -> {
21+
color
22+
}
23+
})
24+
} ?: default
25+
}

core/src/main/java/io/snabble/sdk/Project.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ package io.snabble.sdk
33
import com.google.gson.JsonElement
44
import com.google.gson.JsonObject
55
import com.google.gson.reflect.TypeToken
6-
import io.snabble.sdk.Snabble.instance
76

87
import io.snabble.sdk.googlepay.GooglePayHelper
98
import io.snabble.sdk.encodedcodes.EncodedCodesOptions
109
import io.snabble.sdk.codes.templates.CodeTemplate
1110
import io.snabble.sdk.codes.templates.PriceOverrideTemplate
1211
import io.snabble.sdk.auth.SnabbleAuthorizationInterceptor
1312
import io.snabble.sdk.checkout.Checkout
13+
import io.snabble.sdk.coupons.Coupon
14+
import io.snabble.sdk.coupons.CouponSource
15+
import io.snabble.sdk.coupons.Coupons
1416
import io.snabble.sdk.utils.*
1517
import okhttp3.OkHttpClient
1618
import okhttp3.Request

core/src/main/java/io/snabble/sdk/ShoppingCart.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import io.snabble.sdk.checkout.Violation;
2828
import io.snabble.sdk.codes.ScannedCode;
2929
import io.snabble.sdk.codes.templates.CodeTemplate;
30+
import io.snabble.sdk.coupons.Coupon;
31+
import io.snabble.sdk.coupons.CouponType;
3032
import io.snabble.sdk.utils.Dispatch;
3133
import io.snabble.sdk.utils.GsonHolder;
3234

@@ -119,7 +121,7 @@ void initWithProject(Project project) {
119121
item.cart = this;
120122
}
121123
}
122-
124+
123125
if (uuid == null) {
124126
generateNewUUID();
125127
}
@@ -205,7 +207,6 @@ void insert(Item item, int index, boolean update) {
205207
}
206208
}
207209

208-
209210
items.add(index, item);
210211

211212
clearBackup();
@@ -479,7 +480,7 @@ public void invalidateOnlinePrices() {
479480
* multiple {@link #updatePrices(boolean)} calls together
480481
*/
481482
public void updatePrices(boolean debounce) {
482-
if(debounce) {
483+
if (debounce) {
483484
updater.dispatchUpdate();
484485
} else {
485486
updater.update(true);
@@ -774,7 +775,7 @@ private Item(ShoppingCart cart, Product product, ScannedCode scannedCode) {
774775
} else {
775776
for (Product.Code code : product.getScannableCodes()) {
776777
if (code.template != null && code.template.equals(scannedCode.getTemplateName())
777-
&& code.lookupCode != null && code.lookupCode.equals(scannedCode.getLookupCode())) {
778+
&& code.lookupCode != null && code.lookupCode.equals(scannedCode.getLookupCode())) {
778779
this.quantity = code.specifiedQuantity;
779780

780781
if (!code.isPrimary && code.specifiedQuantity > 1) {
@@ -865,7 +866,7 @@ public int getQuantity(boolean ignoreLineItem) {
865866
if (lineItem != null && !ignoreLineItem) {
866867
if (lineItem.getWeight() != null) {
867868
return lineItem.getWeight();
868-
} else if (lineItem.getUnits() != null){
869+
} else if (lineItem.getUnits() != null) {
869870
return lineItem.getUnits();
870871
} else {
871872
return lineItem.getAmount();
@@ -1434,13 +1435,13 @@ void resolveViolations(List<Violation> violations) {
14341435
Item item = items.get(i);
14351436
items.remove(item);
14361437
boolean found = false;
1437-
for(ViolationNotification notification: violationNotifications) {
1438-
if(notification.getRefersTo().equals(violation.getRefersTo())) {
1438+
for (ViolationNotification notification : violationNotifications) {
1439+
if (notification.getRefersTo().equals(violation.getRefersTo())) {
14391440
found = true;
14401441
break;
14411442
}
14421443
}
1443-
if(!found) {
1444+
if (!found) {
14441445
violationNotifications.add(new ViolationNotification(
14451446
item.coupon.getName(),
14461447
violation.getRefersTo(),
@@ -1456,6 +1457,7 @@ void resolveViolations(List<Violation> violations) {
14561457

14571458
/**
14581459
* Remove the handled ViolationNotifications.
1460+
*
14591461
* @param violations the handled ViolationNotifications.
14601462
*/
14611463
public void removeViolationNotification(List<ViolationNotification> violations) {
@@ -1560,7 +1562,7 @@ public void onCheckoutLimitReached(ShoppingCart list) {}
15601562
public void onOnlinePaymentLimitReached(ShoppingCart list) {}
15611563

15621564
@Override
1563-
public void onViolationDetected(List<ViolationNotification> violations) {}
1565+
public void onViolationDetected(@NonNull List<ViolationNotification> violations) {}
15641566
}
15651567

15661568
private void notifyItemAdded(final ShoppingCart list, final Item item) {

core/src/main/java/io/snabble/sdk/ShoppingCartUpdater.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.snabble.sdk.checkout.PaymentMethodInfo;
2424
import io.snabble.sdk.checkout.SignedCheckoutInfo;
2525
import io.snabble.sdk.codes.ScannedCode;
26+
import io.snabble.sdk.coupons.Coupon;
2627
import io.snabble.sdk.utils.Dispatch;
2728
import io.snabble.sdk.utils.GsonHolder;
2829
import io.snabble.sdk.utils.Logger;

core/src/main/java/io/snabble/sdk/Snabble.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,15 +557,15 @@ object Snabble {
557557

558558
private fun getUrl(jsonObject: JsonObject, urlName: String): String? {
559559
return try {
560-
absoluteUrl(jsonObject["links"].asJsonObject[urlName].asJsonObject["href"].asString)
560+
jsonObject["links"]?.asJsonObject?.get(urlName)?.asJsonObject?.get("href")?.asString?.let(::absoluteUrl)
561561
} catch (e: Exception) {
562562
null
563563
}
564564
}
565565

566566
private fun parseBrands(jsonObject: JsonObject) {
567567
val jsonBrands = GsonHolder.get().fromJson(jsonObject["brands"], Array<Brand>::class.java)
568-
brands = jsonBrands.map { it.id to it }.toMap()
568+
brands = jsonBrands.associateBy { it.id }
569569
}
570570

571571
private fun parseProjects(jsonObject: JsonObject) {

core/src/main/java/io/snabble/sdk/checkout/CheckoutApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.google.gson.reflect.TypeToken
77
import io.snabble.sdk.ShoppingCart.BackendCart
88
import io.snabble.sdk.payment.PaymentCredentials
99
import io.snabble.sdk.Product
10-
import io.snabble.sdk.Coupon
10+
import io.snabble.sdk.coupons.Coupon
1111
import io.snabble.sdk.FulfillmentState
1212
import io.snabble.sdk.PaymentMethod
1313
import java.lang.Exception

core/src/main/java/io/snabble/sdk/checkout/PersistentState.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.snabble.sdk.checkout
22

3-
import io.snabble.sdk.Coupon
3+
import io.snabble.sdk.coupons.Coupon
44
import io.snabble.sdk.PaymentMethod
55
import io.snabble.sdk.Product
66
import io.snabble.sdk.utils.Dispatch
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package io.snabble.sdk.coupons
2+
3+
import android.graphics.Color
4+
import android.os.Parcelable
5+
import android.util.DisplayMetrics
6+
import androidx.annotation.ColorInt
7+
import com.google.gson.annotations.SerializedName
8+
import io.snabble.sdk.ColorUtils.parseColor
9+
import io.snabble.sdk.Snabble
10+
import kotlinx.parcelize.IgnoredOnParcel
11+
import kotlinx.parcelize.Parcelize
12+
import java.time.ZonedDateTime
13+
14+
@Parcelize
15+
data class Coupon (
16+
val id: String,
17+
val name: String,
18+
val description: String?,
19+
val promotionDescription: String?,
20+
val type: CouponType,
21+
val codes: List<CouponCode>?,
22+
val code: String?,
23+
@SerializedName("validFrom")
24+
private val _validFrom: String?,
25+
@SerializedName("validUntil")
26+
private val _validUntil: String?,
27+
val image: CouponImage?,
28+
val disclaimer: String?,
29+
val colors: Map<String, String>?,
30+
) : Parcelable {
31+
val isValid: Boolean
32+
get() = when(type) {
33+
CouponType.DIGITAL -> image != null
34+
CouponType.MANUAL -> name != null
35+
CouponType.PRINTED -> true
36+
}
37+
38+
@IgnoredOnParcel
39+
val validFrom: ZonedDateTime?
40+
get() = _validFrom?.let { ZonedDateTime.parse(_validFrom)}
41+
42+
@IgnoredOnParcel
43+
val validUntil: ZonedDateTime?
44+
get() = _validUntil?.let { ZonedDateTime.parse(_validUntil)}
45+
46+
@IgnoredOnParcel
47+
val backgroundColor
48+
get() = parseColor(colors?.get("background"), Color.WHITE)
49+
50+
@IgnoredOnParcel
51+
val textColor
52+
get() = parseColor(colors?.get("foreground"), Color.BLACK)
53+
}
54+
55+
@Parcelize
56+
data class CouponCode (
57+
val code: String,
58+
val template: String,
59+
) : Parcelable
60+
61+
@Parcelize
62+
data class CouponImage (
63+
val name: String?,
64+
val formats: List<CouponImageFormats>,
65+
) : Parcelable {
66+
val bestResolutionUrl: String
67+
get() {
68+
val res = Snabble.application.resources
69+
70+
val mdpiRange = 0..DisplayMetrics.DENSITY_MEDIUM
71+
val hdpiRange = DisplayMetrics.DENSITY_MEDIUM..DisplayMetrics.DENSITY_HIGH
72+
val xhdpiRange = DisplayMetrics.DENSITY_HIGH..DisplayMetrics.DENSITY_XHIGH
73+
val xxhdpiRange = DisplayMetrics.DENSITY_XHIGH..DisplayMetrics.DENSITY_XXHIGH
74+
val xxxhdpiRange = DisplayMetrics.DENSITY_XXXHIGH..Int.MAX_VALUE
75+
76+
val preferredDpi = when (res.displayMetrics.densityDpi) {
77+
in mdpiRange -> "mdpi"
78+
in hdpiRange -> "hdpi"
79+
in xhdpiRange -> "xhdpi"
80+
in xxhdpiRange -> "xxhdpi"
81+
in xxxhdpiRange -> "xxxhdpi"
82+
else -> null
83+
}
84+
85+
val image = formats
86+
.filter { it.contentType == "image/webp" }
87+
.firstOrNull { it.size == preferredDpi }
88+
?: this.formats.last()
89+
90+
return image.url
91+
}
92+
}
93+
94+
@Parcelize
95+
data class CouponImageFormats (
96+
val contentType: String,
97+
val width: Int?,
98+
val height: Int?,
99+
val size: String,
100+
val url: String,
101+
) : Parcelable
102+
103+
enum class CouponType {
104+
@SerializedName("manual") MANUAL,
105+
@SerializedName("printed") PRINTED,
106+
@SerializedName("digital") DIGITAL,
107+
}

0 commit comments

Comments
 (0)