mirror of
https://github.com/TeamNewPipe/NewPipe.git
synced 2024-11-22 11:02:35 +01:00
Ellipsize playlist description if it is longer than 5 lines
The description can be expanded / collapsed via a "show more" / "show less" button.
This commit is contained in:
parent
6c99557553
commit
65eb631711
@ -3,7 +3,7 @@ package org.schabi.newpipe.fragments.list.playlist;
|
|||||||
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
|
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
|
||||||
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
||||||
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
|
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
|
||||||
import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD;
|
import static org.schabi.newpipe.util.ServiceHelper.getServiceById;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -19,7 +19,6 @@ import android.view.ViewGroup;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.core.text.HtmlCompat;
|
|
||||||
|
|
||||||
import com.google.android.material.shape.CornerFamily;
|
import com.google.android.material.shape.CornerFamily;
|
||||||
import com.google.android.material.shape.ShapeAppearanceModel;
|
import com.google.android.material.shape.ShapeAppearanceModel;
|
||||||
@ -52,10 +51,10 @@ import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
|||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
|
||||||
import org.schabi.newpipe.util.PlayButtonHelper;
|
import org.schabi.newpipe.util.PlayButtonHelper;
|
||||||
import org.schabi.newpipe.util.text.TextLinkifier;
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||||
|
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||||
|
import org.schabi.newpipe.util.text.TextEllipsizer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -329,13 +328,24 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
|
|||||||
final Description description = result.getDescription();
|
final Description description = result.getDescription();
|
||||||
if (description != null && description != Description.EMPTY_DESCRIPTION
|
if (description != null && description != Description.EMPTY_DESCRIPTION
|
||||||
&& !isBlank(description.getContent())) {
|
&& !isBlank(description.getContent())) {
|
||||||
TextLinkifier.fromDescription(headerBinding.playlistDescription,
|
final TextEllipsizer ellipsizer = new TextEllipsizer(
|
||||||
description, HtmlCompat.FROM_HTML_MODE_LEGACY,
|
headerBinding.playlistDescription, 5, getServiceById(result.getServiceId()));
|
||||||
result.getService(), result.getUrl(),
|
ellipsizer.setStateChangeListener(isEllipsized ->
|
||||||
disposables, SET_LINK_MOVEMENT_METHOD);
|
headerBinding.playlistDescriptionReadMore.setText(
|
||||||
headerBinding.playlistDescription.setVisibility(View.VISIBLE);
|
Boolean.TRUE.equals(isEllipsized) ? R.string.show_more : R.string.show_less
|
||||||
|
));
|
||||||
|
ellipsizer.setOnContentChanged(canBeEllipsized -> {
|
||||||
|
headerBinding.playlistDescriptionReadMore.setVisibility(
|
||||||
|
Boolean.TRUE.equals(canBeEllipsized) ? View.VISIBLE : View.GONE);
|
||||||
|
if (Boolean.TRUE.equals(canBeEllipsized)) {
|
||||||
|
ellipsizer.ellipsize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ellipsizer.setContent(description);
|
||||||
|
headerBinding.playlistDescriptionReadMore.setOnClickListener(v -> ellipsizer.toggle());
|
||||||
} else {
|
} else {
|
||||||
headerBinding.playlistDescription.setVisibility(View.GONE);
|
headerBinding.playlistDescription.setVisibility(View.GONE);
|
||||||
|
headerBinding.playlistDescriptionReadMore.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.getErrors().isEmpty()) {
|
if (!result.getErrors().isEmpty()) {
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package org.schabi.newpipe.info_list.holder;
|
package org.schabi.newpipe.info_list.holder;
|
||||||
|
|
||||||
import static android.text.TextUtils.isEmpty;
|
|
||||||
import static org.schabi.newpipe.util.ServiceHelper.getServiceById;
|
import static org.schabi.newpipe.util.ServiceHelper.getServiceById;
|
||||||
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.text.Layout;
|
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.text.style.URLSpan;
|
import android.text.style.URLSpan;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -15,42 +12,28 @@ import android.widget.RelativeLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.text.HtmlCompat;
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.Description;
|
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.DeviceUtils;
|
import org.schabi.newpipe.util.DeviceUtils;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||||
import org.schabi.newpipe.util.image.ImageStrategy;
|
import org.schabi.newpipe.util.image.ImageStrategy;
|
||||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
|
||||||
import org.schabi.newpipe.util.text.CommentTextOnTouchListener;
|
import org.schabi.newpipe.util.text.CommentTextOnTouchListener;
|
||||||
import org.schabi.newpipe.util.text.TextLinkifier;
|
import org.schabi.newpipe.util.text.TextEllipsizer;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
|
||||||
|
|
||||||
public class CommentInfoItemHolder extends InfoItemHolder {
|
public class CommentInfoItemHolder extends InfoItemHolder {
|
||||||
private static final String ELLIPSIS = "…";
|
|
||||||
|
|
||||||
private static final int COMMENT_DEFAULT_LINES = 2;
|
private static final int COMMENT_DEFAULT_LINES = 2;
|
||||||
private static final int COMMENT_EXPANDED_LINES = 1000;
|
|
||||||
|
|
||||||
private final int commentHorizontalPadding;
|
private final int commentHorizontalPadding;
|
||||||
private final int commentVerticalPadding;
|
private final int commentVerticalPadding;
|
||||||
|
|
||||||
private final Paint paintAtContentSize;
|
|
||||||
private final float ellipsisWidthPx;
|
|
||||||
|
|
||||||
private final RelativeLayout itemRoot;
|
private final RelativeLayout itemRoot;
|
||||||
private final ImageView itemThumbnailView;
|
private final ImageView itemThumbnailView;
|
||||||
private final TextView itemContentView;
|
private final TextView itemContentView;
|
||||||
@ -61,13 +44,8 @@ public class CommentInfoItemHolder extends InfoItemHolder {
|
|||||||
private final ImageView itemPinnedView;
|
private final ImageView itemPinnedView;
|
||||||
private final Button repliesButton;
|
private final Button repliesButton;
|
||||||
|
|
||||||
private final CompositeDisposable disposables = new CompositeDisposable();
|
@NonNull
|
||||||
@Nullable
|
private final TextEllipsizer textEllipsizer;
|
||||||
private Description commentText;
|
|
||||||
@Nullable
|
|
||||||
private StreamingService streamService;
|
|
||||||
@Nullable
|
|
||||||
private String streamUrl;
|
|
||||||
|
|
||||||
public CommentInfoItemHolder(final InfoItemBuilder infoItemBuilder,
|
public CommentInfoItemHolder(final InfoItemBuilder infoItemBuilder,
|
||||||
final ViewGroup parent) {
|
final ViewGroup parent) {
|
||||||
@ -88,9 +66,14 @@ public class CommentInfoItemHolder extends InfoItemHolder {
|
|||||||
commentVerticalPadding = (int) infoItemBuilder.getContext()
|
commentVerticalPadding = (int) infoItemBuilder.getContext()
|
||||||
.getResources().getDimension(R.dimen.comments_vertical_padding);
|
.getResources().getDimension(R.dimen.comments_vertical_padding);
|
||||||
|
|
||||||
paintAtContentSize = new Paint();
|
textEllipsizer = new TextEllipsizer(itemContentView, COMMENT_DEFAULT_LINES, null);
|
||||||
paintAtContentSize.setTextSize(itemContentView.getTextSize());
|
textEllipsizer.setStateChangeListener(isEllipsized -> {
|
||||||
ellipsisWidthPx = paintAtContentSize.measureText(ELLIPSIS);
|
if (Boolean.TRUE.equals(isEllipsized)) {
|
||||||
|
denyLinkFocus();
|
||||||
|
} else {
|
||||||
|
determineMovementMethod();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -139,16 +122,16 @@ public class CommentInfoItemHolder extends InfoItemHolder {
|
|||||||
|
|
||||||
|
|
||||||
// setup comment content and click listeners to expand/ellipsize it
|
// setup comment content and click listeners to expand/ellipsize it
|
||||||
streamService = getServiceById(item.getServiceId());
|
textEllipsizer.setStreamingService(getServiceById(item.getServiceId()));
|
||||||
streamUrl = item.getUrl();
|
textEllipsizer.setStreamUrl(item.getUrl());
|
||||||
commentText = item.getCommentText();
|
textEllipsizer.setContent(item.getCommentText());
|
||||||
ellipsize();
|
textEllipsizer.ellipsize();
|
||||||
|
|
||||||
//noinspection ClickableViewAccessibility
|
//noinspection ClickableViewAccessibility
|
||||||
itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE);
|
itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE);
|
||||||
|
|
||||||
itemView.setOnClickListener(view -> {
|
itemView.setOnClickListener(view -> {
|
||||||
toggleEllipsize();
|
textEllipsizer.toggle();
|
||||||
if (itemBuilder.getOnCommentsSelectedListener() != null) {
|
if (itemBuilder.getOnCommentsSelectedListener() != null) {
|
||||||
itemBuilder.getOnCommentsSelectedListener().selected(item);
|
itemBuilder.getOnCommentsSelectedListener().selected(item);
|
||||||
}
|
}
|
||||||
@ -202,76 +185,4 @@ public class CommentInfoItemHolder extends InfoItemHolder {
|
|||||||
denyLinkFocus();
|
denyLinkFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ellipsize() {
|
|
||||||
itemContentView.setMaxLines(COMMENT_EXPANDED_LINES);
|
|
||||||
linkifyCommentContentView(v -> {
|
|
||||||
boolean hasEllipsis = false;
|
|
||||||
|
|
||||||
final CharSequence charSeqText = itemContentView.getText();
|
|
||||||
if (charSeqText != null && itemContentView.getLineCount() > COMMENT_DEFAULT_LINES) {
|
|
||||||
// Note that converting to String removes spans (i.e. links), but that's something
|
|
||||||
// we actually want since when the text is ellipsized we want all clicks on the
|
|
||||||
// comment to expand the comment, not to open links.
|
|
||||||
final String text = charSeqText.toString();
|
|
||||||
|
|
||||||
final Layout layout = itemContentView.getLayout();
|
|
||||||
final float lineWidth = layout.getLineWidth(COMMENT_DEFAULT_LINES - 1);
|
|
||||||
final float layoutWidth = layout.getWidth();
|
|
||||||
final int lineStart = layout.getLineStart(COMMENT_DEFAULT_LINES - 1);
|
|
||||||
final int lineEnd = layout.getLineEnd(COMMENT_DEFAULT_LINES - 1);
|
|
||||||
|
|
||||||
// remove characters up until there is enough space for the ellipsis
|
|
||||||
// (also summing 2 more pixels, just to be sure to avoid float rounding errors)
|
|
||||||
int end = lineEnd;
|
|
||||||
float removedCharactersWidth = 0.0f;
|
|
||||||
while (lineWidth - removedCharactersWidth + ellipsisWidthPx + 2.0f > layoutWidth
|
|
||||||
&& end >= lineStart) {
|
|
||||||
end -= 1;
|
|
||||||
// recalculate each time to account for ligatures or other similar things
|
|
||||||
removedCharactersWidth = paintAtContentSize.measureText(
|
|
||||||
text.substring(end, lineEnd));
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove trailing spaces and newlines
|
|
||||||
while (end > 0 && Character.isWhitespace(text.charAt(end - 1))) {
|
|
||||||
end -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String newVal = text.substring(0, end) + ELLIPSIS;
|
|
||||||
itemContentView.setText(newVal);
|
|
||||||
hasEllipsis = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemContentView.setMaxLines(COMMENT_DEFAULT_LINES);
|
|
||||||
if (hasEllipsis) {
|
|
||||||
denyLinkFocus();
|
|
||||||
} else {
|
|
||||||
determineMovementMethod();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleEllipsize() {
|
|
||||||
final CharSequence text = itemContentView.getText();
|
|
||||||
if (!isEmpty(text) && text.charAt(text.length() - 1) == ELLIPSIS.charAt(0)) {
|
|
||||||
expand();
|
|
||||||
} else if (itemContentView.getLineCount() > COMMENT_DEFAULT_LINES) {
|
|
||||||
ellipsize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void expand() {
|
|
||||||
itemContentView.setMaxLines(COMMENT_EXPANDED_LINES);
|
|
||||||
linkifyCommentContentView(v -> determineMovementMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void linkifyCommentContentView(@Nullable final Consumer<TextView> onCompletion) {
|
|
||||||
disposables.clear();
|
|
||||||
if (commentText != null) {
|
|
||||||
TextLinkifier.fromDescription(itemContentView, commentText,
|
|
||||||
HtmlCompat.FROM_HTML_MODE_LEGACY, streamService, streamUrl, disposables,
|
|
||||||
onCompletion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,195 @@
|
|||||||
|
package org.schabi.newpipe.util.text;
|
||||||
|
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.text.HtmlCompat;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.stream.Description;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Class to ellipsize text inside a {@link TextView}.</p>
|
||||||
|
* This class provides all utils to automatically ellipsize and expand a text
|
||||||
|
*/
|
||||||
|
public final class TextEllipsizer {
|
||||||
|
private static final int EXPANDED_LINES = Integer.MAX_VALUE;
|
||||||
|
private static final String ELLIPSIS = "…";
|
||||||
|
|
||||||
|
@NonNull private final CompositeDisposable disposable = new CompositeDisposable();
|
||||||
|
|
||||||
|
@NonNull private final TextView view;
|
||||||
|
private final int maxLines;
|
||||||
|
@NonNull private Description content;
|
||||||
|
@Nullable private StreamingService streamingService;
|
||||||
|
@Nullable private String streamUrl;
|
||||||
|
private boolean isEllipsized = false;
|
||||||
|
@Nullable private Boolean caBeEllipsized = null;
|
||||||
|
|
||||||
|
@NonNull private final Paint paintAtContentSize = new Paint();
|
||||||
|
private final float ellipsisWidthPx;
|
||||||
|
@Nullable private Consumer<Boolean> stateChangeListener = null;
|
||||||
|
@Nullable private Consumer<Boolean> onContentChanged;
|
||||||
|
|
||||||
|
public TextEllipsizer(@NonNull final TextView view,
|
||||||
|
final int maxLines,
|
||||||
|
@Nullable final StreamingService streamingService) {
|
||||||
|
this.view = view;
|
||||||
|
this.maxLines = maxLines;
|
||||||
|
this.streamingService = streamingService;
|
||||||
|
|
||||||
|
paintAtContentSize.setTextSize(view.getTextSize());
|
||||||
|
ellipsisWidthPx = paintAtContentSize.measureText(ELLIPSIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnContentChanged(@Nullable final Consumer<Boolean> onContentChanged) {
|
||||||
|
this.onContentChanged = onContentChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(@NonNull final Description content) {
|
||||||
|
this.content = content;
|
||||||
|
caBeEllipsized = null;
|
||||||
|
linkifyContentView(v -> {
|
||||||
|
final int currentMaxLines = view.getMaxLines();
|
||||||
|
view.setMaxLines(EXPANDED_LINES);
|
||||||
|
caBeEllipsized = view.getLineCount() > maxLines;
|
||||||
|
view.setMaxLines(currentMaxLines);
|
||||||
|
if (onContentChanged != null) {
|
||||||
|
onContentChanged.accept(caBeEllipsized);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStreamUrl(@Nullable final String streamUrl) {
|
||||||
|
this.streamUrl = streamUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStreamingService(@NonNull final StreamingService streamingService) {
|
||||||
|
this.streamingService = streamingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the {@link TextEllipsizer#content} to its full length.
|
||||||
|
*/
|
||||||
|
public void expand() {
|
||||||
|
view.setMaxLines(EXPANDED_LINES);
|
||||||
|
linkifyContentView(v -> isEllipsized = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorten the {@link TextEllipsizer#content} to the given number of
|
||||||
|
* {@link TextEllipsizer#maxLines maximum lines} and add trailing '{@code …}'
|
||||||
|
* if the text was shorted.
|
||||||
|
*/
|
||||||
|
public void ellipsize() {
|
||||||
|
// expand text to see whether it is necessary to ellipsize the text
|
||||||
|
view.setMaxLines(EXPANDED_LINES);
|
||||||
|
linkifyContentView(v -> {
|
||||||
|
final CharSequence charSeqText = view.getText();
|
||||||
|
if (charSeqText != null && view.getLineCount() > maxLines) {
|
||||||
|
// Note that converting to String removes spans (i.e. links), but that's something
|
||||||
|
// we actually want since when the text is ellipsized we want all clicks on the
|
||||||
|
// comment to expand the comment, not to open links.
|
||||||
|
final String text = charSeqText.toString();
|
||||||
|
|
||||||
|
final Layout layout = view.getLayout();
|
||||||
|
final float lineWidth = layout.getLineWidth(maxLines - 1);
|
||||||
|
final float layoutWidth = layout.getWidth();
|
||||||
|
final int lineStart = layout.getLineStart(maxLines - 1);
|
||||||
|
final int lineEnd = layout.getLineEnd(maxLines - 1);
|
||||||
|
|
||||||
|
// remove characters up until there is enough space for the ellipsis
|
||||||
|
// (also summing 2 more pixels, just to be sure to avoid float rounding errors)
|
||||||
|
int end = lineEnd;
|
||||||
|
float removedCharactersWidth = 0.0f;
|
||||||
|
while (lineWidth - removedCharactersWidth + ellipsisWidthPx + 2.0f > layoutWidth
|
||||||
|
&& end >= lineStart) {
|
||||||
|
end -= 1;
|
||||||
|
// recalculate each time to account for ligatures or other similar things
|
||||||
|
removedCharactersWidth = paintAtContentSize.measureText(
|
||||||
|
text.substring(end, lineEnd));
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove trailing spaces and newlines
|
||||||
|
while (end > 0 && Character.isWhitespace(text.charAt(end - 1))) {
|
||||||
|
end -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String newVal = text.substring(0, end) + ELLIPSIS;
|
||||||
|
view.setText(newVal);
|
||||||
|
isEllipsized = true;
|
||||||
|
} else {
|
||||||
|
isEllipsized = false;
|
||||||
|
}
|
||||||
|
view.setMaxLines(maxLines);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the view between the ellipsed and expanded state.
|
||||||
|
*/
|
||||||
|
public void toggle() {
|
||||||
|
if (isEllipsized) {
|
||||||
|
expand();
|
||||||
|
} else {
|
||||||
|
ellipsize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the {@link view} can be ellipsized.
|
||||||
|
* This is only the case when the {@link content} has more lines
|
||||||
|
* than allowed via {@link maxLines}.
|
||||||
|
* @return {@code true} if the {@link content} has more lines than allowed via {@link maxLines}
|
||||||
|
* and thus can be shortened, {@code false} if the {@code content} fits into the {@link view}
|
||||||
|
* without being shortened and {@code null} if the initialization is not completed yet.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Boolean canBeEllipsized() {
|
||||||
|
return caBeEllipsized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void linkifyContentView(final Consumer<View> consumer) {
|
||||||
|
final boolean oldState = isEllipsized;
|
||||||
|
disposable.clear();
|
||||||
|
TextLinkifier.fromDescription(view, content,
|
||||||
|
HtmlCompat.FROM_HTML_MODE_LEGACY, streamingService, streamUrl, disposable,
|
||||||
|
v -> {
|
||||||
|
consumer.accept(v);
|
||||||
|
notifyStateChangeListener(oldState);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a listener which is called when the given content is changed,
|
||||||
|
* either from <em>ellipsized</em> to <em>full</em> or vice versa.
|
||||||
|
* @param listener The listener to be called.
|
||||||
|
* The Boolean parameter is the new state.
|
||||||
|
* <em>Ellipsized</em> content is represented as {@code true},
|
||||||
|
* normal or <em>full</em> content by {@code false}.
|
||||||
|
*/
|
||||||
|
public void setStateChangeListener(final Consumer<Boolean> listener) {
|
||||||
|
this.stateChangeListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeStateChangeListener() {
|
||||||
|
this.stateChangeListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyStateChangeListener(final boolean oldState) {
|
||||||
|
if (oldState != isEllipsized && stateChangeListener != null) {
|
||||||
|
stateChangeListener.accept(isEllipsized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -87,12 +87,25 @@
|
|||||||
android:layout_below="@id/playlist_meta"
|
android:layout_below="@id/playlist_meta"
|
||||||
android:paddingHorizontal="@dimen/video_item_search_padding"
|
android:paddingHorizontal="@dimen/video_item_search_padding"
|
||||||
android:paddingTop="6dp"
|
android:paddingTop="6dp"
|
||||||
tools:text="This is a multiline playlist description. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit" />
|
android:maxLines="5"
|
||||||
|
tools:text="This is a multiline playlist description. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit" />
|
||||||
|
|
||||||
|
<org.schabi.newpipe.views.NewPipeTextView
|
||||||
|
android:id="@+id/playlist_description_read_more"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/playlist_description"
|
||||||
|
android:gravity="end"
|
||||||
|
android:paddingHorizontal="@dimen/video_item_search_padding"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:text="@string/show_more"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:textColor="?attr/colorPrimary" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/playlist_description">
|
android:layout_below="@id/playlist_description_read_more">
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/playlist_control"
|
android:id="@+id/playlist_control"
|
||||||
|
@ -842,4 +842,6 @@
|
|||||||
<item quantity="one">%s reply</item>
|
<item quantity="one">%s reply</item>
|
||||||
<item quantity="other">%s replies</item>
|
<item quantity="other">%s replies</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="show_more">Show more</string>
|
||||||
|
<string name="show_less">Show less</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user