diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java index 6b807b7ecc..bff027615b 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java @@ -6,6 +6,8 @@ import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; import android.util.SparseBooleanArray; import android.util.TypedValue; import android.view.HapticFeedbackConstants; @@ -88,9 +90,13 @@ public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor i inputHolder.mDivider.setVisibility(View.GONE); LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor); - Bitmap icon = Utils.retrieveCardImage(mContext, loyaltyCard.id, ImageLocationType.icon); + inputHolder.mCardIcon.setContentDescription(loyaltyCard.store); + + // Default header at first, real icon will be retrieved later + Utils.setIconOrTextWithBackground(mContext, loyaltyCard, null, inputHolder.mCardIcon, inputHolder.mCardText); - if (mLoyaltyCardListDisplayOptions.showingNameBelowThumbnail() && icon != null) { + ImageLocationType thumbnailImageLocationType = Utils.getThumbnailImageLocationType(mContext, loyaltyCard.id); + if (mLoyaltyCardListDisplayOptions.showingNameBelowThumbnail() && thumbnailImageLocationType != null) { showDivider = true; inputHolder.setStoreField(loyaltyCard.store); } else { @@ -122,15 +128,28 @@ public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor i inputHolder.setExtraField(inputHolder.mExpiryField, null, null, false); } - inputHolder.mCardIcon.setContentDescription(loyaltyCard.store); - inputHolder.mIconBackgroundColor = Utils.setIconOrTextWithBackground(mContext, loyaltyCard, icon, inputHolder.mCardIcon, inputHolder.mCardText); - inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition())); inputHolder.itemView.setActivated(mSelectedItems.get(inputCursor.getPosition(), false)); applyIconAnimation(inputHolder, inputCursor.getPosition()); applyClickEvents(inputHolder, inputCursor.getPosition()); + if (thumbnailImageLocationType != null) { + new Thread() { + @Override + public void run() { + Bitmap icon = Utils.retrieveCardImage(mContext, loyaltyCard.id, thumbnailImageLocationType); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + public void run() { + inputHolder.mIconBackgroundColor = Utils.setIconOrTextWithBackground(mContext, loyaltyCard, icon, inputHolder.mCardIcon, inputHolder.mCardText); + inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition())); + } + }); + } + }.start(); + } + // Force redraw to fix size not shrinking after data change inputHolder.mRow.requestLayout(); } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index 9792a0bfde..a91f6e6ef8 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -19,6 +19,7 @@ import android.text.style.ForegroundColorSpan; import android.text.util.Linkify; import android.util.Log; +import android.util.Pair; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -85,6 +86,9 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements String barcodeIdString; CatimaBarcode format; + Bitmap iconBitmap; + ImageLocationType iconBitmapLocationType; + Bitmap frontImageBitmap; Bitmap backImageBitmap; @@ -302,10 +306,16 @@ public void onStopTrackingTouch(SeekBar seekBar) { binding.bottomAppBarUpdateBalanceButton.setOnClickListener(view -> showBalanceUpdateDialog()); binding.iconContainer.setOnClickListener(view -> { - if (Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.icon) != null) { + if (iconBitmapLocationType == null) { + Toast.makeText(LoyaltyCardViewActivity.this, R.string.icon_header_click_text, Toast.LENGTH_LONG).show(); + } else if (iconBitmapLocationType == ImageLocationType.icon) { openImageInGallery(ImageType.ICON); + } else if (iconBitmapLocationType == ImageLocationType.front) { + openImageInGallery(ImageType.IMAGE_FRONT); + } else if (iconBitmapLocationType == ImageLocationType.back) { + openImageInGallery(ImageType.IMAGE_BACK); } else { - Toast.makeText(LoyaltyCardViewActivity.this, R.string.icon_header_click_text, Toast.LENGTH_LONG).show(); + throw new IllegalArgumentException("Unknown image type: " + iconBitmapLocationType); } }); binding.iconContainer.setOnLongClickListener(view -> { @@ -674,7 +684,12 @@ protected void onResume() { dialog.show(); }); - int backgroundHeaderColor = Utils.getHeaderColor(this, loyaltyCard); + Pair bitmapImageLocationTypePair = Utils.getThumbnailWithFallback(this, loyaltyCard.id); + iconBitmap = bitmapImageLocationTypePair.first; + iconBitmapLocationType = bitmapImageLocationTypePair.second; + Utils.setIconOrTextWithBackground(this, loyaltyCard, iconBitmap, binding.iconImage, binding.iconText); + + int backgroundHeaderColor = Utils.getHeaderColorFromImage(iconBitmap, Utils.getHeaderColor(this, loyaltyCard)); // Also apply colours to UI elements int darkenedColor = ColorUtils.blendARGB(backgroundHeaderColor, Color.BLACK, 0.1f); @@ -692,9 +707,6 @@ protected void onResume() { editButtonIcon.setTint(Utils.needsDarkForeground(complementaryColor) ? Color.BLACK : Color.WHITE); binding.fabEdit.setImageDrawable(editButtonIcon); - Bitmap icon = Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.icon); - Utils.setIconOrTextWithBackground(this, loyaltyCard, icon, binding.iconImage, binding.iconText); - // If the background is very bright, we should use dark icons backgroundNeedsDarkIcons = Utils.needsDarkForeground(backgroundHeaderColor); diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index eb3ef6bf18..9ac66a0d3b 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -23,6 +23,7 @@ import android.text.Spanned; import android.text.style.ClickableSpan; import android.util.Log; +import android.util.Pair; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; @@ -619,6 +620,16 @@ public static File retrieveCardImageAsFile(Context context, int loyaltyCardId, I return retrieveCardImageAsFile(context, getCardImageFileName(loyaltyCardId, type)); } + static public boolean hasCardImage(Context context, String fileName) { + try { + context.openFileInput(fileName); + } catch (FileNotFoundException e) { + return false; + } + + return true; + } + static public Bitmap retrieveCardImage(Context context, String fileName) { FileInputStream in; try { @@ -630,6 +641,10 @@ static public Bitmap retrieveCardImage(Context context, String fileName) { return BitmapFactory.decodeStream(in); } + static public boolean hasCardImage(Context context, int loyaltyCardId, ImageLocationType type) { + return hasCardImage(context, getCardImageFileName(loyaltyCardId, type)); + } + static public Bitmap retrieveCardImage(Context context, int loyaltyCardId, ImageLocationType type) { return retrieveCardImage(context, getCardImageFileName(loyaltyCardId, type)); } @@ -934,7 +949,7 @@ public static String linkify(final String input) { * @return background colour */ public static int setIconOrTextWithBackground(Context context, LoyaltyCard loyaltyCard, Bitmap icon, ImageView backgroundOrIcon, TextView textWhenNoImage) { - int headerColor = getHeaderColor(context, loyaltyCard); + int headerColor = getHeaderColorFromImage(icon, getHeaderColor(context, loyaltyCard)); backgroundOrIcon.setImageBitmap(icon); backgroundOrIcon.setBackgroundColor(headerColor); @@ -1025,4 +1040,41 @@ public static boolean deviceHasCamera(Context context) { return false; } } + + /** + * Check if the card has an image thumbnail and if yes return what type it is + * + * @param context Android context + * @param loyaltyCardId Id of Loyalty Card + * @return ImageLocationType The location type of the thumbnail if it exists, or null if no thumbnail + */ + public static ImageLocationType getThumbnailImageLocationType(Context context, int loyaltyCardId) { + for (ImageLocationType imageLocationType : new ImageLocationType[]{ImageLocationType.icon, ImageLocationType.front, ImageLocationType.back}) { + if (Utils.hasCardImage(context, loyaltyCardId, imageLocationType)) { + return imageLocationType; + } + } + + return null; + } + + /** + * Retrieve the card thumbnail, falling back to the front and back card image if no thumbnail is set + * + * @param context Android context + * @param loyaltyCardId Id of Loyalty Card + * @return Pair thumbnail and imageLocationType or both null if not set + */ + public static Pair getThumbnailWithFallback(Context context, int loyaltyCardId) { + Bitmap icon; + for (ImageLocationType imageLocationType : new ImageLocationType[]{ ImageLocationType.icon, ImageLocationType.front, ImageLocationType.back }) { + icon = Utils.retrieveCardImage(context, loyaltyCardId, imageLocationType); + + if (icon != null) { + return new Pair<>(icon, imageLocationType); + } + } + + return new Pair<>(null, null); + } } diff --git a/app/src/main/res/layout/loyalty_card_layout.xml b/app/src/main/res/layout/loyalty_card_layout.xml index 45b9e158f0..0f01633008 100644 --- a/app/src/main/res/layout/loyalty_card_layout.xml +++ b/app/src/main/res/layout/loyalty_card_layout.xml @@ -29,7 +29,6 @@ android:layout_height="match_parent" android:contentDescription="@string/thumbnailDescription" android:scaleType="fitCenter" - android:src="@mipmap/ic_launcher" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"