improve preview cards (#4782)
- new design thats more Material3-ish - support for the Mastodon 4.3 fediverse:creator feature and other new card attributes closes #4732 closes https://github.com/tuskyapp/Tusky/issues/3340 before: <img src="https://github.com/user-attachments/assets/6cd9ccfc-7f7d-459b-90d9-547cdca0d8c4" width="280"/> <img src="https://github.com/user-attachments/assets/286b5b19-49a3-4b2f-97a9-185fc1f31a8e" width="280"/> after: <img src="https://github.com/user-attachments/assets/b57acf74-e7d3-411e-9186-763de87fa9ca" width="280"/> <img src="https://github.com/user-attachments/assets/50684c30-b4bf-4f05-8b8e-e5fd2bf3d7b6" width="280"/>
This commit is contained in:
parent
d6b276d8df
commit
510e093456
16 changed files with 225 additions and 225 deletions
|
|
@ -6,6 +6,7 @@ import android.content.Context;
|
|||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
|
|
@ -34,6 +35,7 @@ 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.card.MaterialCardView;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.shape.CornerFamily;
|
||||
|
|
@ -43,10 +45,11 @@ import com.keylesspalace.tusky.ViewMediaActivity;
|
|||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.Attachment.Focus;
|
||||
import com.keylesspalace.tusky.entity.Attachment.MetaData;
|
||||
import com.keylesspalace.tusky.entity.Card;
|
||||
import com.keylesspalace.tusky.entity.PreviewCard;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.HashTag;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.TimelineAccount;
|
||||
import com.keylesspalace.tusky.entity.Translation;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.AbsoluteTimeFormatter;
|
||||
|
|
@ -111,12 +114,14 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
private final TextView pollDescription;
|
||||
private final Button pollButton;
|
||||
|
||||
private final LinearLayout cardView;
|
||||
private final LinearLayout cardInfo;
|
||||
private final MaterialCardView cardView;
|
||||
private final LinearLayout cardLayout;
|
||||
private final ShapeableImageView cardImage;
|
||||
private final TextView cardTitle;
|
||||
private final TextView cardDescription;
|
||||
private final TextView cardUrl;
|
||||
private final TextView cardMetadata;
|
||||
private final TextView cardAuthor;
|
||||
private final TextView cardAuthorButton;
|
||||
|
||||
private final PollAdapter pollAdapter;
|
||||
protected final ConstraintLayout statusContainer;
|
||||
private final TextView translationStatusView;
|
||||
|
|
@ -169,11 +174,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
pollButton = itemView.findViewById(R.id.status_poll_button);
|
||||
|
||||
cardView = itemView.findViewById(R.id.status_card_view);
|
||||
cardInfo = itemView.findViewById(R.id.card_info);
|
||||
cardLayout = itemView.findViewById(R.id.status_card_layout);
|
||||
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);
|
||||
cardMetadata = itemView.findViewById(R.id.card_metadata);
|
||||
cardAuthor = itemView.findViewById(R.id.card_author);
|
||||
cardAuthorButton = itemView.findViewById(R.id.card_author_button);
|
||||
|
||||
statusContainer = itemView.findViewById(R.id.status_container);
|
||||
|
||||
|
|
@ -830,9 +836,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
for (Object item : (List<?>) payloads) {
|
||||
if (Key.KEY_CREATED.equals(item)) {
|
||||
setMetaData(status, statusDisplayOptions, listener);
|
||||
if (status.getStatus().getCard() != null && status.getStatus().getCard().getPublishedAt() != null) {
|
||||
// there is a preview card showing the published time, we need to refresh it as well
|
||||
setupCard(status, status.isExpanded(), statusDisplayOptions.cardViewMode(), statusDisplayOptions, listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1128,8 +1137,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
return;
|
||||
}
|
||||
|
||||
final Context context = cardView.getContext();
|
||||
|
||||
final Status actionable = status.getActionable();
|
||||
final Card card = actionable.getCard();
|
||||
final PreviewCard card = actionable.getCard();
|
||||
|
||||
if (cardViewMode != CardViewMode.NONE &&
|
||||
actionable.getAttachments().isEmpty() &&
|
||||
|
|
@ -1141,45 +1152,74 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
cardView.setVisibility(View.VISIBLE);
|
||||
cardTitle.setText(card.getTitle());
|
||||
if (TextUtils.isEmpty(card.getDescription()) && TextUtils.isEmpty(card.getAuthorName())) {
|
||||
cardDescription.setVisibility(View.GONE);
|
||||
|
||||
String providerName = card.getProviderName();
|
||||
if (TextUtils.isEmpty(providerName)) {
|
||||
providerName = Uri.parse(card.getUrl()).getHost();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(providerName) && card.getPublishedAt() != null) {
|
||||
cardMetadata.setVisibility(View.GONE);
|
||||
} else {
|
||||
cardDescription.setVisibility(View.VISIBLE);
|
||||
if (TextUtils.isEmpty(card.getDescription())) {
|
||||
cardDescription.setText(card.getAuthorName());
|
||||
cardMetadata.setVisibility(View.VISIBLE);
|
||||
if (card.getPublishedAt() == null) {
|
||||
cardMetadata.setText(providerName);
|
||||
} else {
|
||||
cardDescription.setText(card.getDescription());
|
||||
String metadataJoiner = context.getString(R.string.metadata_joiner);
|
||||
cardMetadata.setText(providerName + metadataJoiner + TimestampUtils.getRelativeTimeSpanString(context, card.getPublishedAt().getTime(), System.currentTimeMillis()));
|
||||
}
|
||||
}
|
||||
|
||||
cardUrl.setText(card.getUrl());
|
||||
String cardAuthorName;
|
||||
final TimelineAccount cardAuthorAccount;
|
||||
if (card.getAuthors().isEmpty()) {
|
||||
cardAuthorAccount = null;
|
||||
cardAuthorName = card.getAuthorName();
|
||||
} else {
|
||||
cardAuthorName = card.getAuthors().get(0).getName();
|
||||
cardAuthorAccount = card.getAuthors().get(0).getAccount();
|
||||
if (cardAuthorAccount != null) {
|
||||
cardAuthorName = cardAuthorAccount.getName();
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(cardAuthorName)) {
|
||||
cardAuthor.setVisibility(View.VISIBLE);
|
||||
cardAuthor.setText(card.getDescription());
|
||||
cardAuthorButton.setVisibility(View.GONE);
|
||||
} else if (cardAuthorAccount == null) {
|
||||
cardAuthor.setVisibility(View.VISIBLE);
|
||||
cardAuthor.setText(context.getString(R.string.preview_card_by_author, cardAuthorName));
|
||||
cardAuthorButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
cardAuthorButton.setVisibility(View.VISIBLE);
|
||||
final String buttonText = context.getString(R.string.preview_card_more_by_author, cardAuthorName);
|
||||
final CharSequence emojifiedButtonText = CustomEmojiHelper.emojify(buttonText, cardAuthorAccount.getEmojis(), cardAuthorButton, statusDisplayOptions.animateEmojis());
|
||||
cardAuthorButton.setText(emojifiedButtonText);
|
||||
cardAuthorButton.setOnClickListener(v-> listener.onViewAccount(cardAuthorAccount.getId()));
|
||||
cardAuthor.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 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);
|
||||
int radius = context.getResources().getDimensionPixelSize(R.dimen.inner_card_radius);
|
||||
ShapeAppearanceModel.Builder cardImageShape = ShapeAppearanceModel.builder();
|
||||
|
||||
if (card.getWidth() > card.getHeight()) {
|
||||
cardView.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
cardLayout.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);
|
||||
cardLayout.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);
|
||||
}
|
||||
|
|
@ -1197,14 +1237,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
.into(cardImage);
|
||||
} else if (statusDisplayOptions.useBlurhash() && !TextUtils.isEmpty(card.getBlurhash())) {
|
||||
int radius = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_radius);
|
||||
.getDimensionPixelSize(R.dimen.inner_card_radius);
|
||||
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardLayout.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)
|
||||
|
|
@ -1218,12 +1256,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
.load(decodeBlurHash(card.getBlurhash()))
|
||||
.into(cardImage);
|
||||
} else {
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardLayout.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());
|
||||
|
||||
|
|
@ -1238,11 +1274,9 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
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()) ?
|
||||
cardImage.setOnClickListener(card.getType().equals(PreviewCard.TYPE_PHOTO) && !TextUtils.isEmpty(card.getEmbedUrl()) ?
|
||||
v -> cardView.getContext().startActivity(ViewMediaActivity.newSingleImageIntent(cardView.getContext(), card.getEmbedUrl())) :
|
||||
visitLink);
|
||||
|
||||
cardView.setClipToOutline(true);
|
||||
} else {
|
||||
cardView.setVisibility(View.GONE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,12 @@
|
|||
package com.keylesspalace.tusky.components.compose.view
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import com.google.android.material.R as materialR
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.adapter.PreviewPollOptionsAdapter
|
||||
import com.keylesspalace.tusky.databinding.ViewPollPreviewBinding
|
||||
|
|
@ -27,23 +30,18 @@ import com.keylesspalace.tusky.entity.NewPoll
|
|||
class PollPreviewView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
defStyleAttr: Int = materialR.attr.materialCardViewOutlinedStyle
|
||||
) :
|
||||
LinearLayout(context, attrs, defStyleAttr) {
|
||||
MaterialCardView(context, attrs, defStyleAttr) {
|
||||
|
||||
private val adapter = PreviewPollOptionsAdapter()
|
||||
|
||||
private val binding = ViewPollPreviewBinding.inflate(LayoutInflater.from(context), this)
|
||||
|
||||
init {
|
||||
orientation = VERTICAL
|
||||
|
||||
setBackgroundResource(R.drawable.card_frame)
|
||||
|
||||
val padding = resources.getDimensionPixelSize(R.dimen.poll_preview_padding)
|
||||
|
||||
setPadding(padding, padding, padding, padding)
|
||||
|
||||
setStrokeColor(ColorStateList.valueOf(MaterialColors.getColor(this, materialR.attr.colorOutline)))
|
||||
strokeWidth
|
||||
elevation = 0f
|
||||
binding.pollPreviewOptions.adapter = adapter
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ import com.keylesspalace.tusky.components.conversation.ConversationAccountEntity
|
|||
import com.keylesspalace.tusky.createTabDataFromId
|
||||
import com.keylesspalace.tusky.db.entity.DraftAttachment
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Card
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
import com.keylesspalace.tusky.entity.FilterResult
|
||||
import com.keylesspalace.tusky.entity.HashTag
|
||||
import com.keylesspalace.tusky.entity.NewPoll
|
||||
import com.keylesspalace.tusky.entity.Poll
|
||||
import com.keylesspalace.tusky.entity.PreviewCard
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.settings.DefaultReplyVisibility
|
||||
import com.squareup.moshi.Moshi
|
||||
|
|
@ -196,13 +196,13 @@ class Converters @Inject constructor(
|
|||
}
|
||||
|
||||
@TypeConverter
|
||||
fun cardToJson(card: Card?): String {
|
||||
return moshi.adapter<Card?>().toJson(card)
|
||||
fun cardToJson(card: PreviewCard?): String {
|
||||
return moshi.adapter<PreviewCard?>().toJson(card)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToCard(cardJson: String?): Card? {
|
||||
return cardJson?.let { moshi.adapter<Card?>().fromJson(cardJson) }
|
||||
fun jsonToCard(cardJson: String?): PreviewCard? {
|
||||
return cardJson?.let { moshi.adapter<PreviewCard?>().fromJson(cardJson) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ import com.keylesspalace.tusky.db.Converters
|
|||
import com.keylesspalace.tusky.db.entity.TimelineAccountEntity
|
||||
import com.keylesspalace.tusky.db.entity.TimelineStatusEntity
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Card
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
import com.keylesspalace.tusky.entity.HashTag
|
||||
import com.keylesspalace.tusky.entity.Poll
|
||||
import com.keylesspalace.tusky.entity.PreviewCard
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
|
|
@ -81,7 +81,7 @@ AND s.tuskyAccountId = :tuskyAccountId"""
|
|||
poll = moshi.adapter<Poll?>().toJson(status.poll),
|
||||
muted = status.muted,
|
||||
pinned = status.pinned,
|
||||
card = moshi.adapter<Card?>().toJson(status.card),
|
||||
card = moshi.adapter<PreviewCard?>().toJson(status.card),
|
||||
language = status.language
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ import androidx.room.Index
|
|||
import androidx.room.TypeConverters
|
||||
import com.keylesspalace.tusky.db.Converters
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Card
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
import com.keylesspalace.tusky.entity.FilterResult
|
||||
import com.keylesspalace.tusky.entity.HashTag
|
||||
import com.keylesspalace.tusky.entity.Poll
|
||||
import com.keylesspalace.tusky.entity.PreviewCard
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
|
||||
/**
|
||||
|
|
@ -81,7 +81,7 @@ data class TimelineStatusEntity(
|
|||
val contentCollapsed: Boolean,
|
||||
val contentShowing: Boolean,
|
||||
val pinned: Boolean,
|
||||
val card: Card?,
|
||||
val card: PreviewCard?,
|
||||
val language: String?,
|
||||
val filtered: List<FilterResult>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,13 +17,17 @@ package com.keylesspalace.tusky.entity
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.util.Date
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Card(
|
||||
data class PreviewCard(
|
||||
val url: String,
|
||||
val title: String,
|
||||
val description: String = "",
|
||||
@Json(name = "author_name") val authorName: String = "",
|
||||
val authors: List<PreviewCardAuthor> = emptyList(),
|
||||
@Json(name = "author_name") val authorName: String? = null,
|
||||
@Json(name = "provider_name") val providerName: String? = null,
|
||||
@Json(name = "published_at") val publishedAt: Date?,
|
||||
val image: String? = null,
|
||||
val type: String,
|
||||
val width: Int = 0,
|
||||
|
|
@ -35,7 +39,7 @@ data class Card(
|
|||
override fun hashCode() = url.hashCode()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Card) {
|
||||
if (other !is PreviewCard) {
|
||||
return false
|
||||
}
|
||||
return other.url == this.url
|
||||
|
|
@ -45,3 +49,10 @@ data class Card(
|
|||
const val TYPE_PHOTO = "photo"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PreviewCardAuthor(
|
||||
val name: String,
|
||||
val url: String,
|
||||
val account: TimelineAccount?
|
||||
)
|
||||
|
|
@ -53,7 +53,7 @@ data class Status(
|
|||
val muted: Boolean = false,
|
||||
val poll: Poll? = null,
|
||||
/** Preview card for links included within status content. */
|
||||
val card: Card? = null,
|
||||
val card: PreviewCard? = null,
|
||||
/** ISO 639 language code for this status. */
|
||||
val language: String? = null,
|
||||
/** If the current token has an authorized user: The filter and keywords that matched this status.
|
||||
|
|
|
|||
|
|
@ -16,13 +16,11 @@
|
|||
package com.keylesspalace.tusky.view
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.content.res.use
|
||||
import com.google.android.material.R as materialR
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.databinding.CardLicenseBinding
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
|
|
@ -32,14 +30,12 @@ class LicenseCard
|
|||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = R.attr.licenseCardStyle
|
||||
defStyleAttr: Int = materialR.attr.materialCardViewFilledStyle
|
||||
) : MaterialCardView(context, attrs, defStyleAttr) {
|
||||
|
||||
init {
|
||||
val binding = CardLicenseBinding.inflate(LayoutInflater.from(context), this)
|
||||
|
||||
setStrokeColor(ColorStateList.valueOf(MaterialColors.getColor(this, materialR.attr.colorOutline)))
|
||||
|
||||
val (name, license, link) = context.theme.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.LicenseCard,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue