Add option to show link previews in timelines (#1681)
* Add option to show link previews in timelines. Addresses #1075 * Indent cards in non-selected statuses when viewing threads * Indent cards in timelines * Fix clipping of right side of preview in timelines
This commit is contained in:
parent
dd8abad8ca
commit
3edc47aa4a
17 changed files with 214 additions and 119 deletions
|
@ -129,7 +129,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
|
||||||
|
|
||||||
}
|
}
|
||||||
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars",
|
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars",
|
||||||
"useBlurhash" -> {
|
"useBlurhash", "showCardsInTimelines" -> {
|
||||||
restartActivitiesOnExit = true
|
restartActivitiesOnExit = true
|
||||||
}
|
}
|
||||||
"language" -> {
|
"language" -> {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import com.keylesspalace.tusky.entity.Emoji;
|
||||||
import com.keylesspalace.tusky.entity.Notification;
|
import com.keylesspalace.tusky.entity.Notification;
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener;
|
import com.keylesspalace.tusky.interfaces.LinkListener;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
|
import com.keylesspalace.tusky.util.CardViewMode;
|
||||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||||
import com.keylesspalace.tusky.util.LinkHelper;
|
import com.keylesspalace.tusky.util.LinkHelper;
|
||||||
|
@ -230,7 +231,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||||
mediaPreviewEnabled,
|
mediaPreviewEnabled,
|
||||||
statusDisplayOptions.useAbsoluteTime(),
|
statusDisplayOptions.useAbsoluteTime(),
|
||||||
statusDisplayOptions.showBotOverlay(),
|
statusDisplayOptions.showBotOverlay(),
|
||||||
statusDisplayOptions.useBlurhash()
|
statusDisplayOptions.useBlurhash(),
|
||||||
|
CardViewMode.NONE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@ import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
@ -22,14 +24,18 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
import com.keylesspalace.tusky.R;
|
import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.entity.Attachment;
|
import com.keylesspalace.tusky.entity.Attachment;
|
||||||
import com.keylesspalace.tusky.entity.Attachment.Focus;
|
import com.keylesspalace.tusky.entity.Attachment.Focus;
|
||||||
import com.keylesspalace.tusky.entity.Attachment.MetaData;
|
import com.keylesspalace.tusky.entity.Attachment.MetaData;
|
||||||
|
import com.keylesspalace.tusky.entity.Card;
|
||||||
import com.keylesspalace.tusky.entity.Emoji;
|
import com.keylesspalace.tusky.entity.Emoji;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
|
import com.keylesspalace.tusky.util.CardViewMode;
|
||||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||||
import com.keylesspalace.tusky.util.HtmlUtils;
|
import com.keylesspalace.tusky.util.HtmlUtils;
|
||||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||||
|
@ -86,6 +92,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
private TextView pollDescription;
|
private TextView pollDescription;
|
||||||
private Button pollButton;
|
private Button pollButton;
|
||||||
|
|
||||||
|
private LinearLayout cardView;
|
||||||
|
private LinearLayout cardInfo;
|
||||||
|
private ImageView cardImage;
|
||||||
|
private TextView cardTitle;
|
||||||
|
private TextView cardDescription;
|
||||||
|
private TextView cardUrl;
|
||||||
private PollAdapter pollAdapter;
|
private PollAdapter pollAdapter;
|
||||||
|
|
||||||
private SimpleDateFormat shortSdf;
|
private SimpleDateFormat shortSdf;
|
||||||
|
@ -143,6 +155,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
pollDescription = itemView.findViewById(R.id.status_poll_description);
|
pollDescription = itemView.findViewById(R.id.status_poll_description);
|
||||||
pollButton = itemView.findViewById(R.id.status_poll_button);
|
pollButton = itemView.findViewById(R.id.status_poll_button);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
pollAdapter = new PollAdapter();
|
pollAdapter = new PollAdapter();
|
||||||
pollOptions.setAdapter(pollAdapter);
|
pollOptions.setAdapter(pollAdapter);
|
||||||
pollOptions.setLayoutManager(new LinearLayoutManager(pollOptions.getContext()));
|
pollOptions.setLayoutManager(new LinearLayoutManager(pollOptions.getContext()));
|
||||||
|
@ -683,6 +702,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
hideSensitiveMediaWarning();
|
hideSensitiveMediaWarning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cardView != null) {
|
||||||
|
setupCard(status, statusDisplayOptions.cardViewMode());
|
||||||
|
}
|
||||||
|
|
||||||
setupButtons(listener, status.getSenderId());
|
setupButtons(listener, status.getSenderId());
|
||||||
setRebloggingEnabled(status.getRebloggingEnabled(), status.getVisibility());
|
setRebloggingEnabled(status.getRebloggingEnabled(), status.getVisibility());
|
||||||
|
|
||||||
|
@ -911,6 +934,80 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo);
|
return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setupCard(StatusViewData.Concrete status, CardViewMode cardViewMode) {
|
||||||
|
if (cardViewMode != CardViewMode.NONE && status.getAttachments().size() == 0 && status.getCard() != null && !TextUtils.isEmpty(status.getCard().getUrl())) {
|
||||||
|
final Card card = status.getCard();
|
||||||
|
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());
|
||||||
|
} else {
|
||||||
|
cardDescription.setText(card.getDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cardUrl.setText(card.getUrl());
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(card.getImage())) {
|
||||||
|
|
||||||
|
int topLeftRadius = 0;
|
||||||
|
int topRightRadius = 0;
|
||||||
|
int bottomRightRadius = 0;
|
||||||
|
int bottomLeftRadius = 0;
|
||||||
|
|
||||||
|
int radius = cardImage.getContext().getResources()
|
||||||
|
.getDimensionPixelSize(R.dimen.card_radius);
|
||||||
|
|
||||||
|
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;
|
||||||
|
topLeftRadius = radius;
|
||||||
|
topRightRadius = 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;
|
||||||
|
topLeftRadius = radius;
|
||||||
|
bottomLeftRadius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Glide.with(cardImage)
|
||||||
|
.load(card.getImage())
|
||||||
|
.transform(
|
||||||
|
new CenterCrop(),
|
||||||
|
new GranularRoundedCorners(topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius)
|
||||||
|
)
|
||||||
|
.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.setImageResource(R.drawable.card_image_placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
cardView.setOnClickListener(v -> LinkHelper.openLink(card.getUrl(), v.getContext()));
|
||||||
|
cardView.setClipToOutline(true);
|
||||||
|
} else {
|
||||||
|
cardView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String formatDuration(double durationInSeconds) {
|
private static String formatDuration(double durationInSeconds) {
|
||||||
int seconds = (int) Math.round(durationInSeconds) % 60;
|
int seconds = (int) Math.round(durationInSeconds) % 60;
|
||||||
int minutes = (int) durationInSeconds % 3600 / 60;
|
int minutes = (int) durationInSeconds % 3600 / 60;
|
||||||
|
|
|
@ -4,25 +4,18 @@ import android.content.ClipData;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
|
||||||
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
|
||||||
import com.keylesspalace.tusky.R;
|
import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.entity.Card;
|
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
|
import com.keylesspalace.tusky.util.CardViewMode;
|
||||||
import com.keylesspalace.tusky.util.LinkHelper;
|
import com.keylesspalace.tusky.util.LinkHelper;
|
||||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||||
|
@ -33,27 +26,13 @@ import java.util.Date;
|
||||||
class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||||
private TextView reblogs;
|
private TextView reblogs;
|
||||||
private TextView favourites;
|
private TextView favourites;
|
||||||
private LinearLayout cardView;
|
|
||||||
private LinearLayout cardInfo;
|
|
||||||
private ImageView cardImage;
|
|
||||||
private TextView cardTitle;
|
|
||||||
private TextView cardDescription;
|
|
||||||
private TextView cardUrl;
|
|
||||||
private View infoDivider;
|
private View infoDivider;
|
||||||
|
|
||||||
StatusDetailedViewHolder(View view) {
|
StatusDetailedViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
reblogs = view.findViewById(R.id.status_reblogs);
|
reblogs = view.findViewById(R.id.status_reblogs);
|
||||||
favourites = view.findViewById(R.id.status_favourites);
|
favourites = view.findViewById(R.id.status_favourites);
|
||||||
cardView = view.findViewById(R.id.card_view);
|
|
||||||
cardInfo = view.findViewById(R.id.card_info);
|
|
||||||
cardImage = view.findViewById(R.id.card_image);
|
|
||||||
cardTitle = view.findViewById(R.id.card_title);
|
|
||||||
cardDescription = view.findViewById(R.id.card_description);
|
|
||||||
cardUrl = view.findViewById(R.id.card_link);
|
|
||||||
infoDivider = view.findViewById(R.id.status_info_divider);
|
infoDivider = view.findViewById(R.id.status_info_divider);
|
||||||
|
|
||||||
cardView.setClipToOutline(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -127,6 +106,7 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||||
StatusDisplayOptions statusDisplayOptions,
|
StatusDisplayOptions statusDisplayOptions,
|
||||||
@Nullable Object payloads) {
|
@Nullable Object payloads) {
|
||||||
super.setupWithStatus(status, listener, statusDisplayOptions, payloads);
|
super.setupWithStatus(status, listener, statusDisplayOptions, payloads);
|
||||||
|
setupCard(status, CardViewMode.FULL_WIDTH); // Always show card for detailed status
|
||||||
if (payloads == null) {
|
if (payloads == null) {
|
||||||
setReblogAndFavCount(status.getReblogsCount(), status.getFavouritesCount(), listener);
|
setReblogAndFavCount(status.getReblogsCount(), status.getFavouritesCount(), listener);
|
||||||
|
|
||||||
|
@ -145,82 +125,6 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||||
|
|
||||||
content.setOnLongClickListener(longClickListener);
|
content.setOnLongClickListener(longClickListener);
|
||||||
contentWarningDescription.setOnLongClickListener(longClickListener);
|
contentWarningDescription.setOnLongClickListener(longClickListener);
|
||||||
|
|
||||||
if (status.getAttachments().size() == 0 && status.getCard() != null && !TextUtils.isEmpty(status.getCard().getUrl())) {
|
|
||||||
final Card card = status.getCard();
|
|
||||||
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());
|
|
||||||
} else {
|
|
||||||
cardDescription.setText(card.getDescription());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cardUrl.setText(card.getUrl());
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(card.getImage())) {
|
|
||||||
|
|
||||||
int topLeftRadius = 0;
|
|
||||||
int topRightRadius = 0;
|
|
||||||
int bottomRightRadius = 0;
|
|
||||||
int bottomLeftRadius = 0;
|
|
||||||
|
|
||||||
int radius = cardImage.getContext().getResources()
|
|
||||||
.getDimensionPixelSize(R.dimen.card_radius);
|
|
||||||
|
|
||||||
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;
|
|
||||||
topLeftRadius = radius;
|
|
||||||
topRightRadius = 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;
|
|
||||||
topLeftRadius = radius;
|
|
||||||
bottomLeftRadius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Glide.with(cardImage)
|
|
||||||
.load(card.getImage())
|
|
||||||
.transform(
|
|
||||||
new CenterCrop(),
|
|
||||||
new GranularRoundedCorners(topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius)
|
|
||||||
)
|
|
||||||
.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.setImageResource(R.drawable.card_image_placeholder);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cardView.setOnClickListener(v -> LinkHelper.openLink(card.getUrl(), v.getContext()));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
cardView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
setStatusVisibility(status.getVisibility());
|
setStatusVisibility(status.getVisibility());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,8 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
||||||
mediaPreviewEnabled,
|
mediaPreviewEnabled,
|
||||||
statusDisplayOptions.useAbsoluteTime(),
|
statusDisplayOptions.useAbsoluteTime(),
|
||||||
statusDisplayOptions.showBotOverlay(),
|
statusDisplayOptions.showBotOverlay(),
|
||||||
statusDisplayOptions.useBlurhash()
|
statusDisplayOptions.useBlurhash(),
|
||||||
|
statusDisplayOptions.cardViewMode()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,7 @@ import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import com.keylesspalace.tusky.fragment.SFragment
|
import com.keylesspalace.tusky.fragment.SFragment
|
||||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||||
import com.keylesspalace.tusky.util.NetworkState
|
import com.keylesspalace.tusky.util.*
|
||||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils
|
|
||||||
import com.keylesspalace.tusky.util.hide
|
|
||||||
import kotlinx.android.synthetic.main.fragment_timeline.*
|
import kotlinx.android.synthetic.main.fragment_timeline.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -68,7 +65,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
||||||
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
||||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
||||||
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
|
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
|
||||||
useBlurhash = preferences.getBoolean("useBlurhash", true)
|
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||||
|
cardViewMode = CardViewMode.NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,7 @@ import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import com.keylesspalace.tusky.entity.Attachment
|
import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
import com.keylesspalace.tusky.util.*
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils
|
|
||||||
import com.keylesspalace.tusky.util.hide
|
|
||||||
import com.keylesspalace.tusky.util.show
|
|
||||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||||
import kotlinx.android.synthetic.main.fragment_report_statuses.*
|
import kotlinx.android.synthetic.main.fragment_report_statuses.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -119,7 +116,8 @@ class ReportStatusesFragment : Fragment(), Injectable, AdapterHandler {
|
||||||
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
||||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
||||||
showBotOverlay = false,
|
showBotOverlay = false,
|
||||||
useBlurhash = preferences.getBoolean("useBlurhash", true)
|
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||||
|
cardViewMode = CardViewMode.NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
adapter = StatusesAdapter(statusDisplayOptions,
|
adapter = StatusesAdapter(statusDisplayOptions,
|
||||||
|
|
|
@ -51,6 +51,7 @@ import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||||
|
import com.keylesspalace.tusky.util.CardViewMode
|
||||||
import com.keylesspalace.tusky.util.NetworkState
|
import com.keylesspalace.tusky.util.NetworkState
|
||||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||||
|
@ -79,7 +80,8 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
mediaPreviewEnabled = viewModel.mediaPreviewEnabled,
|
mediaPreviewEnabled = viewModel.mediaPreviewEnabled,
|
||||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
||||||
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
|
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
|
||||||
useBlurhash = preferences.getBoolean("useBlurhash", true)
|
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||||
|
cardViewMode = CardViewMode.NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
searchRecyclerView.addItemDecoration(DividerItemDecoration(searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
searchRecyclerView.addItemDecoration(DividerItemDecoration(searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||||
|
|
|
@ -69,6 +69,7 @@ import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
|
import com.keylesspalace.tusky.util.CardViewMode;
|
||||||
import com.keylesspalace.tusky.util.Either;
|
import com.keylesspalace.tusky.util.Either;
|
||||||
import com.keylesspalace.tusky.util.HttpHeaderLink;
|
import com.keylesspalace.tusky.util.HttpHeaderLink;
|
||||||
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
||||||
|
@ -244,7 +245,8 @@ public class NotificationsFragment extends SFragment implements
|
||||||
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
||||||
preferences.getBoolean("absoluteTimeView", false),
|
preferences.getBoolean("absoluteTimeView", false),
|
||||||
preferences.getBoolean("showBotOverlay", true),
|
preferences.getBoolean("showBotOverlay", true),
|
||||||
preferences.getBoolean("useBlurhash", true)
|
preferences.getBoolean("useBlurhash", true),
|
||||||
|
CardViewMode.NONE
|
||||||
);
|
);
|
||||||
|
|
||||||
adapter = new NotificationsAdapter(accountManager.getActiveAccount().getAccountId(),
|
adapter = new NotificationsAdapter(accountManager.getActiveAccount().getAccountId(),
|
||||||
|
|
|
@ -72,6 +72,7 @@ import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
import com.keylesspalace.tusky.repository.Placeholder;
|
import com.keylesspalace.tusky.repository.Placeholder;
|
||||||
import com.keylesspalace.tusky.repository.TimelineRepository;
|
import com.keylesspalace.tusky.repository.TimelineRepository;
|
||||||
import com.keylesspalace.tusky.repository.TimelineRequestMode;
|
import com.keylesspalace.tusky.repository.TimelineRequestMode;
|
||||||
|
import com.keylesspalace.tusky.util.CardViewMode;
|
||||||
import com.keylesspalace.tusky.util.Either;
|
import com.keylesspalace.tusky.util.Either;
|
||||||
import com.keylesspalace.tusky.util.LinkHelper;
|
import com.keylesspalace.tusky.util.LinkHelper;
|
||||||
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
||||||
|
@ -226,7 +227,10 @@ public class TimelineFragment extends SFragment implements
|
||||||
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
||||||
preferences.getBoolean("absoluteTimeView", false),
|
preferences.getBoolean("absoluteTimeView", false),
|
||||||
preferences.getBoolean("showBotOverlay", true),
|
preferences.getBoolean("showBotOverlay", true),
|
||||||
preferences.getBoolean("useBlurhash", true)
|
preferences.getBoolean("useBlurhash", true),
|
||||||
|
preferences.getBoolean("showCardsInTimelines", false) ?
|
||||||
|
CardViewMode.INDENTED :
|
||||||
|
CardViewMode.NONE
|
||||||
);
|
);
|
||||||
adapter = new TimelineAdapter(dataSource, statusDisplayOptions, this);
|
adapter = new TimelineAdapter(dataSource, statusDisplayOptions, this);
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.entity.StatusContext;
|
import com.keylesspalace.tusky.entity.StatusContext;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
import com.keylesspalace.tusky.network.MastodonApi;
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
|
import com.keylesspalace.tusky.util.CardViewMode;
|
||||||
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
||||||
import com.keylesspalace.tusky.util.PairedList;
|
import com.keylesspalace.tusky.util.PairedList;
|
||||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||||
|
@ -131,7 +132,10 @@ public final class ViewThreadFragment extends SFragment implements
|
||||||
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
||||||
preferences.getBoolean("absoluteTimeView", false),
|
preferences.getBoolean("absoluteTimeView", false),
|
||||||
preferences.getBoolean("showBotOverlay", true),
|
preferences.getBoolean("showBotOverlay", true),
|
||||||
preferences.getBoolean("useBlurhash", true)
|
preferences.getBoolean("useBlurhash", true),
|
||||||
|
preferences.getBoolean("showCardsInTimelines", false) ?
|
||||||
|
CardViewMode.INDENTED :
|
||||||
|
CardViewMode.NONE
|
||||||
);
|
);
|
||||||
adapter = new ThreadAdapter(statusDisplayOptions, this);
|
adapter = new ThreadAdapter(statusDisplayOptions, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.keylesspalace.tusky.util
|
||||||
|
|
||||||
|
enum class CardViewMode {
|
||||||
|
NONE,
|
||||||
|
FULL_WIDTH,
|
||||||
|
INDENTED
|
||||||
|
}
|
|
@ -10,5 +10,7 @@ data class StatusDisplayOptions(
|
||||||
@get:JvmName("showBotOverlay")
|
@get:JvmName("showBotOverlay")
|
||||||
val showBotOverlay: Boolean,
|
val showBotOverlay: Boolean,
|
||||||
@get:JvmName("useBlurhash")
|
@get:JvmName("useBlurhash")
|
||||||
val useBlurhash: Boolean
|
val useBlurhash: Boolean,
|
||||||
|
@get:JvmName("cardViewMode")
|
||||||
|
val cardViewMode: CardViewMode
|
||||||
)
|
)
|
|
@ -157,6 +157,73 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_content_warning_button"
|
app:layout_constraintTop_toBottomOf="@id/status_content_warning_button"
|
||||||
tools:text="This is a status" />
|
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:background="@drawable/card_frame"
|
||||||
|
android:clipChildren="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:minHeight="80dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/status_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:visibility="gone">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/card_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="300dp"
|
||||||
|
android:layout_margin="1dp"
|
||||||
|
android:background="?attr/colorBackgroundAccent"
|
||||||
|
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">
|
||||||
|
|
||||||
|
<androidx.emoji.widget.EmojiTextView
|
||||||
|
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" />
|
||||||
|
|
||||||
|
<androidx.emoji.widget.EmojiTextView
|
||||||
|
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>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_toggle_content"
|
android:id="@+id/button_toggle_content"
|
||||||
style="@style/TuskyButton.Outlined"
|
style="@style/TuskyButton.Outlined"
|
||||||
|
@ -175,7 +242,7 @@
|
||||||
android:textSize="?attr/status_text_medium"
|
android:textSize="?attr/status_text_medium"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_content"
|
app:layout_constraintTop_toBottomOf="@id/status_card_view"
|
||||||
tools:text="@string/status_content_show_less"
|
tools:text="@string/status_content_show_less"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
tools:text="Status content. Can be pretty long. " />
|
tools:text="Status content. Can be pretty long. " />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/card_view"
|
android:id="@+id/status_card_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
@ -204,7 +204,7 @@
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
android:background="@drawable/media_preview_outline"
|
android:background="@drawable/media_preview_outline"
|
||||||
android:importantForAccessibility="noHideDescendants"
|
android:importantForAccessibility="noHideDescendants"
|
||||||
app:layout_constraintTop_toBottomOf="@id/card_view">
|
app:layout_constraintTop_toBottomOf="@id/status_card_view">
|
||||||
|
|
||||||
<com.keylesspalace.tusky.view.MediaPreviewImageView
|
<com.keylesspalace.tusky.view.MediaPreviewImageView
|
||||||
android:id="@+id/status_media_preview_0"
|
android:id="@+id/status_media_preview_0"
|
||||||
|
|
|
@ -548,5 +548,6 @@
|
||||||
<string name="no_saved_status">You don\'t have any drafts.</string>
|
<string name="no_saved_status">You don\'t have any drafts.</string>
|
||||||
<string name="no_scheduled_status">You don\'t have any scheduled statuses.</string>
|
<string name="no_scheduled_status">You don\'t have any scheduled statuses.</string>
|
||||||
<string name="warning_scheduling_interval">Mastodon has a minimum scheduling interval of 5 minutes.</string>
|
<string name="warning_scheduling_interval">Mastodon has a minimum scheduling interval of 5 minutes.</string>
|
||||||
|
<string name="pref_title_show_cards_in_timelines">Show link previews in timelines</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -72,6 +72,12 @@
|
||||||
android:title="@string/pref_title_show_notifications_filter"
|
android:title="@string/pref_title_show_notifications_filter"
|
||||||
app:singleLineTitle="false" />
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="showCardsInTimelines"
|
||||||
|
android:title="@string/pref_title_show_cards_in_timelines"
|
||||||
|
app:singleLineTitle="false" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/pref_title_browser_settings">
|
<PreferenceCategory android:title="@string/pref_title_browser_settings">
|
||||||
|
|
Loading…
Reference in a new issue