Skip to content

Commit eb3cb9d

Browse files
committed
Added support for EAN13 internal checksum
1 parent 8eeb5c5 commit eb3cb9d

File tree

5 files changed

+200
-37
lines changed

5 files changed

+200
-37
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.snabble.sdk;
2+
3+
import junit.framework.Assert;
4+
5+
import org.junit.Test;
6+
7+
import io.snabble.sdk.codes.EAN13;
8+
import io.snabble.sdk.codes.ScannableCode;
9+
10+
public class EAN13Test extends SnabbleSdkTest {
11+
@Test
12+
public void testEAN13() {
13+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001801").getClass());
14+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001802").getClass());
15+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001803").getClass());
16+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001804").getClass());
17+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001805").getClass());
18+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001806").getClass());
19+
Assert.assertEquals(EAN13.class, ScannableCode.parse(snabbleSdk, "4029764001807").getClass());
20+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001808").getClass());
21+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001809").getClass());
22+
Assert.assertEquals(ScannableCode.class, ScannableCode.parse(snabbleSdk, "4029764001800").getClass());
23+
24+
Assert.assertEquals(7, EAN13.checksum("402976400180"));
25+
Assert.assertEquals(0, EAN13.checksum("200123400000"));
26+
Assert.assertEquals(7, EAN13.checksum("200123500129"));
27+
Assert.assertEquals(3, EAN13.checksum("629104150021"));
28+
Assert.assertEquals(2, EAN13.checksum("222111100000"));
29+
Assert.assertEquals(1, EAN13.checksum("222111200000"));
30+
}
31+
32+
@Test
33+
public void testInternalChecksum() {
34+
checkInternalChecksum(6, "2810276146856");
35+
checkInternalChecksum(5, "2810275005024");
36+
checkInternalChecksum(4, "2746724008288");
37+
checkInternalChecksum(0, "2850090007827");
38+
checkInternalChecksum(6, "2957776004085");
39+
checkInternalChecksum(2, "2957822002843");
40+
checkInternalChecksum(6, "2810606013360");
41+
checkInternalChecksum(0, "2810270002387");
42+
checkInternalChecksum(8, "2810478001908");
43+
checkInternalChecksum(2, "2893482001744");
44+
checkInternalChecksum(6, "2957796002801");
45+
checkInternalChecksum(5, "2893035003607");
46+
checkInternalChecksum(5, "2810115003609");
47+
checkInternalChecksum(4, "2810444001505");
48+
checkInternalChecksum(5, "2893525001724");
49+
checkInternalChecksum(3, "2957783000742");
50+
checkInternalChecksum(0, "2958300015089");
51+
checkInternalChecksum(7, "2810307003349");
52+
checkInternalChecksum(9, "2810029003221");
53+
checkInternalChecksum(0, "2810030004729");
54+
checkInternalChecksum(5, "2956755020146");
55+
checkInternalChecksum(6, "2957736007385");
56+
checkInternalChecksum(9, "2957679012040");
57+
checkInternalChecksum(3, "2810063024800");
58+
}
59+
60+
private void checkInternalChecksum(int checksum, String code){
61+
Assert.assertEquals(checksum, EAN13.internalChecksum(code));
62+
63+
ScannableCode scannableCode = ScannableCode.parse(snabbleSdk, code);
64+
Assert.assertEquals(true, scannableCode.isEmbeddedDataOk());
65+
}
66+
}

core/src/main/java/io/snabble/sdk/codes/EAN13.java

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.snabble.sdk.codes;
22

3+
import android.util.SparseIntArray;
4+
35
import java.io.Serializable;
46

