Skip to content

Commit 742791f

Browse files
committed
stackable scan messages
1 parent 22d19d3 commit 742791f

File tree

13 files changed

+227
-53
lines changed

13 files changed

+227
-53
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.38.0]
5+
6+
### Added
7+
- Added stackable scan messages with a new Style
8+
9+
### Deprecated
10+
- Deprecated UIUtils.showTopDownInfoBox
11+
412
## [0.37.12]
513

614
### Added

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.37.12'
34+
sdkVersion='0.38.0'
3535
versionCode=1
3636

3737
compileSdkVersion=30

ui/src/main/java/io/snabble/sdk/ui/scanner/SelfScanningView.java

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import androidx.appcompat.app.AlertDialog;
2626
import androidx.core.app.ActivityCompat;
27+
import androidx.core.content.res.ResourcesCompat;
2728
import androidx.core.util.Pair;
2829
import androidx.core.view.ViewCompat;
2930
import androidx.core.widget.ImageViewCompat;
@@ -49,6 +50,7 @@
4950
import io.snabble.sdk.ui.utils.I18nUtils;
5051
import io.snabble.sdk.ui.utils.OneShotClickListener;
5152
import io.snabble.sdk.ui.utils.UIUtils;
53+
import io.snabble.sdk.ui.views.MessageBoxStackView;
5254
import io.snabble.sdk.utils.Dispatch;
5355
import io.snabble.sdk.utils.SimpleActivityLifecycleCallbacks;
5456
import io.snabble.sdk.utils.Utils;
@@ -68,7 +70,7 @@ public class SelfScanningView extends FrameLayout {
6870
private boolean isShowingHint;
6971
private boolean manualCameraControl;
7072
private int topDownInfoBoxOffset;
71-
private UIUtils.TopDownInfoBoxController lastMessage;
73+
private MessageBoxStackView messages;
7274

7375
public SelfScanningView(Context context) {
7476
super(context);
@@ -94,6 +96,7 @@ private void inflateView() {
9496

9597
shoppingCart = project.getShoppingCart();
9698

99+
messages = findViewById(R.id.messages);
97100
barcodeScanner = findViewById(R.id.barcode_scanner_view);
98101
noPermission = findViewById(R.id.no_permission);
99102

@@ -135,7 +138,10 @@ public void click() {
135138
isInitialized = true;
136139
startBarcodeScanner(false);
137140

138-
setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
141+
setLayoutParams(new ViewGroup.LayoutParams(
142+
ViewGroup.LayoutParams.MATCH_PARENT,
143+
ViewGroup.LayoutParams.MATCH_PARENT)
144+
);
139145
}
140146

141147
private void showShoppingCart() {
@@ -162,10 +168,6 @@ private void updateCartButton() {
162168
}
163169

164170
public void lookupAndShowProduct(List<ScannedCode> scannedCodes, BarcodeFormat barcodeFormat) {
165-
if (lastMessage != null) {
166-
lastMessage.hide();
167-
lastMessage = null;
168-
}
169171
new ProductResolver.Builder(getContext())
170172
.setCodes(scannedCodes)
171173
.setBarcodeFormat(barcodeFormat)
@@ -262,23 +264,21 @@ private void handleBarcodeDetected(final Barcode barcode) {
262264
}
263265

264266
private void showInfo(final String text) {
265-
Dispatch.mainThread(() ->
266-
lastMessage = UIUtils.showTopDownInfoBox(
267-
SelfScanningView.this,
268-
text,
269-
UIUtils.getDurationByLength(text),
270-
UIUtils.INFO_NEUTRAL,
271-
topDownInfoBoxOffset));
267+
Dispatch.mainThread(() -> messages.show(
268+
text,
269+
UIUtils.getDurationByLength(text),
270+
ResourcesCompat.getColor(getResources(), R.color.snabble_infoColor, null),
271+
ResourcesCompat.getColor(getResources(), R.color.snabble_infoTextColor, null)
272+
));
272273
}
273274

274275
private void showWarning(final String text) {
275-
Dispatch.mainThread(() ->
276-
lastMessage = UIUtils.showTopDownInfoBox(
277-
SelfScanningView.this,
278-
text,
279-
UIUtils.getDurationByLength(text),
280-
UIUtils.INFO_WARNING,
281-
topDownInfoBoxOffset));
276+
Dispatch.mainThread(() -> messages.show(
277+
text,
278+
UIUtils.getDurationByLength(text),
279+
ResourcesCompat.getColor(getResources(), R.color.snabble_infoColorWarning, null),
280+
ResourcesCompat.getColor(getResources(), R.color.snabble_infoTextColorWarning, null)
281+
));
282282
}
283283

284284
public void setDefaultButtonVisibility(boolean visible) {

ui/src/main/java/io/snabble/sdk/ui/utils/UIUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public static Activity getHostActivity(Context context) {
8080
}
8181

8282
public static int getDurationByLength(String text) {
83-
return Math.max(Math.min(text.length() * 70, 2000), 7000);
83+
return Math.min(Math.max(text.length() * 70, 4000), 7000);
8484
}
8585

8686
public static int getColorByAttribute(Context context, @AttrRes int attrResId) {
@@ -89,6 +89,7 @@ public static int getColorByAttribute(Context context, @AttrRes int attrResId) {
8989
return typedValue.data;
9090
}
9191

92+
@Deprecated
9293
public static TopDownInfoBoxController showTopDownInfoBox(ViewGroup parent, String text, int duration, int type, int offsetY) {
9394
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
9495
final TextView info = (TextView)inflater.inflate(R.layout.snabble_view_info, parent, false);
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package io.snabble.sdk.ui.views
2+
3+
import android.animation.*
4+
import android.content.Context
5+
import android.graphics.Color
6+
import android.util.AttributeSet
7+
import android.view.ViewGroup
8+
import android.widget.FrameLayout
9+
import android.widget.LinearLayout
10+
import android.widget.TextView
11+
import androidx.cardview.widget.CardView
12+
import androidx.core.view.isVisible
13+
import com.google.android.material.animation.AnimationUtils.LINEAR_INTERPOLATOR
14+
import io.snabble.sdk.ui.R
15+
import io.snabble.sdk.utils.Dispatch
16+
import io.snabble.sdk.utils.Utils
17+
import io.snabble.sdk.utils.Utils.dp2px
18+
19+
20+
class MessageBox @JvmOverloads constructor(
21+
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
22+
) : FrameLayout(context, attrs, defStyleAttr) {
23+
val text: TextView by lazy { findViewById(R.id.text) }
24+
val cardView: CardView by lazy { findViewById(R.id.card_view) }
25+
26+
init {
27+
inflate(context, R.layout.snabble_view_message_box, this)
28+
isVisible = false
29+
}
30+
31+
fun animateViewIn() {
32+
post {
33+
isVisible = true
34+
startFadeInAnimation()
35+
}
36+
}
37+
38+
fun animateViewOut(onEnd: Runnable) {
39+
startFadeOutAnimation(onEnd)
40+
}
41+
42+
private fun startFadeInAnimation() {
43+
val alphaAnimator = getAlphaAnimator(0f, 1f)
44+
val scaleAnimator: ValueAnimator = getScaleAnimator(0.8f, 1.0f)
45+
val animatorSet = AnimatorSet()
46+
animatorSet.playTogether(alphaAnimator, scaleAnimator)
47+
animatorSet.duration = 150
48+
animatorSet.start()
49+
}
50+
51+
private fun startFadeOutAnimation(onEnd: Runnable) {
52+
val animator = getAlphaAnimator(1f, 0f)
53+
animator.duration = 75
54+
animator.addListener(
55+
object : AnimatorListenerAdapter() {
56+
override fun onAnimationEnd(animator: Animator) {
57+
onEnd.run()
58+
}
59+
})
60+
animator.start()
61+
}
62+
63+
private fun getAlphaAnimator(vararg alphaValues: Float): ValueAnimator {
64+
val animator = ValueAnimator.ofFloat(*alphaValues)
65+
animator.interpolator = LINEAR_INTERPOLATOR
66+
animator.addUpdateListener { valueAnimator -> alpha = valueAnimator.animatedValue as Float }
67+
return animator
68+
}
69+
70+
private fun getScaleAnimator(vararg scaleValues: Float): ValueAnimator {
71+
val animator = ValueAnimator.ofFloat(*scaleValues)
72+
animator.interpolator = com.google.android.material.animation.AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR
73+
animator.addUpdateListener { valueAnimator ->
74+
val scale = valueAnimator.animatedValue as Float
75+
scaleX = scale
76+
scaleY = scale
77+
}
78+
return animator
79+
}
80+
}
81+
82+
class MessageBoxStackView @JvmOverloads constructor(
83+
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
84+
) : LinearLayout(context, attrs, defStyleAttr) {
85+
init {
86+
inflate(context, R.layout.snabble_view_message_box_stack, this)
87+
orientation = VERTICAL
88+
}
89+
90+
fun show(message: String,
91+
duration: Long = 5000,
92+
bgColor: Int = Color.WHITE,
93+
textColor: Int = Color.BLACK) {
94+
val tag = Utils.sha1Hex(message)
95+
val duplicate = findViewWithTag<MessageBox>(tag)
96+
97+
if (duplicate != null) {
98+
removeView(duplicate)
99+
}
100+
101+
val messageBox = MessageBox(context)
102+
messageBox.text.text = message
103+
messageBox.text.setTextColor(textColor)
104+
messageBox.tag = tag
105+
messageBox.cardView.setCardBackgroundColor(bgColor)
106+
107+
val lp = MarginLayoutParams(
108+
ViewGroup.LayoutParams.MATCH_PARENT,
109+
ViewGroup.LayoutParams.WRAP_CONTENT,
110+
)
111+
lp.leftMargin = dp2px(context, 16.0f)
112+
lp.topMargin = dp2px(context, 8.0f)
113+
lp.rightMargin = dp2px(context, 16.0f)
114+
lp.bottomMargin = dp2px(context, 8.0f)
115+
addView(messageBox, 0, lp)
116+
117+
messageBox.animateViewIn()
118+
119+
Dispatch.mainThread({
120+
if (indexOfChild(messageBox) != -1) {
121+
messageBox.animateViewOut {
122+
removeView(messageBox)
123+
}
124+
}
125+
}, duration)
126+
}
127+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<merge xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent"
7+
tools:parentTag="android.widget.FrameLayout">
8+
<androidx.cardview.widget.CardView
9+
android:id="@+id/card_view"
10+
android:layout_width="match_parent"
11+
android:layout_height="match_parent"
12+
app:cardElevation="2dp"
13+
app:cardCornerRadius="4dp">
14+
<TextView
15+
android:id="@+id/text"
16+
android:padding="16dp"
17+
android:gravity="center_vertical"
18+
android:layout_width="match_parent"
19+
android:layout_height="match_parent" />
20+
</androidx.cardview.widget.CardView>
21+
</merge>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<merge xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:layout_width="match_parent"
5+
android:layout_height="wrap_content"
6+
tools:parentTag="android.widget.LinearLayout">
7+
8+
</merge>

ui/src/main/res/layout/snabble_view_self_scanning.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@
1212
android:layout_height="match_parent"
1313
android:visibility="gone" />
1414

15+
<io.snabble.sdk.ui.views.MessageBoxStackView
16+
android:id="@+id/messages"
17+
android:layout_width="match_parent"
18+
android:layout_height="wrap_content"
19+
android:paddingTop="8dp"
20+
android:clipChildren="false"
21+
android:clipToPadding="false"
22+
android:orientation="vertical" />
23+
1524
<TextView
1625
android:layout_width="match_parent"
1726
android:layout_height="match_parent"

ui/src/main/res/values-de/strings.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@
152152
<string name="Snabble.Scanner.Camera.allowAccess">Entschuldigung, wir benötigen Zugriff auf deine Kamera, um mit ihr Barcodes zu scannen. Bitte erlaube die Nutzung in den Einstellungen und kehre dann hierher zurück.</string>
153153
<string name="Snabble.Scanner.couponAdded">Gutschein „%s“ hinzugefügt</string>
154154
<string name="Snabble.Scanner.duplicateDepositScanned">Jeder Pfandbon kann nur einmal gescannt werden.</string>
155-
<string name="Snabble.Scanner.enterBarcode">Barcode eingeben</string>
155+
<string name="Snabble.Scanner.enterBarcode">Code eingeben</string>
156156
<string name="Snabble.Scanner.enterCodeButton">Barcode\neingeben</string>
157157
<!-- Text der Snackbar die Angezeigt wird, wenn der Benutzer noch nie ein Produkt gescannt hat -->
158158
<string name="Snabble.Scanner.firstScan">Scanne dein erstes Produkt. Halte dazu den Barcode eines Produktes in die Kamera.</string>
@@ -189,7 +189,7 @@
189189
<string name="Snabble.Shoppingcart.coupon">Gutschein</string>
190190
<string name="Snabble.Shoppingcart.coupons">Gutscheine</string>
191191
<string name="Snabble.Shoppingcart.deposit">Pfand</string>
192-
<string name="Snabble.Shoppingcart.discounts">Rabatte insgesamt</string>
192+
<string name="Snabble.Shoppingcart.discounts">Rabatt</string>
193193
<string name="Snabble.Shoppingcart.emptyState.buttonTitle">Jetzt scannen</string>
194194
<string name="Snabble.Shoppingcart.emptyState.description">Dein Warenkorb ist leer. Scanne Barcodes von Artikeln, die du kaufen möchtest.</string>
195195
<string name="Snabble.Shoppingcart.emptyState.restartButtonTitle">Neuen Einkauf beginnen</string>

ui/src/main/res/values-en/strings.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@
152152
<string name="Snabble.Scanner.Camera.allowAccess">Sorry, we\'ll need to access your camera to scan barcodes. Please allow this in the settings and return here.</string>
153153
<string name="Snabble.Scanner.couponAdded">Added coupon “%s”</string>
154154
<string name="Snabble.Scanner.duplicateDepositScanned">Each deposit slip can only be scanned once.</string>
155-
<string name="Snabble.Scanner.enterBarcode">Enter Barcode</string>
155+
<string name="Snabble.Scanner.enterBarcode">Enter code</string>
156156
<string name="Snabble.Scanner.enterCodeButton">Enter\nbarcode</string>
157157
<!-- Text der Snackbar die Angezeigt wird, wenn der Benutzer noch nie ein Produkt gescannt hat -->
158158
<string name="Snabble.Scanner.firstScan">Scan your first product. Place the barcode in front of your camera.</string>
@@ -189,7 +189,7 @@
189189
<string name="Snabble.Shoppingcart.coupon">Coupon</string>
190190
<string name="Snabble.Shoppingcart.coupons">Coupons</string>
191191
<string name="Snabble.Shoppingcart.deposit">Deposit</string>
192-
<string name="Snabble.Shoppingcart.discounts">Total discounts</string>
192+
<string name="Snabble.Shoppingcart.discounts">Discounts</string>
193193
<string name="Snabble.Shoppingcart.emptyState.buttonTitle">Scan now</string>
194194
<string name="Snabble.Shoppingcart.emptyState.description">Visit a store that supports Snabble and scan the barcodes of products you wish to purchase.</string>
195195
<string name="Snabble.Shoppingcart.emptyState.restartButtonTitle">Start new shopping trip</string>

0 commit comments

Comments
 (0)