From 0431803336f9ac41d3f30d24b36162c326a42a40 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Sun, 16 Jun 2024 11:25:20 +0200 Subject: [PATCH] Load thumbnails on main screen asynchronously Decoding a bitmap can be slow. On slow devices, loading all the images as soon as the card should be shown can lead to UI freezes. By loading the thumbnail asynchronously, scrolling quickly remains smooth even on slow devices. --- .../card_locker/LoyaltyCardCursorAdapter.java | 31 ++++++++++++++----- .../main/res/layout/loyalty_card_layout.xml | 1 - 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java index 6b807b7ecc..d48ea96d56 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,29 @@ 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); - if (mLoyaltyCardListDisplayOptions.showingNameBelowThumbnail() && icon != null) { + inputHolder.mCardIcon.setContentDescription(loyaltyCard.store); + + // Default header at first, real icon will be retrieved asynchronously if it exists to ensure + // smooth scrolling even on slower devices + Utils.setIconOrTextWithBackground(mContext, loyaltyCard, null, inputHolder.mCardIcon, inputHolder.mCardText); + inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition())); + boolean hasIcon = Utils.retrieveCardImageAsFile(mContext, loyaltyCard.id, ImageLocationType.icon).exists(); + if (hasIcon) { + new Thread() { + @Override + public void run() { + Bitmap icon = Utils.retrieveCardImage(mContext, loyaltyCard.id, ImageLocationType.icon); + + new Handler(Looper.getMainLooper()).post(() -> { + inputHolder.mIconBackgroundColor = Utils.setIconOrTextWithBackground(mContext, loyaltyCard, icon, inputHolder.mCardIcon, inputHolder.mCardText); + inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputHolder.getAdapterPosition())); + }); + } + }.start(); + } + + if (mLoyaltyCardListDisplayOptions.showingNameBelowThumbnail() && hasIcon) { showDivider = true; inputHolder.setStoreField(loyaltyCard.store); } else { @@ -122,11 +144,6 @@ 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()); 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"