Skip to content

Commit 65eb631

Browse files
TobiGrStypox
authored andcommitted
Ellipsize playlist description if it is longer than 5 lines
The description can be expanded / collapsed via a "show more" / "show less" button.
1 parent 6c99557 commit 65eb631

5 files changed

Lines changed: 249 additions & 118 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
44
import static org.schabi.newpipe.ktx.ViewUtils.animate;
55
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
6-
import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD;
6+
import static org.schabi.newpipe.util.ServiceHelper.getServiceById;
77

88
import android.content.Context;
99
import android.os.Bundle;
@@ -19,7 +19,6 @@
1919
import androidx.annotation.NonNull;
2020
import androidx.annotation.Nullable;
2121
import androidx.appcompat.content.res.AppCompatResources;
22-
import androidx.core.text.HtmlCompat;
2322

2423
import com.google.android.material.shape.CornerFamily;
2524
import com.google.android.material.shape.ShapeAppearanceModel;
@@ -52,10 +51,10 @@
5251
import org.schabi.newpipe.util.ExtractorHelper;
5352
import org.schabi.newpipe.util.Localization;
5453
import org.schabi.newpipe.util.NavigationHelper;
55-
import org.schabi.newpipe.util.image.PicassoHelper;
56-
import org.schabi.newpipe.util.external_communication.ShareUtils;
5754
import org.schabi.newpipe.util.PlayButtonHelper;
58-
import org.schabi.newpipe.util.text.TextLinkifier;
55+
import org.schabi.newpipe.util.external_communication.ShareUtils;
56+
import org.schabi.newpipe.util.image.PicassoHelper;
57+
import org.schabi.newpipe.util.text.TextEllipsizer;
5958

