From 4e043edb64b8eb8ce4969567266e85006de13848 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Tue, 6 Apr 2021 00:33:35 +0200 Subject: [PATCH] Show all barcodes and recover from invalid selection --- CHANGELOG.md | 2 + .../card_locker/BarcodeImageWriterTask.java | 89 +++++++++++++++++-- .../card_locker/BarcodeSelectorActivity.java | 45 ++++++---- .../card_locker/LoyaltyCardEditActivity.java | 32 +++++-- .../card_locker/LoyaltyCardViewActivity.java | 10 ++- .../res/layout/barcode_selector_activity.xml | 1 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 144 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c2c950d..2c4a89429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Changes: - Simplify font sizing configuration - Several small UI fixes - Use letter icon for shortcuts too +- Always show all barcode types in manual entry +- Revert to valid barcode type if the value is not valid for the selected barcode type in edit activity ## v1.12 (2021-03-30) diff --git a/app/src/main/java/protect/card_locker/BarcodeImageWriterTask.java b/app/src/main/java/protect/card_locker/BarcodeImageWriterTask.java index 58231c9a7..231b958c1 100644 --- a/app/src/main/java/protect/card_locker/BarcodeImageWriterTask.java +++ b/app/src/main/java/protect/card_locker/BarcodeImageWriterTask.java @@ -1,6 +1,8 @@ package protect.card_locker; import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PorterDuff; import android.os.AsyncTask; import android.util.Log; import android.view.View; @@ -23,6 +25,9 @@ class BarcodeImageWriterTask extends AsyncTask { private static final String TAG = "Catima"; + private static final int IS_VALID = 999; + private boolean isSuccesful; + // When drawn in a smaller window 1D barcodes for some reason end up // squished, whereas 2D barcodes look fine. private static final int MAX_WIDTH_1D = 1500; @@ -30,14 +35,20 @@ class BarcodeImageWriterTask extends AsyncTask private final WeakReference imageViewReference; private final WeakReference textViewReference; - private final String cardId; + private String cardId; private final BarcodeFormat format; private final int imageHeight; private final int imageWidth; + private final boolean showFallback; + private final Runnable callback; BarcodeImageWriterTask(ImageView imageView, String cardIdString, - BarcodeFormat barcodeFormat, TextView textView) + BarcodeFormat barcodeFormat, TextView textView, + boolean showFallback, Runnable callback) { + isSuccesful = true; + this.callback = callback; + // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference<>(imageView); textViewReference = new WeakReference<>(textView); @@ -59,11 +70,8 @@ class BarcodeImageWriterTask extends AsyncTask double ratio = (double)MAX_WIDTH / (double)imageView.getWidth(); imageHeight = (int)(imageView.getHeight() * ratio); } - } - BarcodeImageWriterTask(ImageView imageView, String cardIdString, BarcodeFormat barcodeFormat) - { - this(imageView, cardIdString, barcodeFormat, null); + this.showFallback = showFallback; } private int getMaxWidth(BarcodeFormat format) @@ -96,7 +104,41 @@ class BarcodeImageWriterTask extends AsyncTask } } - public Bitmap doInBackground(Void... params) + private String getFallbackString(BarcodeFormat format) + { + switch(format) + { + // 2D barcodes + case AZTEC: + return "AZTEC"; + case DATA_MATRIX: + return "DATA_MATRIX"; + case PDF_417: + return "PDF_417"; + case QR_CODE: + return "QR_CODE"; + + // 1D barcodes: + case CODABAR: + return "C0C"; + case CODE_39: + return "CODE_39"; + case CODE_128: + return "CODE_128"; + case EAN_8: + return "32123456"; + case EAN_13: + return "5901234123457"; + case ITF: + return "1003"; + case UPC_A: + return "123456789012"; + default: + throw new IllegalArgumentException("No fallback known for this barcode type"); + } + } + + private Bitmap generate() { if (cardId.isEmpty()) { @@ -165,13 +207,30 @@ class BarcodeImageWriterTask extends AsyncTask catch(OutOfMemoryError e) { Log.w(TAG, "Insufficient memory to render barcode, " - + imageWidth + "x" + imageHeight + ", " + format.name() - + ", length=" + cardId.length(), e); + + imageWidth + "x" + imageHeight + ", " + format.name() + + ", length=" + cardId.length(), e); } return null; } + public Bitmap doInBackground(Void... params) + { + Bitmap bitmap = generate(); + + if (bitmap == null) { + isSuccesful = false; + + if (showFallback) { + Log.i(TAG, "Barcode generation failed, generating fallback..."); + cardId = getFallbackString(format); + bitmap = generate(); + } + } + + return bitmap; + } + protected void onPostExecute(Bitmap result) { Log.i(TAG, "Finished generating barcode image of type " + format + ": " + cardId); @@ -182,6 +241,8 @@ class BarcodeImageWriterTask extends AsyncTask return; } + imageView.setTag(isSuccesful); + imageView.setImageBitmap(result); TextView textView = textViewReference.get(); @@ -190,6 +251,12 @@ class BarcodeImageWriterTask extends AsyncTask Log.i(TAG, "Displaying barcode"); imageView.setVisibility(View.VISIBLE); + if (isSuccesful) { + imageView.setColorFilter(null); + } else { + imageView.setColorFilter(Color.GRAY, PorterDuff.Mode.LIGHTEN); + } + if (textView != null) { textView.setVisibility(View.VISIBLE); textView.setText(format.name()); @@ -203,5 +270,9 @@ class BarcodeImageWriterTask extends AsyncTask textView.setVisibility(View.GONE); } } + + if (callback != null) { + callback.run(); + } } } diff --git a/app/src/main/java/protect/card_locker/BarcodeSelectorActivity.java b/app/src/main/java/protect/card_locker/BarcodeSelectorActivity.java index 201315650..c5160798f 100644 --- a/app/src/main/java/protect/card_locker/BarcodeSelectorActivity.java +++ b/app/src/main/java/protect/card_locker/BarcodeSelectorActivity.java @@ -17,6 +17,7 @@ import android.view.ViewTreeObserver; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; import com.google.common.collect.ImmutableMap; import com.google.zxing.BarcodeFormat; @@ -104,20 +105,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity { Log.d(TAG, "Entered text: " + s); - // Stop any async tasks which may not have been started yet - for(AsyncTask task : barcodeGeneratorTasks) - { - task.cancel(false); - } - barcodeGeneratorTasks.clear(); - - // Update barcodes - for(String key : barcodeViewMap.keySet()) - { - ImageView image = findViewById(barcodeViewMap.get(key).first); - TextView text = findViewById(barcodeViewMap.get(key).second); - createBarcodeOption(image, key, s.toString(), text); - } + generateBarcodes(s.toString()); View noBarcodeButtonView = findViewById(R.id.noBarcode); setButtonListener(noBarcodeButtonView, s.toString()); @@ -137,6 +125,25 @@ public class BarcodeSelectorActivity extends AppCompatActivity if(initialCardId != null) { cardId.setText(initialCardId); + } else { + generateBarcodes(""); + } + } + + private void generateBarcodes(String value) { + // Stop any async tasks which may not have been started yet + for(AsyncTask task : barcodeGeneratorTasks) + { + task.cancel(false); + } + barcodeGeneratorTasks.clear(); + + // Update barcodes + for(String key : barcodeViewMap.keySet()) + { + ImageView image = findViewById(barcodeViewMap.get(key).first); + TextView text = findViewById(barcodeViewMap.get(key).second); + createBarcodeOption(image, key, value, text); } } @@ -171,6 +178,12 @@ public class BarcodeSelectorActivity extends AppCompatActivity public void onClick(View v) { Log.d(TAG, "Selected barcode type " + formatType); + + if (!((boolean) image.getTag())) { + Toast.makeText(BarcodeSelectorActivity.this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show(); + return; + } + Intent result = new Intent(); result.putExtra(BARCODE_FORMAT, formatType); result.putExtra(BARCODE_CONTENTS, cardId); @@ -193,7 +206,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity image.getViewTreeObserver().removeOnGlobalLayoutListener(this); Log.d(TAG, "Generating barcode for type " + formatType); - BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text); + BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text, true, null); barcodeGeneratorTasks.add(task); task.execute(); } @@ -202,7 +215,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity else { Log.d(TAG, "Generating barcode for type " + formatType); - BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text); + BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text, true, null); barcodeGeneratorTasks.add(task); task.execute(); } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 4101b4d8a..0502945e4 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -99,6 +99,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity AlertDialog confirmExitDialog = null; boolean validBalance = true; + BarcodeFormat lastValidBarcodeType = null; + Runnable updateOrRevertValidBarcodeType; HashMap currencies = new HashMap<>(); @@ -158,6 +160,18 @@ public class LoyaltyCardEditActivity extends AppCompatActivity enterButton = findViewById(R.id.enterButton); + updateOrRevertValidBarcodeType = new Runnable() { + @Override + public void run() { + if ((boolean) barcodeImage.getTag()) { + lastValidBarcodeType = (BarcodeFormat) barcodeTypeField.getTag(); + } else { + Toast.makeText(LoyaltyCardEditActivity.this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show(); + barcodeTypeField.setText(lastValidBarcodeType != null ? lastValidBarcodeType.name() : getString(R.string.noBarcode)); + } + } + }; + storeFieldEdit.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @@ -403,7 +417,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity } @Override - public void afterTextChanged(Editable s) { } + public void afterTextChanged(Editable s) { + ArrayList barcodeList = new ArrayList<>(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES); + barcodeList.add(0, getString(R.string.noBarcode)); + ArrayAdapter barcodeAdapter = new ArrayAdapter<>(LoyaltyCardEditActivity.this, android.R.layout.select_dialog_item, barcodeList); + barcodeTypeField.setAdapter(barcodeAdapter); + } }); tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @@ -639,11 +658,6 @@ public class LoyaltyCardEditActivity extends AppCompatActivity enterButton.setOnClickListener(new EditCardIdAndBarcode()); barcodeImage.setOnClickListener(new EditCardIdAndBarcode()); - ArrayList barcodeList = new ArrayList<>(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES); - barcodeList.add(0, getString(R.string.noBarcode)); - ArrayAdapter barcodeAdapter = new ArrayAdapter<>(this, android.R.layout.select_dialog_item, barcodeList); - barcodeTypeField.setAdapter(barcodeAdapter); - FloatingActionButton saveButton = findViewById(R.id.fabSave); saveButton.setOnClickListener(v -> doSave()); @@ -937,7 +951,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity String cardIdString = barcodeIdField.getTag() != null ? barcodeIdField.getTag().toString() : cardIdFieldView.getText().toString(); BarcodeFormat barcodeFormat = (BarcodeFormat) barcodeTypeField.getTag(); - if (barcodeFormat == null || cardIdString.isEmpty()) { + if (barcodeFormat == null || cardIdString.isEmpty() || !BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES.contains(barcodeFormat.name())) { hideBarcode(); } else { generateBarcode(cardIdString, barcodeFormat); @@ -956,12 +970,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this); Log.d(TAG, "ImageView size now known"); - new BarcodeImageWriterTask(barcodeImage, cardIdString, barcodeFormat).execute(); + new BarcodeImageWriterTask(barcodeImage, cardIdString, barcodeFormat, null, false, updateOrRevertValidBarcodeType).execute(); } }); } else { Log.d(TAG, "ImageView size known known, creating barcode"); - new BarcodeImageWriterTask(barcodeImage, cardIdString, barcodeFormat).execute(); + new BarcodeImageWriterTask(barcodeImage, cardIdString, barcodeFormat, null, false, updateOrRevertValidBarcodeType).execute(); } showBarcode(); diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index f84a1ecdc..308151e51 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -414,7 +414,10 @@ public class LoyaltyCardViewActivity extends AppCompatActivity new BarcodeImageWriterTask( barcodeImage, barcodeIdString != null ? barcodeIdString : cardIdString, - format) + format, + null, + false, + null) .execute(); } @@ -588,7 +591,10 @@ public class LoyaltyCardViewActivity extends AppCompatActivity new BarcodeImageWriterTask( barcodeImage, barcodeIdString != null ? barcodeIdString : cardIdString, - format) + format, + null, + false, + null) .execute(); } }); diff --git a/app/src/main/res/layout/barcode_selector_activity.xml b/app/src/main/res/layout/barcode_selector_activity.xml index 714cba583..5cbb27494 100644 --- a/app/src/main/res/layout/barcode_selector_activity.xml +++ b/app/src/main/res/layout/barcode_selector_activity.xml @@ -55,6 +55,7 @@ android:labelFor="@+id/cardId" android:text="@string/cardId" /> Same as card ID Set barcode value This barcode type can\'t yet be displayed. It may be supported in a newer version of the app. + The value is not valid for the selected barcode type