From 4096aaf47ea6f0341274e82fc14c5a3960a83f5f Mon Sep 17 00:00:00 2001 From: GitStart <1501599+gitstart@users.noreply.github.com> Date: Sat, 11 Feb 2023 19:04:14 +0100 Subject: Convert subscriptions screen to cards (#6261) --- .../de/danoeh/antennapod/adapter/CoverLoader.java | 100 +++++---------- .../adapter/SubscriptionsRecyclerAdapter.java | 108 ++++++++-------- .../antennapod/fragment/SubscriptionFragment.java | 3 +- app/src/main/res/layout/subscription_item.xml | 141 ++++++++++++++------- core/src/main/res/drawable/bg_pill_translucent.xml | 5 + core/src/main/res/values/colors.xml | 2 +- core/src/main/res/values/styles.xml | 9 ++ .../danoeh/antennapod/ui/glide/ApGlideModule.java | 2 - .../danoeh/antennapod/ui/glide/PaletteBitmap.java | 20 --- .../antennapod/ui/glide/PaletteBitmapResource.java | 40 ------ .../ui/glide/PaletteBitmapTranscoder.java | 31 ----- 11 files changed, 197 insertions(+), 264 deletions(-) create mode 100644 core/src/main/res/drawable/bg_pill_translucent.xml delete mode 100644 ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmap.java delete mode 100644 ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmapResource.java delete mode 100644 ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmapTranscoder.java diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java index dc82e3adc..1d06bf07c 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java @@ -1,40 +1,28 @@ package de.danoeh.antennapod.adapter; -import android.content.Context; import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.ColorUtils; -import androidx.palette.graphics.Palette; - import android.view.View; import android.widget.ImageView; import android.widget.TextView; - +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.CustomViewTarget; - -import java.lang.ref.WeakReference; - import com.bumptech.glide.request.transition.Transition; - -import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.ui.common.ThemeUtils; -import de.danoeh.antennapod.ui.glide.PaletteBitmap; + +import java.lang.ref.WeakReference; public class CoverLoader { private int resource = 0; private String uri; private String fallbackUri; - private TextView txtvPlaceholder; private ImageView imgvCover; private boolean textAndImageCombined; private MainActivity activity; + private TextView fallbackTitle; public CoverLoader(MainActivity activity) { this.activity = activity; @@ -60,31 +48,30 @@ public class CoverLoader { return this; } - public CoverLoader withPlaceholderView(TextView placeholderView) { - txtvPlaceholder = placeholderView; + public CoverLoader withPlaceholderView(TextView title) { + this.fallbackTitle = title; return this; } /** * Set cover text and if it should be shown even if there is a cover image. - * - * @param placeholderView Cover text. + * @param fallbackTitle Fallback title text * @param textAndImageCombined Show cover text even if there is a cover image? */ @NonNull - public CoverLoader withPlaceholderView(@NonNull TextView placeholderView, boolean textAndImageCombined) { - this.txtvPlaceholder = placeholderView; + public CoverLoader withPlaceholderView(TextView fallbackTitle, boolean textAndImageCombined) { + this.fallbackTitle = fallbackTitle; this.textAndImageCombined = textAndImageCombined; return this; } public void load() { - CoverTarget coverTarget = new CoverTarget(txtvPlaceholder, imgvCover, textAndImageCombined); + CoverTarget coverTarget = new CoverTarget(fallbackTitle, imgvCover, textAndImageCombined); if (resource != 0) { Glide.with(activity).clear(coverTarget); imgvCover.setImageResource(resource); - CoverTarget.setPlaceholderVisibility(txtvPlaceholder, textAndImageCombined, null); + CoverTarget.setTitleVisibility(fallbackTitle, textAndImageCombined); return; } @@ -92,14 +79,14 @@ public class CoverLoader { .fitCenter() .dontAnimate(); - RequestBuilder builder = Glide.with(activity) - .as(PaletteBitmap.class) + RequestBuilder builder = Glide.with(activity) + .as(Drawable.class) .load(uri) .apply(options); - if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) { + if (fallbackUri != null) { builder = builder.error(Glide.with(activity) - .as(PaletteBitmap.class) + .as(Drawable.class) .load(fallbackUri) .apply(options)); } @@ -107,64 +94,41 @@ public class CoverLoader { builder.into(coverTarget); } - static class CoverTarget extends CustomViewTarget { - private final WeakReference placeholder; + static class CoverTarget extends CustomViewTarget { + private final WeakReference fallbackTitle; private final WeakReference cover; - private boolean textAndImageCombined; + private final boolean textAndImageCombined; - public CoverTarget(TextView txtvPlaceholder, ImageView imgvCover, boolean textAndImageCombined) { - super(imgvCover); - if (txtvPlaceholder != null) { - txtvPlaceholder.setVisibility(View.VISIBLE); - } - placeholder = new WeakReference<>(txtvPlaceholder); - cover = new WeakReference<>(imgvCover); + public CoverTarget(TextView fallbackTitle, ImageView coverImage, boolean textAndImageCombined) { + super(coverImage); + this.fallbackTitle = new WeakReference<>(fallbackTitle); + this.cover = new WeakReference<>(coverImage); this.textAndImageCombined = textAndImageCombined; } @Override public void onLoadFailed(Drawable errorDrawable) { - setPlaceholderVisibility(this.placeholder.get(), true, null); + setTitleVisibility(fallbackTitle.get(), true); } @Override - public void onResourceReady(@NonNull PaletteBitmap resource, - @Nullable Transition transition) { + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition transition) { ImageView ivCover = cover.get(); - ivCover.setImageBitmap(resource.bitmap); - setPlaceholderVisibility(placeholder.get(), textAndImageCombined, resource.palette); + ivCover.setImageDrawable(resource); + setTitleVisibility(fallbackTitle.get(), textAndImageCombined); } @Override protected void onResourceCleared(@Nullable Drawable placeholder) { ImageView ivCover = cover.get(); ivCover.setImageDrawable(placeholder); - setPlaceholderVisibility(this.placeholder.get(), textAndImageCombined, null); + setTitleVisibility(fallbackTitle.get(), textAndImageCombined); } - static void setPlaceholderVisibility(TextView placeholder, boolean textAndImageCombined, Palette palette) { - boolean showTitle = UserPreferences.shouldShowSubscriptionTitle(); - if (placeholder != null) { - if (textAndImageCombined || showTitle) { - final Context context = placeholder.getContext(); - placeholder.setVisibility(View.VISIBLE); - int bgColor = ContextCompat.getColor(context, R.color.feed_text_bg); - if (palette == null || !showTitle) { - placeholder.setBackgroundColor(bgColor); - placeholder.setTextColor(ThemeUtils.getColorFromAttr(placeholder.getContext(), - android.R.attr.textColorPrimary)); - return; - } - int dominantColor = palette.getDominantColor(bgColor); - int textColor = ContextCompat.getColor(context, R.color.white); - if (ColorUtils.calculateLuminance(dominantColor) > 0.5) { - textColor = ContextCompat.getColor(context, R.color.black); - } - placeholder.setTextColor(textColor); - placeholder.setBackgroundColor(dominantColor); - } else { - placeholder.setVisibility(View.INVISIBLE); - } + static void setTitleVisibility(TextView fallbackTitle, boolean textAndImageCombined) { + if (fallbackTitle != null) { + fallbackTitle.setVisibility(textAndImageCombined ? View.VISIBLE : View.GONE); } } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java index 7be417984..bfa37b600 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java @@ -5,7 +5,6 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; -import android.text.TextUtils; import android.view.ContextMenu; import android.view.InputDevice; import android.view.LayoutInflater; @@ -17,20 +16,19 @@ import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.RelativeLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.content.ContextCompat; +import androidx.cardview.widget.CardView; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.elevation.SurfaceColors; import java.lang.ref.WeakReference; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; @@ -39,7 +37,6 @@ import de.danoeh.antennapod.core.storage.NavDrawerData; import de.danoeh.antennapod.fragment.FeedItemlistFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment; import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.ui.common.TriangleLabelView; /** * Adapter for subscriptions @@ -52,6 +49,7 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter listItems; private NavDrawerData.DrawerItem selectedItem = null; int longPressedPosition = 0; // used to init actionMode + private int columnCount = 3; public SubscriptionsRecyclerAdapter(MainActivity mainActivity) { super(mainActivity); @@ -60,6 +58,10 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter setSelected(holder.getBindingAdapterPosition(), isChecked)); - holder.imageView.setAlpha(0.6f); + holder.coverImage.setAlpha(0.6f); holder.count.setVisibility(View.GONE); } else { holder.selectView.setVisibility(View.GONE); - holder.imageView.setAlpha(1.0f); + holder.coverImage.setAlpha(1.0f); } holder.itemView.setOnLongClickListener(v -> { @@ -224,55 +209,74 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter 0) { - count.setPrimaryText(NumberFormat.getInstance().format(drawerItem.getCounter())); + count.setText(NumberFormat.getInstance().format(drawerItem.getCounter())); count.setVisibility(View.VISIBLE); } else { count.setVisibility(View.GONE); } + CoverLoader coverLoader = new CoverLoader(mainActivityRef.get()); + boolean textAndImageCombined; if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed; - boolean textAndImageCombind = feed.isLocalFeed() - && feed.getImageUrl() != null && feed.getImageUrl().startsWith(Feed.PREFIX_GENERATIVE_COVER); - new CoverLoader(mainActivityRef.get()) - .withUri(feed.getImageUrl()) - .withPlaceholderView(feedTitle, textAndImageCombind) - .withCoverView(imageView) - .load(); + textAndImageCombined = feed.isLocalFeed() && feed.getImageUrl() != null + && feed.getImageUrl().startsWith(Feed.PREFIX_GENERATIVE_COVER); + coverLoader.withUri(feed.getImageUrl()); } else { - new CoverLoader(mainActivityRef.get()) - .withResource(R.drawable.ic_tag) - .withPlaceholderView(feedTitle, true) - .withCoverView(imageView) - .load(); + textAndImageCombined = true; + coverLoader.withResource(R.drawable.ic_tag); + } + if (UserPreferences.shouldShowSubscriptionTitle()) { + // No need for fallback title when already showing title + fallbackTitle.setVisibility(View.GONE); + } else { + coverLoader.withPlaceholderView(fallbackTitle, textAndImageCombined); + } + coverLoader.withCoverView(coverImage); + coverLoader.load(); + + float density = mainActivityRef.get().getResources().getDisplayMetrics().density; + card.setCardBackgroundColor(SurfaceColors.getColorForElevation(mainActivityRef.get(), 1 * density)); + + int textPadding = columnCount <= 3 ? 16 : 8; + title.setPadding(textPadding, textPadding, textPadding, textPadding); + fallbackTitle.setPadding(textPadding, textPadding, textPadding, textPadding); + + int textSize = 14; + if (columnCount == 3) { + textSize = 15; + } else if (columnCount == 2) { + textSize = 16; } + title.setTextSize(textSize); + fallbackTitle.setTextSize(textSize); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java index 15886cf9a..cc13f4ce3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -142,7 +142,6 @@ public class SubscriptionFragment extends Fragment } subscriptionRecycler = root.findViewById(R.id.subscriptions_grid); - setColumnNumber(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns())); subscriptionRecycler.addItemDecoration(new SubscriptionsRecyclerAdapter.GridDividerItemDecorator()); registerForContextMenu(subscriptionRecycler); subscriptionRecycler.addOnScrollListener(new LiftOnScrollListener(root.findViewById(R.id.appbar))); @@ -153,6 +152,7 @@ public class SubscriptionFragment extends Fragment MenuItemUtils.setOnClickListeners(menu, SubscriptionFragment.this::onContextItemSelected); } }; + setColumnNumber(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns())); subscriptionAdapter.setOnSelectModeListener(this); subscriptionRecycler.setAdapter(subscriptionAdapter); setupEmptyView(); @@ -251,6 +251,7 @@ public class SubscriptionFragment extends Fragment private void setColumnNumber(int columns) { GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), columns, RecyclerView.VERTICAL, false); + subscriptionAdapter.setColumnCount(columns); subscriptionRecycler.setLayoutManager(gridLayoutManager); prefs.edit().putInt(PREF_NUM_COLUMNS, columns).apply(); refreshToolbarState(); diff --git a/app/src/main/res/layout/subscription_item.xml b/app/src/main/res/layout/subscription_item.xml index 50cb9745f..0b689fa28 100644 --- a/app/src/main/res/layout/subscription_item.xml +++ b/app/src/main/res/layout/subscription_item.xml @@ -1,64 +1,107 @@ - + android:padding="4dp"> - + android:clickable="false" + android:foreground="?attr/selectableItemBackground" + app:cardCornerRadius="12dp" + app:cardElevation="0dp"> - - - - - - - + + + + + + + + + + + + + + + + + + + + + android:clickable="false"> + + + + - + - + diff --git a/core/src/main/res/drawable/bg_pill_translucent.xml b/core/src/main/res/drawable/bg_pill_translucent.xml new file mode 100644 index 000000000..b25a9ac82 --- /dev/null +++ b/core/src/main/res/drawable/bg_pill_translucent.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml index 19a7c0fde..553da121a 100644 --- a/core/src/main/res/values/colors.xml +++ b/core/src/main/res/values/colors.xml @@ -9,7 +9,7 @@ #000000 #80000000 #50000000 - #ccbfbfbf + #55333333 #FFFFFF diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml index d436abaeb..09e3cadf8 100644 --- a/core/src/main/res/values/styles.xml +++ b/core/src/main/res/values/styles.xml @@ -284,4 +284,13 @@ true + + diff --git a/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/ApGlideModule.java b/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/ApGlideModule.java index d233fba14..132ed4901 100644 --- a/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/ApGlideModule.java +++ b/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/ApGlideModule.java @@ -2,7 +2,6 @@ package de.danoeh.antennapod.ui.glide; import android.annotation.SuppressLint; import android.content.Context; -import android.graphics.Bitmap; import android.util.Log; import androidx.annotation.NonNull; @@ -51,6 +50,5 @@ public class ApGlideModule extends AppGlideModule { registry.append(String.class, InputStream.class, new NoHttpStringLoader.StreamFactory()); registry.append(EmbeddedChapterImage.class, ByteBuffer.class, new ChapterImageModelLoader.Factory()); - registry.register(Bitmap.class, PaletteBitmap.class, new PaletteBitmapTranscoder()); } } diff --git a/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmap.java b/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmap.java deleted file mode 100644 index a3b590ba2..000000000 --- a/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmap.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Source: https://github.com/bumptech/glide/wiki/Custom-targets#palette-example - */ - -package de.danoeh.antennapod.ui.glide; - -import android.graphics.Bitmap; - -import androidx.annotation.NonNull; -import androidx.palette.graphics.Palette; - -public class PaletteBitmap { - public final Palette palette; - public final Bitmap bitmap; - - public PaletteBitmap(@NonNull Bitmap bitmap, Palette palette) { - this.bitmap = bitmap; - this.palette = palette; - } -} \ No newline at end of file diff --git a/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmapResource.java b/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmapResource.java deleted file mode 100644 index 2fd18a0cb..000000000 --- a/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmapResource.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Source: https://github.com/bumptech/glide/wiki/Custom-targets#palette-example - */ - -package de.danoeh.antennapod.ui.glide; - -import androidx.annotation.NonNull; - -import com.bumptech.glide.load.engine.Resource; -import com.bumptech.glide.util.Util; - -public class PaletteBitmapResource implements Resource { - private final PaletteBitmap paletteBitmap; - - public PaletteBitmapResource(@NonNull PaletteBitmap paletteBitmap) { - this.paletteBitmap = paletteBitmap; - } - - @NonNull - @Override - public Class getResourceClass() { - return PaletteBitmap.class; - } - - @NonNull - @Override - public PaletteBitmap get() { - return paletteBitmap; - } - - @Override - public int getSize() { - return Util.getBitmapByteSize(paletteBitmap.bitmap); - } - - @Override - public void recycle() { - paletteBitmap.bitmap.recycle(); - } -} \ No newline at end of file diff --git a/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmapTranscoder.java b/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmapTranscoder.java deleted file mode 100644 index bcb5f590a..000000000 --- a/ui/glide/src/main/java/de/danoeh/antennapod/ui/glide/PaletteBitmapTranscoder.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Source: https://github.com/bumptech/glide/wiki/Custom-targets#palette-example - */ - -package de.danoeh.antennapod.ui.glide; - -import android.graphics.Bitmap; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.palette.graphics.Palette; - -import com.bumptech.glide.load.Options; -import com.bumptech.glide.load.engine.Resource; -import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; -import de.danoeh.antennapod.storage.preferences.UserPreferences; - -public class PaletteBitmapTranscoder implements ResourceTranscoder { - - @Nullable - @Override - public Resource transcode(@NonNull Resource toTranscode, @NonNull Options options) { - Bitmap bitmap = toTranscode.get(); - Palette palette = null; - if (UserPreferences.shouldShowSubscriptionTitle()) { - palette = new Palette.Builder(bitmap).generate(); - } - PaletteBitmap result = new PaletteBitmap(bitmap, palette); - return new PaletteBitmapResource(result); - } -} -- cgit v1.2.3