6059
import java.util.ArrayList;
6160
import java.util.List;
@@ -329,13 +328,24 @@ public void handleResult(@NonNull final PlaylistInfo result) {
329328
final Description description = result.getDescription();
330329
if (description != null && description != Description.EMPTY_DESCRIPTION
331330
&& !isBlank(description.getContent())) {
332-
TextLinkifier.fromDescription(headerBinding.playlistDescription,
333-
description, HtmlCompat.FROM_HTML_MODE_LEGACY,
334-
result.getService(), result.getUrl(),
335-
disposables, SET_LINK_MOVEMENT_METHOD);
336-
headerBinding.playlistDescription.setVisibility(View.VISIBLE);
331+
final TextEllipsizer ellipsizer = new TextEllipsizer(
332+
headerBinding.playlistDescription, 5, getServiceById(result.getServiceId()));
333+
ellipsizer.setStateChangeListener(isEllipsized ->
334+
headerBinding.playlistDescriptionReadMore.setText(
335+
Boolean.TRUE.equals(isEllipsized) ? R.string.show_more : R.string.show_less
336+
));
337+
ellipsizer.setOnContentChanged(canBeEllipsized -> {
338+
headerBinding.playlistDescriptionReadMore.setVisibility(
339+
Boolean.TRUE.equals(canBeEllipsized) ? View.VISIBLE : View.GONE);
340+
if (Boolean.TRUE.equals(canBeEllipsized)) {
341+
ellipsizer.ellipsize();
342+
}
343+
});
344+
ellipsizer.setContent(description);
345+
headerBinding.playlistDescriptionReadMore.setOnClickListener(v -> ellipsizer.toggle());
337346
} else {
338347
headerBinding.playlistDescription.setVisibility(View.GONE);
348+
headerBinding.playlistDescriptionReadMore.setVisibility(View.GONE);
339349
}
340350

341351
if (!result.getErrors().isEmpty()) {
Lines changed: 17 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package org.schabi.newpipe.info_list.holder;
22

3-
import static android.text.TextUtils.isEmpty;
43
import static org.schabi.newpipe.util.ServiceHelper.getServiceById;
54

6-
import android.graphics.Paint;
7-
import android.text.Layout;
85
import android.text.method.LinkMovementMethod;
96
import android.text.style.URLSpan;
107
import android.view.View;
@@ -15,42 +12,28 @@
1512
import android.widget.TextView;
1613

1714
import androidx.annotation.NonNull;
18-
import androidx.annotation.Nullable;
19-
import androidx.core.text.HtmlCompat;
2015
import androidx.fragment.app.FragmentActivity;
2116

2217
import org.schabi.newpipe.R;
2318
import org.schabi.newpipe.extractor.InfoItem;
24-
import org.schabi.newpipe.extractor.StreamingService;
2519
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
26-
import org.schabi.newpipe.extractor.stream.Description;
2720
import org.schabi.newpipe.info_list.InfoItemBuilder;
2821
import org.schabi.newpipe.local.history.HistoryRecordManager;
2922
import org.schabi.newpipe.util.DeviceUtils;
3023
import org.schabi.newpipe.util.Localization;
3124
import org.schabi.newpipe.util.NavigationHelper;
25+
import org.schabi.newpipe.util.external_communication.ShareUtils;
3226
import org.schabi.newpipe.util.image.ImageStrategy;
3327
import org.schabi.newpipe.util.image.PicassoHelper;
34-
import org.schabi.newpipe.util.external_communication.ShareUtils;
3528
import org.schabi.newpipe.util.text.CommentTextOnTouchListener;
36-
import org.schabi.newpipe.util.text.TextLinkifier;
37-
38-
import java.util.function.Consumer;
39-
40-
import io.reactivex.rxjava3.disposables.CompositeDisposable;
29+
import org.schabi.newpipe.util.text.TextEllipsizer;
4130

4231
public class CommentInfoItemHolder extends InfoItemHolder {
43-
private static final String ELLIPSIS = "…";
4432

4533
private static final int COMMENT_DEFAULT_LINES = 2;
46-
private static final int COMMENT_EXPANDED_LINES = 1000;
47-
4834
private final int commentHorizontalPadding;
4935
private final int commentVerticalPadding;
5036

51-
private final Paint paintAtContentSize;
52-
private final float ellipsisWidthPx;
53-
5437
private final RelativeLayout itemRoot;
5538
private final ImageView itemThumbnailView;
5639
private final TextView itemContentView;
@@ -61,13 +44,8 @@ public class CommentInfoItemHolder extends InfoItemHolder {
6144
private final ImageView itemPinnedView;
6245
private final Button repliesButton;
6346

64-
private final CompositeDisposable disposables = new CompositeDisposable();
65-
@Nullable
66-
private Description commentText;
67-
@Nullable
68-
private StreamingService streamService;
69-
@Nullable
70-
private String streamUrl;
47+
@NonNull
48+
private final TextEllipsizer textEllipsizer;
7149

7250
public CommentInfoItemHolder(final InfoItemBuilder infoItemBuilder,
7351
final ViewGroup parent) {
@@ -88,9 +66,14 @@ public CommentInfoItemHolder(final InfoItemBuilder infoItemBuilder,
8866
commentVerticalPadding = (int) infoItemBuilder.getContext()
8967
.getResources().getDimension(R.dimen.comments_vertical_padding);
9068

91-
paintAtContentSize = new Paint();
92-
paintAtContentSize.setTextSize(itemContentView.getTextSize());
93-
ellipsisWidthPx = paintAtContentSize.measureText(ELLIPSIS);
69+
textEllipsizer = new TextEllipsizer(itemContentView, COMMENT_DEFAULT_LINES, null);
70+
textEllipsizer.setStateChangeListener(isEllipsized -> {
71+
if (Boolean.TRUE.equals(isEllipsized)) {
72+
denyLinkFocus();
73+
} else {
74+
determineMovementMethod();
75+
}
76+
});
9477
}
9578

9679
@Override
@@ -139,16 +122,16 @@ public void updateFromItem(final InfoItem infoItem,
139122

140123

141124
// setup comment content and click listeners to expand/ellipsize it
142-
streamService = getServiceById(item.getServiceId());
143-
streamUrl = item.getUrl();
144-
commentText = item.getCommentText();
145-
ellipsize();
125+
textEllipsizer.setStreamingService(getServiceById(item.getServiceId()));
126+
textEllipsizer.setStreamUrl(item.getUrl());
127+
textEllipsizer.setContent(item.getCommentText());
128+
textEllipsizer.ellipsize();
146129

147130
//noinspection ClickableViewAccessibility
148131
itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE);
149132

150133
itemView.setOnClickListener(view -> {
151-
toggleEllipsize();
134+
textEllipsizer.toggle();
152135
if (itemBuilder.getOnCommentsSelectedListener() != null) {
153136
itemBuilder.getOnCommentsSelectedListener().selected(item);
154137
}
@@ -202,76 +185,4 @@ private void determineMovementMethod() {
202185
denyLinkFocus();
203186
}
204187
}
205-
206-
private void ellipsize() {
207-
itemContentView.setMaxLines(COMMENT_EXPANDED_LINES);
208-
linkifyCommentContentView(v -> {
209-
boolean hasEllipsis = false;
210-
211-
final CharSequence charSeqText = itemContentView.getText();
212-
if (charSeqText != null && itemContentView.getLineCount() > COMMENT_DEFAULT_LINES) {
213-
// Note that converting to String removes spans (i.e. links), but that's something
214-
// we actually want since when the text is ellipsized we want all clicks on the
215-
// comment to expand the comment, not to open links.
216-
final String text = charSeqText.toString();
217-
218-
final Layout layout = itemContentView.getLayout();
219-
final float lineWidth = layout.getLineWidth(COMMENT_DEFAULT_LINES - 1);
220-
final float layoutWidth = layout.getWidth();
221-
final int lineStart = layout.getLineStart(COMMENT_DEFAULT_LINES - 1);
222-
final int lineEnd = layout.getLineEnd(COMMENT_DEFAULT_LINES - 1);
223-
224-
// remove characters up until there is enough space for the ellipsis
225-
// (also summing 2 more pixels, just to be sure to avoid float rounding errors)
226-
int end = lineEnd;
227-
float removedCharactersWidth = 0.0f;
228-
while (lineWidth - removedCharactersWidth + ellipsisWidthPx + 2.0f > layoutWidth
229-
&& end >= lineStart) {
230-
end -= 1;
231-
// recalculate each time to account for ligatures or other similar things
232-
removedCharactersWidth = paintAtContentSize.measureText(
233-
text.substring(end, lineEnd));
234-
}
235-
236-
// remove trailing spaces and newlines
237-
while (end > 0 && Character.isWhitespace(text.charAt(end - 1))) {
238-
end -= 1;
239-
}
240-
241-
final String newVal = text.substring(0, end) + ELLIPSIS;
242-
itemContentView.setText(newVal);
243-
hasEllipsis = true;
244-
}
245-
246-
itemContentView.setMaxLines(COMMENT_DEFAULT_LINES);
247-
if (hasEllipsis) {
248-
denyLinkFocus();
249-
} else {
250-
determineMovementMethod();
251-
}
252-
});
253-
}
254-
255-
private void toggleEllipsize() {
256-
final CharSequence text = itemContentView.getText();
257-
if (!isEmpty(text) && text.charAt(text.length() - 1) == ELLIPSIS.charAt(0)) {
258-
expand();
259-
} else if (itemContentView.getLineCount() > COMMENT_DEFAULT_LINES) {
260-
ellipsize();
261-
}
262-
}
263-
264-
private void expand() {
265-
itemContentView.setMaxLines(COMMENT_EXPANDED_LINES);
266-
linkifyCommentContentView(v -> determineMovementMethod());
267-
}
268-
269-
private void linkifyCommentContentView(@Nullable final Consumer<TextView> onCompletion) {
270-
disposables.clear();
271-
if (commentText != null) {
272-
TextLinkifier.fromDescription(itemContentView, commentText,
273-
HtmlCompat.FROM_HTML_MODE_LEGACY, streamService, streamUrl, disposables,
274-
onCompletion);
275-
}
276-
}
277188
}

0 commit comments

Comments
 (0)