diff --git a/app/src/main/java/protect/card_locker/BarcodeImageWriterTask.java b/app/src/main/java/protect/card_locker/BarcodeImageWriterTask.java new file mode 100644 index 000000000..d71bee2ac --- /dev/null +++ b/app/src/main/java/protect/card_locker/BarcodeImageWriterTask.java @@ -0,0 +1,108 @@ +package protect.card_locker; + +import android.graphics.Bitmap; +import android.os.AsyncTask; +import android.util.Log; +import android.widget.ImageView; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; + +import java.lang.ref.WeakReference; + +/** + * This task will generate a barcode and load it into an ImageView. + * Only a weak reference of the ImageView is kept, so this class will not + * prevent the ImageView from being garbage collected. + */ +class BarcodeImageWriterTask extends AsyncTask +{ + private static final String TAG = "LoyaltyCardLocker"; + + private final WeakReference imageViewReference; + private final String cardId; + private final BarcodeFormat format; + private final int imageHeight; + private final int imageWidth; + + public BarcodeImageWriterTask(ImageView imageView, String cardIdString, + BarcodeFormat barcodeFormat) + { + // Use a WeakReference to ensure the ImageView can be garbage collected + imageViewReference = new WeakReference<>(imageView); + + cardId = cardIdString; + format = barcodeFormat; + imageHeight = imageView.getHeight(); + imageWidth = imageView.getWidth(); + } + + public Bitmap doInBackground(Void... params) + { + MultiFormatWriter writer = new MultiFormatWriter(); + BitMatrix bitMatrix; + try + { + bitMatrix = writer.encode(cardId, format, imageWidth, imageHeight, null); + + final int WHITE = 0xFFFFFFFF; + final int BLACK = 0xFF000000; + + int bitMatrixWidth = bitMatrix.getWidth(); + int bitMatrixHeight = bitMatrix.getHeight(); + + int[] pixels = new int[bitMatrixWidth * bitMatrixHeight]; + + for (int y = 0; y < bitMatrixHeight; y++) + { + int offset = y * bitMatrixWidth; + for (int x = 0; x < bitMatrixWidth; x++) + { + int color = bitMatrix.get(x, y) ? BLACK : WHITE; + pixels[offset + x] = color; + } + } + Bitmap bitmap = Bitmap.createBitmap(bitMatrixWidth, bitMatrixHeight, + Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, bitMatrixWidth, 0, 0, bitMatrixWidth, bitMatrixHeight); + + // Determine if the image needs to be scaled. + // This is necessary because the datamatrix barcode generator + // ignores the requested size and returns the smallest image necessary + // to represent the barcode. If we let the ImageView scale the image + // it will use bi-linear filtering, which results in a blurry barcode. + // To avoid this, if scaling is needed do so without filtering. + + int heightScale = imageHeight / bitMatrixHeight; + int widthScale = imageWidth / bitMatrixHeight; + int scalingFactor = Math.min(heightScale, widthScale); + + if(scalingFactor > 1) + { + bitmap = Bitmap.createScaledBitmap(bitmap, bitMatrixWidth * scalingFactor, bitMatrixHeight * scalingFactor, false); + } + + return bitmap; + } + catch (WriterException | IllegalArgumentException e) + { + Log.e(TAG, "Failed to generate barcode", e); + } + + return null; + } + + protected void onPostExecute(Bitmap result) + { + ImageView imageView = imageViewReference.get(); + if(imageView == null) + { + // The ImageView no longer exists, nothing to do + return; + } + + imageView.setImageBitmap(result); + } +} \ No newline at end of file diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index 015a76d7d..a11840d91 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -2,7 +2,6 @@ package protect.card_locker; import android.content.Intent; -import android.graphics.Bitmap; import android.os.Bundle; import android.support.design.widget.Snackbar; import android.support.v7.app.ActionBar; @@ -12,14 +11,12 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewTreeObserver; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import com.google.zxing.BarcodeFormat; -import com.google.zxing.MultiFormatWriter; -import com.google.zxing.WriterException; -import com.google.zxing.common.BitMatrix; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; @@ -149,52 +146,34 @@ public class LoyaltyCardViewActivity extends AppCompatActivity if(cardIdField.getText().length() > 0 && barcodeTypeField.getText().length() > 0) { - MultiFormatWriter writer = new MultiFormatWriter(); - BitMatrix result; - try + String formatString = barcodeTypeField.getText().toString(); + final BarcodeFormat format = BarcodeFormat.valueOf(formatString); + final String cardIdString = cardIdField.getText().toString(); + + if(barcodeImage.getHeight() == 0) { - String formatString = barcodeTypeField.getText().toString(); - BarcodeFormat format = BarcodeFormat.valueOf(formatString); - if(format == null) - { - throw new IllegalArgumentException("Unrecognized barcode format: " + formatString); - } - - int generateWidth = 100; - int generateHeight = 100; - - String cardIdString = cardIdField.getText().toString(); - - Log.i(TAG, "Card: " + cardIdString); - - result = writer.encode(cardIdString, format, generateWidth, generateHeight, null); - - final int WHITE = 0xFFFFFFFF; - final int BLACK = 0xFF000000; - - int width = result.getWidth(); - int height = result.getHeight(); - int[] pixels = new int[width * height]; - for (int y = 0; y < height; y++) - { - int offset = y * width; - for (int x = 0; x < width; x++) + Log.d(TAG, "ImageView size is not known known at start, waiting for load"); + // The size of the ImageView is not yet available as it has not + // yet been drawn. Wait for it to be drawn so the size is available. + barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { - pixels[offset + x] = result.get(x, y) ? BLACK : WHITE; - } - } - Bitmap bitmap = Bitmap.createBitmap(width, height, - Bitmap.Config.ARGB_8888); - bitmap.setPixels(pixels, 0, width, 0, 0, width, height); - barcodeImage.setImageBitmap(bitmap); - - barcodeIdLayout.setVisibility(View.VISIBLE); - barcodeImageLayout.setVisibility(View.VISIBLE); + @Override + public void onGlobalLayout() + { + Log.d(TAG, "ImageView size now known"); + new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute(); + } + }); } - catch (WriterException | IllegalArgumentException e) + else { - Log.e(TAG, "Failed to generate barcode", e); + Log.d(TAG, "ImageView size known known, creating barcode"); + new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute(); } + + barcodeIdLayout.setVisibility(View.VISIBLE); + barcodeImageLayout.setVisibility(View.VISIBLE); } View.OnClickListener captureCallback = new View.OnClickListener() @@ -332,4 +311,4 @@ public class LoyaltyCardViewActivity extends AppCompatActivity } } } -} +} \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index fab4bdcdd..56b5d76d1 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -5,10 +5,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="@dimen/activity_vertical_margin" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="protect.card_locker.MainActivity" tools:showIn="@layout/main_activity"> diff --git a/app/src/main/res/layout/loyalty_card_layout.xml b/app/src/main/res/layout/loyalty_card_layout.xml index 1232c0517..cba2d38d9 100644 --- a/app/src/main/res/layout/loyalty_card_layout.xml +++ b/app/src/main/res/layout/loyalty_card_layout.xml @@ -1,11 +1,11 @@ @@ -18,7 +18,7 @@