57
public class EAN13 extends ScannableCode implements Serializable {
@@ -16,25 +18,30 @@ public EAN13(String code, String[] weighPrefixes, String[] pricePrefixes, String
1618
throw new IllegalArgumentException("Not a valid EAN13 code");
1719
}
1820

19-
for(String prefix : weighPrefixes){
20-
if(code.startsWith(prefix)){
21-
hasWeighData = true;
21+
if(isEmbeddedDataOk()) {
22+
for (String prefix : weighPrefixes) {
23+
if (code.startsWith(prefix)) {
24+
hasWeighData = true;
25+
}
2226
}
23-
}
2427

25-
for(String prefix : pricePrefixes){
26-
if(code.startsWith(prefix)){
27-
hasPriceData = true;
28+
for (String prefix : pricePrefixes) {
29+
if (code.startsWith(prefix)) {
30+
hasPriceData = true;
31+
}
2832
}
29-
}
3033

31-
for(String prefix : amountPrefixes){
32-
if(code.startsWith(prefix)){
33-
hasAmountData = true;
34+
for (String prefix : amountPrefixes) {
35+
if (code.startsWith(prefix)) {
36+
hasAmountData = true;
37+
}
3438
}
39+
40+
lookupCode = code.substring(0, 6) + "0000000";
41+
} else {
42+
lookupCode = code;
3543
}
3644

37-
lookupCode = code.substring(0, 6) + "0000000";
3845
embeddedData = Integer.parseInt(code.substring(7, 12));
3946
}
4047

@@ -68,6 +75,18 @@ public boolean hasEmbeddedData(){
6875
return hasAmountData || hasPriceData || hasWeighData;
6976
}
7077

78+
@Override
79+
public boolean isEmbeddedDataOk() {
80+
if(!hasEmbeddedData()){
81+
return true;
82+
}
83+
84+
String code = getCode();
85+
int digit = Character.digit(code.charAt(6), 10);
86+
int check = internalChecksum(code);
87+
return check == digit;
88+
}
89+
7190
/**
7291
* Calculates the checksum of an given ean string.
7392
* <p>
@@ -98,6 +117,31 @@ public static boolean checkChecksum(String ean) {
98117
return false;
99118
}
100119

120+
public static int internalChecksum(String code) {
121+
int sum = 0;
122+
for (int i = 0; i < 5; i++) {
123+
int d = Character.digit(code.charAt(i + 7), 10);
124+
sum += i + weightedProduct(i, d);
125+
}
126+
int mod10 = (10 - (sum % 10)) % 10;
127+
return check5minusReverse.get(mod10, -1);
128+
}
129+
130+
private static int weightedProduct(int index, int digit){
131+
switch(index){
132+
case 0:
133+
case 3:
134+
return check5plus.get(digit, -1);
135+
case 1:
136+
case 4:
137+
return check2minus.get(digit, -1);
138+
case 2:
139+
return check5minus.get(digit, -1);
140+
default:
141+
return -1;
142+
}
143+
}
144+
101145
private static boolean allDigits(String str) {
102146
for (int i = 0; i < str.length(); i++) {
103147
if (!Character.isDigit(str.charAt(i))) {
@@ -123,4 +167,48 @@ public static boolean isEan13(String ean) {
123167

124168
return false;
125169
}
170+
171+
private static SparseIntArray check5plus = new SparseIntArray();
172+
private static SparseIntArray check2minus = new SparseIntArray();
173+
private static SparseIntArray check5minus = new SparseIntArray();
174+
private static SparseIntArray check5minusReverse = new SparseIntArray();
175+
176+
static {
177+
check5plus.put(0, 0);
178+
check5plus.put(1, 5);
179+
check5plus.put(2, 1);
180+
check5plus.put(3, 6);
181+
check5plus.put(4, 2);
182+
check5plus.put(5, 7);
183+
check5plus.put(6, 3);
184+
check5plus.put(7, 8);
185+
check5plus.put(8, 4);
186+
check5plus.put(9, 9);
187+
188+
check2minus.put(0, 0);
189+
check2minus.put(1, 2);
190+
check2minus.put(2, 4);
191+
check2minus.put(3, 6);
192+
check2minus.put(4, 8);
193+
check2minus.put(5, 9);
194+
check2minus.put(6, 1);
195+
check2minus.put(7, 3);
196+
check2minus.put(8, 5);
197+
check2minus.put(9, 7);
198+
199+
check5minus.put(0, 0);
200+
check5minus.put(1, 5);
201+
check5minus.put(2, 9);
202+
check5minus.put(3, 4);
203+
check5minus.put(4, 8);
204+
check5minus.put(5, 3);
205+
check5minus.put(6, 7);
206+
check5minus.put(7, 2);
207+
check5minus.put(8, 6);
208+
check5minus.put(9, 1);
209+
210+
for(int i=0; i<check5minus.size(); i++){
211+
check5minusReverse.put(check5minus.valueAt(i), check5minus.keyAt(i));
212+
}
213+
}
126214
}

core/src/main/java/io/snabble/sdk/codes/ScannableCode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ public boolean hasEmbeddedData(){
3939
return false;
4040
}
4141

42+
public boolean isEmbeddedDataOk() {
43+
return true;
44+
}
45+
4246
public static ScannableCode parse(SnabbleSdk snabbleSdk, String code){
4347
if(EAN13.isEan13(code)){
4448
return new EAN13(code,

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

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import android.text.InputType;
1919
import android.util.AttributeSet;
2020
import android.view.View;
21-
import android.view.ViewGroup;
2221
import android.widget.EditText;
2322
import android.widget.LinearLayout;
2423
import android.widget.TextView;
@@ -49,15 +48,12 @@ public class SelfScanningView extends CoordinatorLayout implements Checkout.OnCh
4948
private boolean isRunning;
5049

5150
private DelayedProgressDialog progressDialog;
52-
private boolean ignore;
5351
private Checkout checkout;
5452
private long detectAfterTimeMs;
5553

5654
private DialogInterface.OnCancelListener progressDialogCancelListener = new DialogInterface.OnCancelListener() {
5755
@Override
5856
public void onCancel(DialogInterface dialog) {
59-
ignore = true;
60-
6157
barcodeScanner.resume();
6258
checkout.cancel();
6359
}
@@ -155,9 +151,19 @@ public void onShow(DialogInterface dialog) {
155151
}
156152

157153
public void lookupAndShowProduct(final ScannableCode scannedCode) {
158-
ignore = false;
159154
productDialog.dismiss();
160155

156+
if(!scannedCode.isEmbeddedDataOk()){
157+
delayNextScan();
158+
159+
Telemetry.event(Telemetry.Event.ScannedUnknownCode, scannedCode.getCode());
160+
UIUtils.snackbar(SelfScanningView.this,
161+
R.string.Snabble_Scanner_unknownBarcode,
162+
Snackbar.LENGTH_LONG)
163+
.show();
164+
return;
165+
}
166+
161167
progressDialog.setOnCancelListener(progressDialogCancelListener);
162168
progressDialog.showAfterDelay(300);
163169
barcodeScanner.pause();
@@ -199,6 +205,10 @@ public void onError() {
199205
}
200206
}
201207

208+
private void delayNextScan() {
209+
detectAfterTimeMs = SystemClock.elapsedRealtime() + 2000;
210+
}
211+
202212
@SuppressLint("MissingPermission")
203213
private void handleBarcodeDetected(final Barcode barcode) {
204214
if (SystemClock.elapsedRealtime() > detectAfterTimeMs) {
@@ -213,10 +223,6 @@ private void handleBarcodeDetected(final Barcode barcode) {
213223
}
214224

215225
private void handleProductAvailable(Product product, boolean wasOnlineProduct, ScannableCode scannedCode) {
216-
if (ignore) {
217-
return;
218-
}
219-
220226
progressDialog.dismiss();
221227
showProduct(product, scannedCode);
222228

@@ -228,13 +234,9 @@ private void handleProductAvailable(Product product, boolean wasOnlineProduct, S
228234
}
229235

230236
private void handleProductNotFound(ScannableCode scannedCode) {
231-
if (ignore) {
232-
return;
233-
}
234-
235237
progressDialog.dismiss();
236238
barcodeScanner.resume();
237-
detectAfterTimeMs = SystemClock.elapsedRealtime() + 2000;
239+
delayNextScan();
238240

239241
Telemetry.event(Telemetry.Event.ScannedUnknownCode, scannedCode.getCode());
240242
UIUtils.snackbar(SelfScanningView.this,
@@ -244,13 +246,9 @@ private void handleProductNotFound(ScannableCode scannedCode) {
244246
}
245247

246248
private void handleProductError() {
247-
if (ignore) {
248-
return;
249-
}
250-
251249
progressDialog.dismiss();
252250
barcodeScanner.resume();
253-
detectAfterTimeMs = SystemClock.elapsedRealtime() + 2000;
251+
delayNextScan();
254252

255253
UIUtils.snackbar(SelfScanningView.this,
256254
R.string.Snabble_Scanner_networkError,

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
public class DelayedProgressDialog extends ProgressDialog {
99
private Handler handler = new Handler(Looper.getMainLooper());
1010
private Runnable runnable;
11+
private boolean isDismissed = false;
1112

1213
public DelayedProgressDialog(Context context) {
1314
super(context);
@@ -16,31 +17,37 @@ public DelayedProgressDialog(Context context) {
1617
public void showAfterDelay(long afterDelayMs) {
1718
runnable = new Runnable() {
1819
public void run() {
19-
show();
20+
if(!isDismissed) {
21+
show();
22+
}
2023
}
2124
};
2225

26+
isDismissed = false;
2327
handler.postDelayed(runnable, afterDelayMs);
2428
}
2529

2630
@Override
2731
public void hide() {
28-
handler.removeCallbacks(runnable);
29-
setOnCancelListener(null);
32+
stopCallbacks();
3033
super.hide();
3134
}
3235

3336
@Override
3437
public void cancel() {
38+
stopCallbacks();
39+
super.cancel();
40+
}
41+
42+
private void stopCallbacks() {
43+
isDismissed = true;
3544
handler.removeCallbacks(runnable);
3645
setOnCancelListener(null);
37-
super.cancel();
3846
}
3947

4048
@Override
4149
public void dismiss() {
42-
handler.removeCallbacks(runnable);
43-
setOnCancelListener(null);
50+
stopCallbacks();
4451
super.dismiss();
4552
}
4653
}

0 commit comments

Comments
 (0)