refactor: Extract PreviewCard display code to PreviewCardView
(#184)
This commit is contained in:
parent
3157f8d946
commit
d39eb3b642
@ -773,7 +773,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="778"
|
||||
line="780"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
@ -938,7 +938,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/components/announcements/AnnouncementAdapter.kt"
|
||||
line="133"
|
||||
line="162"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
@ -2093,7 +2093,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="766"
|
||||
line="768"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -2104,7 +2104,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="800"
|
||||
line="802"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -3787,7 +3787,7 @@
|
||||
errorLine2=" ~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/fragment/ViewImageFragment.kt"
|
||||
line="232"
|
||||
line="217"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
@ -3798,7 +3798,7 @@
|
||||
errorLine2=" ~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/fragment/ViewImageFragment.kt"
|
||||
line="320"
|
||||
line="305"
|
||||
column="34"/>
|
||||
</issue>
|
||||
|
||||
@ -3809,7 +3809,7 @@
|
||||
errorLine2=" ~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/fragment/ViewImageFragment.kt"
|
||||
line="333"
|
||||
line="318"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -3820,7 +3820,7 @@
|
||||
errorLine2=" ~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/fragment/ViewImageFragment.kt"
|
||||
line="340"
|
||||
line="325"
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
@ -3886,7 +3886,7 @@
|
||||
errorLine2=" ~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/fragment/ViewVideoFragment.kt"
|
||||
line="145"
|
||||
line="140"
|
||||
column="32"/>
|
||||
</issue>
|
||||
|
||||
@ -3897,7 +3897,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/fragment/ViewVideoFragment.kt"
|
||||
line="226"
|
||||
line="221"
|
||||
column="21"/>
|
||||
</issue>
|
||||
|
||||
@ -4260,17 +4260,6 @@
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
errorLine1=" <TextView"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_announcement.xml"
|
||||
line="7"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
@ -4766,39 +4755,6 @@
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
errorLine1=" <TextView"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status.xml"
|
||||
line="192"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
errorLine1=" <TextView"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status.xml"
|
||||
line="202"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
errorLine1=" <TextView"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status.xml"
|
||||
line="212"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
@ -4806,7 +4762,7 @@
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status.xml"
|
||||
line="287"
|
||||
line="233"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
@ -4817,7 +4773,7 @@
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status.xml"
|
||||
line="315"
|
||||
line="261"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
@ -4828,7 +4784,7 @@
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status.xml"
|
||||
line="343"
|
||||
line="289"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
@ -4854,39 +4810,6 @@
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
errorLine1=" <TextView"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status_detailed.xml"
|
||||
line="173"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
errorLine1=" <TextView"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status_detailed.xml"
|
||||
line="184"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
errorLine1=" <TextView"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status_detailed.xml"
|
||||
line="195"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SelectableText"
|
||||
message="Consider making the text value selectable by specifying `android:textIsSelectable="true"`"
|
||||
@ -4894,7 +4817,7 @@
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status_detailed.xml"
|
||||
line="234"
|
||||
line="180"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
@ -4905,7 +4828,7 @@
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status_detailed.xml"
|
||||
line="263"
|
||||
line="209"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
@ -4916,7 +4839,7 @@
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status_detailed.xml"
|
||||
line="277"
|
||||
line="223"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
@ -5202,7 +5125,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status.xml"
|
||||
line="277"
|
||||
line="223"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
@ -5316,17 +5239,6 @@
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SetTextI18n"
|
||||
message="Do not concatenate text displayed with `setText`. Use resource string with placeholders."
|
||||
errorLine1=" this.text = "${reaction.name} ${reaction.count}""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/components/announcements/AnnouncementAdapter.kt"
|
||||
line="91"
|
||||
column="37"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardcodedText"
|
||||
message="Hardcoded string "Filter: MyFilter", should use `@string` resource"
|
||||
|
@ -27,12 +27,8 @@ import androidx.core.text.HtmlCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.shape.CornerFamily;
|
||||
import com.google.android.material.shape.ShapeAppearanceModel;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Collections;
|
||||
@ -50,6 +46,7 @@ import app.pachli.entity.Filter;
|
||||
import app.pachli.entity.FilterResult;
|
||||
import app.pachli.entity.HashTag;
|
||||
import app.pachli.entity.Poll;
|
||||
import app.pachli.entity.PreviewCardKind;
|
||||
import app.pachli.entity.Status;
|
||||
import app.pachli.interfaces.StatusActionListener;
|
||||
import app.pachli.util.AbsoluteTimeFormatter;
|
||||
@ -66,6 +63,7 @@ import app.pachli.util.TouchDelegateHelper;
|
||||
import app.pachli.view.MediaPreviewImageView;
|
||||
import app.pachli.view.MediaPreviewLayout;
|
||||
import app.pachli.view.PollView;
|
||||
import app.pachli.view.PreviewCardView;
|
||||
import app.pachli.viewdata.PollViewData;
|
||||
import app.pachli.viewdata.StatusViewData;
|
||||
import at.connyduck.sparkbutton.SparkButton;
|
||||
@ -105,12 +103,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@NonNull
|
||||
private final PollView pollView;
|
||||
private final LinearLayout cardView;
|
||||
private final LinearLayout cardInfo;
|
||||
private final ShapeableImageView cardImage;
|
||||
private final TextView cardTitle;
|
||||
private final TextView cardDescription;
|
||||
private final TextView cardUrl;
|
||||
private final PreviewCardView cardView;
|
||||
protected final LinearLayout filteredPlaceholder;
|
||||
protected final TextView filteredPlaceholderLabel;
|
||||
protected final Button filteredPlaceholderShowButton;
|
||||
@ -160,11 +153,6 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
pollView = itemView.findViewById(R.id.status_poll);
|
||||
|
||||
cardView = itemView.findViewById(R.id.status_card_view);
|
||||
cardInfo = itemView.findViewById(R.id.card_info);
|
||||
cardImage = itemView.findViewById(R.id.card_image);
|
||||
cardTitle = itemView.findViewById(R.id.card_title);
|
||||
cardDescription = itemView.findViewById(R.id.card_description);
|
||||
cardUrl = itemView.findViewById(R.id.card_link);
|
||||
|
||||
filteredPlaceholder = itemView.findViewById(R.id.status_filtered_placeholder);
|
||||
filteredPlaceholderLabel = itemView.findViewById(R.id.status_filter_label);
|
||||
@ -990,9 +978,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
@NonNull final StatusDisplayOptions statusDisplayOptions,
|
||||
@NonNull final StatusActionListener listener
|
||||
) {
|
||||
if (cardView == null) {
|
||||
return;
|
||||
}
|
||||
if (cardView == null) return;
|
||||
|
||||
final Status actionable = status.getActionable();
|
||||
final Card card = actionable.getCard();
|
||||
@ -1006,110 +992,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
(!status.isCollapsible() || !status.isCollapsed())) {
|
||||
|
||||
cardView.setVisibility(View.VISIBLE);
|
||||
cardTitle.setText(card.getTitle());
|
||||
if (TextUtils.isEmpty(card.getDescription()) && TextUtils.isEmpty(card.getAuthorName())) {
|
||||
cardDescription.setVisibility(View.GONE);
|
||||
} else {
|
||||
cardDescription.setVisibility(View.VISIBLE);
|
||||
if (TextUtils.isEmpty(card.getDescription())) {
|
||||
cardDescription.setText(card.getAuthorName());
|
||||
PreviewCardView.OnClickListener cardListener = (PreviewCardView.Target target) -> {
|
||||
if (card.getKind().equals(PreviewCardKind.PHOTO) && !TextUtils.isEmpty(card.getEmbedUrl())) {
|
||||
cardView.getContext().startActivity(ViewMediaActivity.newSingleImageIntent(cardView.getContext(), card.getEmbedUrl()));
|
||||
} else {
|
||||
cardDescription.setText(card.getDescription());
|
||||
listener.onViewUrl(card.getUrl());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cardUrl.setText(card.getUrl());
|
||||
|
||||
// Statuses from other activitypub sources can be marked sensitive even if there's no media,
|
||||
// so let's blur the preview in that case
|
||||
// If media previews are disabled, show placeholder for cards as well
|
||||
if (statusDisplayOptions.mediaPreviewEnabled() && !actionable.getSensitive() && !TextUtils.isEmpty(card.getImage())) {
|
||||
|
||||
int radius = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_radius);
|
||||
ShapeAppearanceModel.Builder cardImageShape = ShapeAppearanceModel.builder();
|
||||
|
||||
if (card.getWidth() > card.getHeight()) {
|
||||
cardView.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
cardImage.getLayoutParams().height = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_vertical_height);
|
||||
cardImage.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
cardImageShape.setTopLeftCorner(CornerFamily.ROUNDED, radius);
|
||||
cardImageShape.setTopRightCorner(CornerFamily.ROUNDED, radius);
|
||||
} else {
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImage.getLayoutParams().width = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_horizontal_width);
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImageShape.setTopLeftCorner(CornerFamily.ROUNDED, radius);
|
||||
cardImageShape.setBottomLeftCorner(CornerFamily.ROUNDED, radius);
|
||||
}
|
||||
|
||||
cardImage.setShapeAppearanceModel(cardImageShape.build());
|
||||
|
||||
cardImage.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
|
||||
RequestBuilder<Drawable> builder = Glide.with(cardImage.getContext())
|
||||
.load(card.getImage())
|
||||
.dontTransform();
|
||||
if (statusDisplayOptions.useBlurhash() && !TextUtils.isEmpty(card.getBlurhash())) {
|
||||
builder = builder.placeholder(decodeBlurHash(card.getBlurhash()));
|
||||
}
|
||||
builder.into(cardImage);
|
||||
} else if (statusDisplayOptions.useBlurhash() && !TextUtils.isEmpty(card.getBlurhash())) {
|
||||
int radius = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_radius);
|
||||
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImage.getLayoutParams().width = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_horizontal_width);
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
ShapeAppearanceModel cardImageShape = ShapeAppearanceModel.builder()
|
||||
.setTopLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
.setBottomLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
.build();
|
||||
cardImage.setShapeAppearanceModel(cardImageShape);
|
||||
|
||||
cardImage.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
|
||||
Glide.with(cardImage.getContext())
|
||||
.load(decodeBlurHash(card.getBlurhash()))
|
||||
.dontTransform()
|
||||
.into(cardImage);
|
||||
} else {
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImage.getLayoutParams().width = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_horizontal_width);
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
cardImage.setShapeAppearanceModel(new ShapeAppearanceModel());
|
||||
|
||||
cardImage.setScaleType(ImageView.ScaleType.CENTER);
|
||||
|
||||
Glide.with(cardImage.getContext())
|
||||
.load(R.drawable.card_image_placeholder)
|
||||
.into(cardImage);
|
||||
}
|
||||
|
||||
View.OnClickListener visitLink = v -> listener.onViewUrl(card.getUrl());
|
||||
|
||||
cardView.setOnClickListener(visitLink);
|
||||
// View embedded photos in our image viewer instead of opening the browser
|
||||
cardImage.setOnClickListener(card.getType().equals(Card.TYPE_PHOTO) && !TextUtils.isEmpty(card.getEmbedUrl()) ?
|
||||
v -> cardView.getContext().startActivity(ViewMediaActivity.newSingleImageIntent(cardView.getContext(), card.getEmbedUrl())) :
|
||||
visitLink);
|
||||
|
||||
cardView.setClipToOutline(true);
|
||||
cardView.bind(card, actionable.getSensitive(), statusDisplayOptions, cardListener);
|
||||
} else {
|
||||
cardView.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -17,119 +17,18 @@
|
||||
|
||||
package app.pachli.components.trending
|
||||
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.ImageView.ScaleType
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import app.pachli.R
|
||||
import app.pachli.databinding.ItemTrendingLinkBinding
|
||||
import app.pachli.entity.TrendsLink
|
||||
import app.pachli.util.StatusDisplayOptions
|
||||
import app.pachli.util.decodeBlurHash
|
||||
import app.pachli.util.hide
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
|
||||
class TrendingLinkViewHolder(
|
||||
private val binding: ItemTrendingLinkBinding,
|
||||
private val onClick: (String) -> Unit,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
private var link: TrendsLink? = null
|
||||
|
||||
private val radius =
|
||||
binding.cardImage.resources.getDimensionPixelSize(R.dimen.card_radius).toFloat()
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener { link?.let { onClick(it.url) } }
|
||||
}
|
||||
|
||||
fun bind(link: TrendsLink, statusDisplayOptions: StatusDisplayOptions) {
|
||||
this.link = link
|
||||
|
||||
// TODO: This is very similar to the code in StatusBaseViewHolder.setupCard().
|
||||
// Can a "PreviewCardView" type be created to encapsulate all this?
|
||||
binding.cardTitle.text = link.title
|
||||
|
||||
when {
|
||||
link.description.isNotBlank() -> link.description
|
||||
link.authorName.isNotBlank() -> link.authorName
|
||||
else -> null
|
||||
}?.let { binding.cardDescription.text = it } ?: binding.cardDescription.hide()
|
||||
|
||||
binding.cardLink.text = link.url
|
||||
|
||||
val cardImageShape = ShapeAppearanceModel.Builder()
|
||||
|
||||
if (statusDisplayOptions.mediaPreviewEnabled && !link.image.isNullOrBlank()) {
|
||||
if (link.width > link.height) {
|
||||
binding.statusCardView.orientation = LinearLayout.VERTICAL
|
||||
binding.cardImage.layoutParams.height =
|
||||
binding.cardImage.resources.getDimensionPixelSize(R.dimen.card_image_vertical_height)
|
||||
binding.cardImage.layoutParams.width = MATCH_PARENT
|
||||
binding.cardInfo.layoutParams.height = MATCH_PARENT
|
||||
binding.cardInfo.layoutParams.width = WRAP_CONTENT
|
||||
cardImageShape.setTopLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
cardImageShape.setTopRightCorner(CornerFamily.ROUNDED, radius)
|
||||
} else {
|
||||
binding.statusCardView.orientation = LinearLayout.HORIZONTAL
|
||||
binding.cardImage.layoutParams.height = MATCH_PARENT
|
||||
binding.cardImage.layoutParams.width =
|
||||
binding.cardImage.resources.getDimensionPixelSize(R.dimen.card_image_horizontal_width)
|
||||
binding.cardInfo.layoutParams.height = WRAP_CONTENT
|
||||
binding.cardInfo.layoutParams.width = MATCH_PARENT
|
||||
cardImageShape.setTopLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
cardImageShape.setBottomLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
}
|
||||
|
||||
binding.cardImage.shapeAppearanceModel = cardImageShape.build()
|
||||
binding.cardImage.scaleType = ScaleType.CENTER_CROP
|
||||
|
||||
val builder = Glide.with(binding.cardImage.context)
|
||||
.load(link.image)
|
||||
.dontTransform()
|
||||
if (statusDisplayOptions.useBlurhash && !link.blurhash.isNullOrBlank()) {
|
||||
builder
|
||||
.placeholder(decodeBlurHash(binding.cardImage.context, link.blurhash))
|
||||
.into(binding.cardImage)
|
||||
} else {
|
||||
builder.into(binding.cardImage)
|
||||
}
|
||||
} else if (statusDisplayOptions.useBlurhash && !link.blurhash.isNullOrBlank()) {
|
||||
binding.statusCardView.orientation = LinearLayout.HORIZONTAL
|
||||
binding.cardImage.layoutParams.height = MATCH_PARENT
|
||||
binding.cardImage.layoutParams.width =
|
||||
binding.cardImage.resources.getDimensionPixelSize(R.dimen.card_image_horizontal_width)
|
||||
binding.cardInfo.layoutParams.height = WRAP_CONTENT
|
||||
binding.cardInfo.layoutParams.width = MATCH_PARENT
|
||||
|
||||
cardImageShape
|
||||
.setTopLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
.setBottomLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
binding.cardImage.shapeAppearanceModel = cardImageShape.build()
|
||||
binding.cardImage.scaleType = ScaleType.CENTER_CROP
|
||||
|
||||
Glide.with(binding.cardImage.context)
|
||||
.load(decodeBlurHash(binding.cardImage.context, link.blurhash))
|
||||
.dontTransform()
|
||||
.into(binding.cardImage)
|
||||
} else {
|
||||
binding.statusCardView.orientation = LinearLayout.HORIZONTAL
|
||||
binding.cardImage.layoutParams.height = MATCH_PARENT
|
||||
binding.cardImage.layoutParams.width =
|
||||
binding.cardImage.resources.getDimensionPixelSize(R.dimen.card_image_horizontal_width)
|
||||
binding.cardInfo.layoutParams.height = WRAP_CONTENT
|
||||
binding.cardInfo.layoutParams.width = MATCH_PARENT
|
||||
|
||||
binding.cardImage.shapeAppearanceModel = ShapeAppearanceModel()
|
||||
binding.cardImage.scaleType = ScaleType.CENTER
|
||||
|
||||
Glide.with(binding.cardImage.context)
|
||||
.load(R.drawable.card_image_placeholder)
|
||||
.into(binding.cardImage)
|
||||
binding.statusCardView.bind(link, sensitive = false, statusDisplayOptions) {
|
||||
onClick(link.url)
|
||||
}
|
||||
|
||||
binding.statusCardView.clipToOutline = true
|
||||
}
|
||||
}
|
||||
|
@ -18,17 +18,21 @@ package app.pachli.entity
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Card(
|
||||
val url: String,
|
||||
val title: String,
|
||||
val description: String,
|
||||
@SerializedName("author_name") val authorName: String,
|
||||
val image: String,
|
||||
val type: String,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val blurhash: String?,
|
||||
@SerializedName("embed_url") val embedUrl: String?,
|
||||
) {
|
||||
override val url: String,
|
||||
override val title: String,
|
||||
override val description: String,
|
||||
@SerializedName("type") override val kind: PreviewCardKind,
|
||||
@SerializedName("author_name") override val authorName: String,
|
||||
@SerializedName("author_url") override val authorUrl: String,
|
||||
@SerializedName("provider_name") override val providerName: String,
|
||||
@SerializedName("provider_url") override val providerUrl: String,
|
||||
override val html: String,
|
||||
override val width: Int,
|
||||
override val height: Int,
|
||||
override val image: String? = null,
|
||||
@SerializedName("embed_url") override val embedUrl: String,
|
||||
override val blurhash: String? = null,
|
||||
) : PreviewCard {
|
||||
|
||||
override fun hashCode() = url.hashCode()
|
||||
|
||||
@ -39,8 +43,4 @@ data class Card(
|
||||
val account = other as Card?
|
||||
return account?.url == this.url
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_PHOTO = "photo"
|
||||
}
|
||||
}
|
||||
|
168
app/src/main/java/app/pachli/view/PreviewCardView.kt
Normal file
168
app/src/main/java/app/pachli/view/PreviewCardView.kt
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2023 Pachli Association
|
||||
*
|
||||
* This file is a part of Pachli.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Pachli; if not,
|
||||
* see <http://www.gnu.org/licenses>.
|
||||
*/
|
||||
|
||||
package app.pachli.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import app.pachli.R
|
||||
import app.pachli.databinding.PreviewCardBinding
|
||||
import app.pachli.entity.PreviewCard
|
||||
import app.pachli.util.StatusDisplayOptions
|
||||
import app.pachli.util.decodeBlurHash
|
||||
import app.pachli.util.hide
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
|
||||
/**
|
||||
* Compound view that displays [PreviewCard].
|
||||
*
|
||||
* Classes hosting this should provide a [PreviewCardView.OnClickListener] to be notified when the
|
||||
* the user clicks on the card.
|
||||
*/
|
||||
class PreviewCardView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
defStyleRes: Int = 0,
|
||||
) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
|
||||
/** Where on the card the user clicked */
|
||||
enum class Target {
|
||||
/** Any part of the card that's not the image */
|
||||
CARD,
|
||||
|
||||
/** The image **/
|
||||
IMAGE,
|
||||
}
|
||||
|
||||
fun interface OnClickListener {
|
||||
/** @param target Where on the card the user clicked */
|
||||
fun onClick(target: Target)
|
||||
}
|
||||
|
||||
private val binding: PreviewCardBinding
|
||||
private val radius = context.resources.getDimensionPixelSize(R.dimen.card_radius).toFloat()
|
||||
|
||||
init {
|
||||
val inflater = context.getSystemService(LayoutInflater::class.java)
|
||||
binding = PreviewCardBinding.inflate(inflater, this)
|
||||
orientation = VERTICAL
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the [PreviewCard] data to the view.
|
||||
*
|
||||
* @param card The card to bind
|
||||
* @param sensitive True if the status that contained this card was marked sensitive
|
||||
* @param statusDisplayOptions
|
||||
* @param listener
|
||||
*/
|
||||
fun bind(
|
||||
card: PreviewCard,
|
||||
sensitive: Boolean,
|
||||
statusDisplayOptions: StatusDisplayOptions,
|
||||
listener: OnClickListener,
|
||||
): Unit = with(binding) {
|
||||
cardTitle.text = card.title
|
||||
|
||||
when {
|
||||
card.description.isNotBlank() -> card.description
|
||||
card.authorName.isNotBlank() -> card.authorName
|
||||
else -> null
|
||||
}?.let { cardDescription.text = it } ?: cardDescription.hide()
|
||||
|
||||
previewCardWrapper.setOnClickListener { listener.onClick(Target.CARD) }
|
||||
cardImage.setOnClickListener { listener.onClick(Target.IMAGE) }
|
||||
|
||||
cardLink.text = card.url
|
||||
|
||||
previewCardWrapper.clipToOutline = true
|
||||
|
||||
// Either:
|
||||
// 1. Card has a (possibly sensitive) image that user wants to see, or
|
||||
// 2. Card has a blurhash, use that as the image, or
|
||||
// 3. Use R.drawable.card_image_placeholder
|
||||
if (statusDisplayOptions.mediaPreviewEnabled && (!sensitive || statusDisplayOptions.showSensitiveMedia) && !card.image.isNullOrBlank()) {
|
||||
cardImage.shapeAppearanceModel = if (card.width > card.height) {
|
||||
setTopBottomLayout()
|
||||
} else {
|
||||
setLeftRightLayout()
|
||||
}.build()
|
||||
|
||||
cardImage.scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
|
||||
val builder = Glide.with(cardImage.context)
|
||||
.load(card.image)
|
||||
.dontTransform()
|
||||
if (statusDisplayOptions.useBlurhash && !card.blurhash.isNullOrBlank()) {
|
||||
builder
|
||||
.placeholder(decodeBlurHash(cardImage.context, card.blurhash!!))
|
||||
.into(cardImage)
|
||||
} else {
|
||||
builder.into(cardImage)
|
||||
}
|
||||
} else if (statusDisplayOptions.useBlurhash && !card.blurhash.isNullOrBlank()) {
|
||||
cardImage.shapeAppearanceModel = setLeftRightLayout().build()
|
||||
cardImage.scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
|
||||
Glide.with(cardImage.context)
|
||||
.load(decodeBlurHash(cardImage.context, card.blurhash!!))
|
||||
.dontTransform()
|
||||
.into(cardImage)
|
||||
} else {
|
||||
cardImage.shapeAppearanceModel = setLeftRightLayout().build()
|
||||
cardImage.scaleType = ImageView.ScaleType.CENTER
|
||||
|
||||
Glide.with(cardImage.context)
|
||||
.load(R.drawable.card_image_placeholder)
|
||||
.into(cardImage)
|
||||
}
|
||||
}
|
||||
|
||||
/** Adjusts the layout parameters to place the image above the information views */
|
||||
private fun setTopBottomLayout() = with(binding) {
|
||||
val cardImageShape = ShapeAppearanceModel.Builder()
|
||||
previewCardWrapper.orientation = VERTICAL
|
||||
cardImage.layoutParams.height =
|
||||
cardImage.resources.getDimensionPixelSize(R.dimen.card_image_vertical_height)
|
||||
cardImage.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
cardInfo.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
cardInfo.layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
cardImageShape.setTopLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
cardImageShape.setTopRightCorner(CornerFamily.ROUNDED, radius)
|
||||
return@with cardImageShape
|
||||
}
|
||||
|
||||
/** Adjusts the layout parameters to place the image on the left, the information on the right */
|
||||
private fun setLeftRightLayout() = with(binding) {
|
||||
val cardImageShape = ShapeAppearanceModel.Builder()
|
||||
previewCardWrapper.orientation = HORIZONTAL
|
||||
cardImage.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
cardImage.layoutParams.width =
|
||||
cardImage.resources.getDimensionPixelSize(R.dimen.card_image_horizontal_width)
|
||||
cardInfo.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
cardInfo.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
cardImageShape.setTopLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
cardImageShape.setBottomLeftCorner(CornerFamily.ROUNDED, radius)
|
||||
return@with cardImageShape
|
||||
}
|
||||
}
|
@ -155,78 +155,11 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/status_content_warning_button"
|
||||
tools:text="This is a status" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/status_card_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:background="@drawable/card_frame"
|
||||
android:clipChildren="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:minHeight="80dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button_toggle_content"
|
||||
tools:visibility="gone">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/card_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:layout_margin="1dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/card_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:lines="1"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:maxLines="2"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_link"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="?android:attr/textColorLink"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_toggle_content"
|
||||
style="@style/AppButton.Outlined"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:minWidth="150dp"
|
||||
android:minHeight="0dp"
|
||||
@ -238,10 +171,22 @@
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/status_content"
|
||||
tools:text="@string/post_content_show_less"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<app.pachli.view.PreviewCardView
|
||||
android:id="@+id/status_card_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:minHeight="80dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_toggle_content"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/status_media_preview_container"
|
||||
android:layout_width="0dp"
|
||||
@ -253,7 +198,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_card_view"
|
||||
tools:visibility="visible">
|
||||
tools:visibility="gone">
|
||||
|
||||
<include layout="@layout/item_media_preview" />
|
||||
|
||||
@ -267,7 +212,8 @@
|
||||
android:layout_marginEnd="14dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container" />
|
||||
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/status_reply"
|
||||
|
@ -137,72 +137,18 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/status_content_warning_button"
|
||||
tools:text="Status content. Can be pretty long. " />
|
||||
|
||||
<LinearLayout
|
||||
<app.pachli.view.PreviewCardView
|
||||
android:id="@+id/status_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:background="@drawable/card_frame"
|
||||
android:clipChildren="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:minHeight="80dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/status_content"
|
||||
tools:visibility="gone">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/card_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:layout_margin="1dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/card_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:lines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_link"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/status_media_preview_container"
|
||||
|
@ -15,79 +15,11 @@
|
||||
~ see <http://www.gnu.org/licenses>.
|
||||
-->
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<app.pachli.view.PreviewCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/status_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="14dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/status_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/card_frame"
|
||||
android:clipChildren="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:minHeight="80dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/card_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:layout_margin="1dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/card_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:lines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:ignore="SelectableText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:ignore="SelectableText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_link"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="?android:attr/textColorLink"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:ignore="SelectableText" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
android:paddingEnd="14dp" />
|
||||
|
84
app/src/main/res/layout/preview_card.xml
Normal file
84
app/src/main/res/layout/preview_card.xml
Normal file
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2023 Pachli Association
|
||||
~
|
||||
~ This file is a part of Pachli.
|
||||
~
|
||||
~ This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
~ GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
~ License, or (at your option) any later version.
|
||||
~
|
||||
~ Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
~ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
~ Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with Pachli; if not,
|
||||
~ see <http://www.gnu.org/licenses>.
|
||||
-->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/preview_card_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/card_frame"
|
||||
android:clipChildren="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:minHeight="80dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/card_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:layout_margin="1dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/card_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:lines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:ignore="SelectableText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:ignore="SelectableText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_link"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="?android:attr/textColorLink"
|
||||
android:textSize="?attr/status_text_small"
|
||||
tools:ignore="SelectableText" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</merge>
|
Loading…
x
Reference in New Issue
Block a user