Fix disposables handling for text linkifier
also use differently Markwon methods to convert plain text to markdown
This commit is contained in:
parent
eef418a757
commit
edfe0f9c30
6 changed files with 101 additions and 104 deletions
|
@ -31,7 +31,7 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
|
|
||||||
import static android.text.TextUtils.isEmpty;
|
import static android.text.TextUtils.isEmpty;
|
||||||
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
|
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
|
||||||
|
@ -41,8 +41,7 @@ public class DescriptionFragment extends BaseFragment {
|
||||||
|
|
||||||
@State
|
@State
|
||||||
StreamInfo streamInfo = null;
|
StreamInfo streamInfo = null;
|
||||||
@Nullable
|
final CompositeDisposable descriptionDisposables = new CompositeDisposable();
|
||||||
Disposable descriptionDisposable = null;
|
|
||||||
FragmentDescriptionBinding binding;
|
FragmentDescriptionBinding binding;
|
||||||
|
|
||||||
public DescriptionFragment() {
|
public DescriptionFragment() {
|
||||||
|
@ -67,10 +66,8 @@ public class DescriptionFragment extends BaseFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
|
descriptionDisposables.clear();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if (descriptionDisposable != null) {
|
|
||||||
descriptionDisposable.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,17 +130,17 @@ public class DescriptionFragment extends BaseFragment {
|
||||||
final Description description = streamInfo.getDescription();
|
final Description description = streamInfo.getDescription();
|
||||||
switch (description.getType()) {
|
switch (description.getType()) {
|
||||||
case Description.HTML:
|
case Description.HTML:
|
||||||
descriptionDisposable = TextLinkifier.createLinksFromHtmlBlock(
|
TextLinkifier.createLinksFromHtmlBlock(binding.detailDescriptionView,
|
||||||
binding.detailDescriptionView, description.getContent(),
|
description.getContent(), HtmlCompat.FROM_HTML_MODE_LEGACY, streamInfo,
|
||||||
HtmlCompat.FROM_HTML_MODE_LEGACY, streamInfo);
|
descriptionDisposables);
|
||||||
break;
|
break;
|
||||||
case Description.MARKDOWN:
|
case Description.MARKDOWN:
|
||||||
descriptionDisposable = TextLinkifier.createLinksFromMarkdownText(
|
TextLinkifier.createLinksFromMarkdownText(binding.detailDescriptionView,
|
||||||
binding.detailDescriptionView, description.getContent(), streamInfo);
|
description.getContent(), streamInfo, descriptionDisposables);
|
||||||
break;
|
break;
|
||||||
case Description.PLAIN_TEXT: default:
|
case Description.PLAIN_TEXT: default:
|
||||||
descriptionDisposable = TextLinkifier.createLinksFromPlainText(
|
TextLinkifier.createLinksFromPlainText(binding.detailDescriptionView,
|
||||||
binding.detailDescriptionView, description.getContent(), streamInfo);
|
description.getContent(), streamInfo, descriptionDisposables);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +195,8 @@ public class DescriptionFragment extends BaseFragment {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (linkifyContent) {
|
if (linkifyContent) {
|
||||||
TextLinkifier.createLinksFromPlainText(itemBinding.metadataContentView, content, null);
|
TextLinkifier.createLinksFromPlainText(itemBinding.metadataContentView, content, null,
|
||||||
|
descriptionDisposables);
|
||||||
} else {
|
} else {
|
||||||
itemBinding.metadataContentView.setText(content);
|
itemBinding.metadataContentView.setText(content);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1546,8 +1546,8 @@ public final class VideoDetailFragment
|
||||||
.getDefaultResolutionIndex(activity, sortedVideoStreams);
|
.getDefaultResolutionIndex(activity, sortedVideoStreams);
|
||||||
updateProgressInfo(info);
|
updateProgressInfo(info);
|
||||||
initThumbnailViews(info);
|
initThumbnailViews(info);
|
||||||
disposables.add(showMetaInfoInTextView(info.getMetaInfo(), binding.detailMetaInfoTextView,
|
showMetaInfoInTextView(info.getMetaInfo(), binding.detailMetaInfoTextView,
|
||||||
binding.detailMetaInfoSeparator));
|
binding.detailMetaInfoSeparator, disposables);
|
||||||
|
|
||||||
if (player == null || player.isStopped()) {
|
if (player == null || player.isStopped()) {
|
||||||
updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl());
|
updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl());
|
||||||
|
|
|
@ -278,8 +278,9 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
|
|
||||||
handleSearchSuggestion();
|
handleSearchSuggestion();
|
||||||
|
|
||||||
disposables.add(showMetaInfoInTextView(metaInfo == null ? null : Arrays.asList(metaInfo),
|
showMetaInfoInTextView(metaInfo == null ? null : Arrays.asList(metaInfo),
|
||||||
searchBinding.searchMetaInfoTextView, searchBinding.searchMetaInfoSeparator));
|
searchBinding.searchMetaInfoTextView, searchBinding.searchMetaInfoSeparator,
|
||||||
|
disposables);
|
||||||
|
|
||||||
if (TextUtils.isEmpty(searchString) || wasSearchFocused) {
|
if (TextUtils.isEmpty(searchString) || wasSearchFocused) {
|
||||||
showKeyboardSearch();
|
showKeyboardSearch();
|
||||||
|
@ -841,7 +842,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
infoListAdapter.clearStreamItemList();
|
infoListAdapter.clearStreamItemList();
|
||||||
hideSuggestionsPanel();
|
hideSuggestionsPanel();
|
||||||
showMetaInfoInTextView(null, searchBinding.searchMetaInfoTextView,
|
showMetaInfoInTextView(null, searchBinding.searchMetaInfoTextView,
|
||||||
searchBinding.searchMetaInfoSeparator);
|
searchBinding.searchMetaInfoSeparator, disposables);
|
||||||
hideKeyboardSearch();
|
hideKeyboardSearch();
|
||||||
|
|
||||||
disposables.add(historyRecordManager.onSearched(serviceId, theSearchString)
|
disposables.add(historyRecordManager.onSearched(serviceId, theSearchString)
|
||||||
|
@ -986,8 +987,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
// List<MetaInfo> cannot be bundled without creating some containers
|
// List<MetaInfo> cannot be bundled without creating some containers
|
||||||
metaInfo = new MetaInfo[result.getMetaInfo().size()];
|
metaInfo = new MetaInfo[result.getMetaInfo().size()];
|
||||||
metaInfo = result.getMetaInfo().toArray(metaInfo);
|
metaInfo = result.getMetaInfo().toArray(metaInfo);
|
||||||
disposables.add(showMetaInfoInTextView(result.getMetaInfo(),
|
showMetaInfoInTextView(result.getMetaInfo(), searchBinding.searchMetaInfoTextView,
|
||||||
searchBinding.searchMetaInfoTextView, searchBinding.searchMetaInfoSeparator));
|
searchBinding.searchMetaInfoSeparator, disposables);
|
||||||
|
|
||||||
handleSearchSuggestion();
|
handleSearchSuggestion();
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ import java.util.List;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.core.Maybe;
|
import io.reactivex.rxjava3.core.Maybe;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
|
|
||||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||||
|
|
||||||
|
@ -269,18 +269,19 @@ public final class ExtractorHelper {
|
||||||
* @param metaInfos a list of meta information, can be null or empty
|
* @param metaInfos a list of meta information, can be null or empty
|
||||||
* @param metaInfoTextView the text view in which to show the formatted HTML
|
* @param metaInfoTextView the text view in which to show the formatted HTML
|
||||||
* @param metaInfoSeparator another view to be shown or hidden accordingly to the text view
|
* @param metaInfoSeparator another view to be shown or hidden accordingly to the text view
|
||||||
* @return a disposable to be stored somewhere and disposed when activity/fragment is destroyed
|
* @param disposables disposables created by the method are added here and their lifecycle
|
||||||
|
* should be handled by the calling class
|
||||||
*/
|
*/
|
||||||
public static Disposable showMetaInfoInTextView(@Nullable final List<MetaInfo> metaInfos,
|
public static void showMetaInfoInTextView(@Nullable final List<MetaInfo> metaInfos,
|
||||||
final TextView metaInfoTextView,
|
final TextView metaInfoTextView,
|
||||||
final View metaInfoSeparator) {
|
final View metaInfoSeparator,
|
||||||
|
final CompositeDisposable disposables) {
|
||||||
final Context context = metaInfoTextView.getContext();
|
final Context context = metaInfoTextView.getContext();
|
||||||
if (metaInfos == null || metaInfos.isEmpty()
|
if (metaInfos == null || metaInfos.isEmpty()
|
||||||
|| !PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
|
|| !PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
|
||||||
context.getString(R.string.show_meta_info_key), true)) {
|
context.getString(R.string.show_meta_info_key), true)) {
|
||||||
metaInfoTextView.setVisibility(View.GONE);
|
metaInfoTextView.setVisibility(View.GONE);
|
||||||
metaInfoSeparator.setVisibility(View.GONE);
|
metaInfoSeparator.setVisibility(View.GONE);
|
||||||
return Disposable.empty();
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
final StringBuilder stringBuilder = new StringBuilder();
|
final StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
@ -311,9 +312,8 @@ public final class ExtractorHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
metaInfoSeparator.setVisibility(View.VISIBLE);
|
metaInfoSeparator.setVisibility(View.VISIBLE);
|
||||||
return TextLinkifier.createLinksFromHtmlBlock(metaInfoTextView,
|
TextLinkifier.createLinksFromHtmlBlock(metaInfoTextView, stringBuilder.toString(),
|
||||||
stringBuilder.toString(), HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING,
|
HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING, null, disposables);
|
||||||
null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,6 @@ import io.reactivex.rxjava3.core.Single;
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
|
||||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
|
||||||
|
|
||||||
public final class InternalUrlsHandler {
|
public final class InternalUrlsHandler {
|
||||||
private static final Pattern AMPERSAND_TIMESTAMP_PATTERN = Pattern.compile("(.*)&t=(\\d+)");
|
private static final Pattern AMPERSAND_TIMESTAMP_PATTERN = Pattern.compile("(.*)&t=(\\d+)");
|
||||||
private static final Pattern HASHTAG_TIMESTAMP_PATTERN =
|
private static final Pattern HASHTAG_TIMESTAMP_PATTERN =
|
||||||
|
@ -50,7 +48,7 @@ public final class InternalUrlsHandler {
|
||||||
disposables,
|
disposables,
|
||||||
final Context context,
|
final Context context,
|
||||||
@NonNull final String url) {
|
@NonNull final String url) {
|
||||||
return handleUrl(disposables, context, url, HASHTAG_TIMESTAMP_PATTERN);
|
return handleUrl(context, url, HASHTAG_TIMESTAMP_PATTERN, disposables);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +68,7 @@ public final class InternalUrlsHandler {
|
||||||
disposables,
|
disposables,
|
||||||
final Context context,
|
final Context context,
|
||||||
@NonNull final String url) {
|
@NonNull final String url) {
|
||||||
return handleUrl(disposables, context, url, AMPERSAND_TIMESTAMP_PATTERN);
|
return handleUrl(context, url, AMPERSAND_TIMESTAMP_PATTERN, disposables);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,42 +78,37 @@ public final class InternalUrlsHandler {
|
||||||
* service URL with a timestamp, the popup player will be opened and true will be returned;
|
* service URL with a timestamp, the popup player will be opened and true will be returned;
|
||||||
* else, false will be returned.
|
* else, false will be returned.
|
||||||
*
|
*
|
||||||
* @param disposables a field of the Activity/Fragment class that calls this method
|
|
||||||
* @param context the context to use
|
* @param context the context to use
|
||||||
* @param url the URL to check if it can be handled
|
* @param url the URL to check if it can be handled
|
||||||
* @param pattern the pattern to use
|
* @param pattern the pattern to use
|
||||||
|
* @param disposables a field of the Activity/Fragment class that calls this method
|
||||||
* @return true if the URL can be handled by NewPipe, false if it cannot
|
* @return true if the URL can be handled by NewPipe, false if it cannot
|
||||||
*/
|
*/
|
||||||
private static boolean handleUrl(@NonNull final CompositeDisposable disposables,
|
private static boolean handleUrl(final Context context,
|
||||||
final Context context,
|
|
||||||
@NonNull final String url,
|
@NonNull final String url,
|
||||||
@NonNull final Pattern pattern) {
|
@NonNull final Pattern pattern,
|
||||||
final String matchedUrl;
|
@NonNull final CompositeDisposable disposables) {
|
||||||
|
final Matcher matcher = pattern.matcher(url);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final String matchedUrl = matcher.group(1);
|
||||||
|
final int seconds = Integer.parseInt(matcher.group(2));
|
||||||
|
|
||||||
final StreamingService service;
|
final StreamingService service;
|
||||||
final StreamingService.LinkType linkType;
|
final StreamingService.LinkType linkType;
|
||||||
final int seconds;
|
|
||||||
final Matcher matcher = pattern.matcher(url);
|
|
||||||
if (matcher.matches()) {
|
|
||||||
matchedUrl = matcher.group(1);
|
|
||||||
seconds = Integer.parseInt(matcher.group(2));
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNullOrEmpty(matchedUrl)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
service = NewPipe.getServiceByUrl(matchedUrl);
|
service = NewPipe.getServiceByUrl(matchedUrl);
|
||||||
linkType = service.getLinkTypeByUrl(matchedUrl);
|
linkType = service.getLinkTypeByUrl(matchedUrl);
|
||||||
|
if (linkType == StreamingService.LinkType.NONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} catch (final ExtractionException e) {
|
} catch (final ExtractionException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (linkType == StreamingService.LinkType.NONE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (linkType == StreamingService.LinkType.STREAM && seconds != -1) {
|
if (linkType == StreamingService.LinkType.STREAM && seconds != -1) {
|
||||||
return playOnPopup(disposables, context, matchedUrl, service, seconds);
|
return playOnPopup(context, matchedUrl, service, seconds, disposables);
|
||||||
} else {
|
} else {
|
||||||
NavigationHelper.openRouterActivity(context, matchedUrl);
|
NavigationHelper.openRouterActivity(context, matchedUrl);
|
||||||
return true;
|
return true;
|
||||||
|
@ -125,18 +118,19 @@ public final class InternalUrlsHandler {
|
||||||
/**
|
/**
|
||||||
* Play a content in the floating player.
|
* Play a content in the floating player.
|
||||||
*
|
*
|
||||||
* @param disposables a field of the Activity/Fragment class that calls this method
|
|
||||||
* @param context the context to be used
|
* @param context the context to be used
|
||||||
* @param url the URL of the content
|
* @param url the URL of the content
|
||||||
* @param service the service of the content
|
* @param service the service of the content
|
||||||
* @param seconds the position in seconds at which the floating player will start
|
* @param seconds the position in seconds at which the floating player will start
|
||||||
|
* @param disposables disposables created by the method are added here and their lifecycle
|
||||||
|
* should be handled by the calling class
|
||||||
* @return true if the playback of the content has successfully started or false if not
|
* @return true if the playback of the content has successfully started or false if not
|
||||||
*/
|
*/
|
||||||
public static boolean playOnPopup(@NonNull final CompositeDisposable disposables,
|
public static boolean playOnPopup(final Context context,
|
||||||
final Context context,
|
|
||||||
final String url,
|
final String url,
|
||||||
@NonNull final StreamingService service,
|
@NonNull final StreamingService service,
|
||||||
final int seconds) {
|
final int seconds,
|
||||||
|
@NonNull final CompositeDisposable disposables) {
|
||||||
final LinkHandlerFactory factory = service.getStreamLHFactory();
|
final LinkHandlerFactory factory = service.getStreamLHFactory();
|
||||||
final String cleanUrl;
|
final String cleanUrl;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import io.noties.markwon.linkify.LinkifyPlugin;
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
|
||||||
import static org.schabi.newpipe.util.external_communication.InternalUrlsHandler.playOnPopup;
|
import static org.schabi.newpipe.util.external_communication.InternalUrlsHandler.playOnPopup;
|
||||||
|
@ -42,9 +41,9 @@ public final class TextLinkifier {
|
||||||
/**
|
/**
|
||||||
* Create web links for contents with an HTML description.
|
* Create web links for contents with an HTML description.
|
||||||
* <p>
|
* <p>
|
||||||
* This will call
|
* This will call {@link TextLinkifier#changeIntentsOfDescriptionLinks(TextView, CharSequence,
|
||||||
* {@link TextLinkifier#changeIntentsOfDescriptionLinks(TextView, CharSequence, Info)}
|
* Info, CompositeDisposable)} after having linked the URLs with
|
||||||
* after having linked the URLs with {@link HtmlCompat#fromHtml(String, int)}.
|
* {@link HtmlCompat#fromHtml(String, int)}.
|
||||||
*
|
*
|
||||||
* @param textView the TextView to set the htmlBlock linked
|
* @param textView the TextView to set the htmlBlock linked
|
||||||
* @param htmlBlock the htmlBlock to be linked
|
* @param htmlBlock the htmlBlock to be linked
|
||||||
|
@ -53,23 +52,24 @@ public final class TextLinkifier {
|
||||||
* @param relatedInfo if given, handle timestamps to open the stream in the popup player at
|
* @param relatedInfo if given, handle timestamps to open the stream in the popup player at
|
||||||
* the specific time, and hashtags to search for the term in the correct
|
* the specific time, and hashtags to search for the term in the correct
|
||||||
* service
|
* service
|
||||||
* @return a disposable to be stored somewhere and disposed when activity/fragment is destroyed
|
* @param disposables disposables created by the method are added here and their lifecycle
|
||||||
|
* should be handled by the calling class
|
||||||
*/
|
*/
|
||||||
@NonNull
|
public static void createLinksFromHtmlBlock(@NonNull final TextView textView,
|
||||||
public static Disposable createLinksFromHtmlBlock(@NonNull final TextView textView,
|
final String htmlBlock,
|
||||||
final String htmlBlock,
|
final int htmlCompatFlag,
|
||||||
final int htmlCompatFlag,
|
@Nullable final Info relatedInfo,
|
||||||
@Nullable final Info relatedInfo) {
|
final CompositeDisposable disposables) {
|
||||||
return changeIntentsOfDescriptionLinks(
|
changeIntentsOfDescriptionLinks(
|
||||||
textView, HtmlCompat.fromHtml(htmlBlock, htmlCompatFlag), relatedInfo);
|
textView, HtmlCompat.fromHtml(htmlBlock, htmlCompatFlag), relatedInfo, disposables);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create web links for contents with a plain text description.
|
* Create web links for contents with a plain text description.
|
||||||
* <p>
|
* <p>
|
||||||
* This will call
|
* This will call {@link TextLinkifier#changeIntentsOfDescriptionLinks(TextView, CharSequence,
|
||||||
* {@link TextLinkifier#changeIntentsOfDescriptionLinks(TextView, CharSequence, Info)}
|
* Info, CompositeDisposable)} after having linked the URLs with
|
||||||
* after having linked the URLs with {@link TextView#setAutoLinkMask(int)} and
|
* {@link TextView#setAutoLinkMask(int)} and
|
||||||
* {@link TextView#setText(CharSequence, TextView.BufferType)}.
|
* {@link TextView#setText(CharSequence, TextView.BufferType)}.
|
||||||
*
|
*
|
||||||
* @param textView the TextView to set the plain text block linked
|
* @param textView the TextView to set the plain text block linked
|
||||||
|
@ -77,40 +77,40 @@ public final class TextLinkifier {
|
||||||
* @param relatedInfo if given, handle timestamps to open the stream in the popup player at
|
* @param relatedInfo if given, handle timestamps to open the stream in the popup player at
|
||||||
* the specific time, and hashtags to search for the term in the correct
|
* the specific time, and hashtags to search for the term in the correct
|
||||||
* service
|
* service
|
||||||
* @return a disposable to be stored somewhere and disposed when activity/fragment is destroyed
|
* @param disposables disposables created by the method are added here and their lifecycle
|
||||||
|
* should be handled by the calling class
|
||||||
*/
|
*/
|
||||||
@NonNull
|
public static void createLinksFromPlainText(@NonNull final TextView textView,
|
||||||
public static Disposable createLinksFromPlainText(@NonNull final TextView textView,
|
final String plainTextBlock,
|
||||||
final String plainTextBlock,
|
@Nullable final Info relatedInfo,
|
||||||
@Nullable final Info relatedInfo) {
|
final CompositeDisposable disposables) {
|
||||||
textView.setAutoLinkMask(Linkify.WEB_URLS);
|
textView.setAutoLinkMask(Linkify.WEB_URLS);
|
||||||
textView.setText(plainTextBlock, TextView.BufferType.SPANNABLE);
|
textView.setText(plainTextBlock, TextView.BufferType.SPANNABLE);
|
||||||
return changeIntentsOfDescriptionLinks(textView, textView.getText(), relatedInfo);
|
changeIntentsOfDescriptionLinks(textView, textView.getText(), relatedInfo, disposables);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create web links for contents with a markdown description.
|
* Create web links for contents with a markdown description.
|
||||||
* <p>
|
* <p>
|
||||||
* This will call
|
* This will call {@link TextLinkifier#changeIntentsOfDescriptionLinks(TextView, CharSequence,
|
||||||
* {@link TextLinkifier#changeIntentsOfDescriptionLinks(TextView, CharSequence, Info)}
|
* Info, CompositeDisposable)} after creating an {@link Markwon} object and using
|
||||||
* after creating an {@link Markwon} object and using
|
|
||||||
* {@link Markwon#setMarkdown(TextView, String)}.
|
* {@link Markwon#setMarkdown(TextView, String)}.
|
||||||
*
|
*
|
||||||
* @param textView the TextView to set the plain text block linked
|
* @param textView the TextView to set the plain text block linked
|
||||||
* @param markdownBlock the block of markdown text to be linked
|
* @param markdownBlock the block of markdown text to be linked
|
||||||
* @param relatedInfo if given, handle timestamps to open the stream in the popup player at
|
* @param relatedInfo if given, handle timestamps to open the stream in the popup player at
|
||||||
* the specific time, and hashtags to search for the term in the correct
|
* the specific time, and hashtags to search for the term in the correct
|
||||||
* service
|
* @param disposables disposables created by the method are added here and their lifecycle
|
||||||
* @return a disposable to be stored somewhere and disposed when activity/fragment is destroyed
|
* should be handled by the calling class
|
||||||
*/
|
*/
|
||||||
@NonNull
|
public static void createLinksFromMarkdownText(@NonNull final TextView textView,
|
||||||
public static Disposable createLinksFromMarkdownText(@NonNull final TextView textView,
|
final String markdownBlock,
|
||||||
final String markdownBlock,
|
@Nullable final Info relatedInfo,
|
||||||
@Nullable final Info relatedInfo) {
|
final CompositeDisposable disposables) {
|
||||||
final Markwon markwon = Markwon.builder(textView.getContext())
|
final Markwon markwon = Markwon.builder(textView.getContext())
|
||||||
.usePlugin(LinkifyPlugin.create()).build();
|
.usePlugin(LinkifyPlugin.create()).build();
|
||||||
markwon.setMarkdown(textView, markdownBlock);
|
changeIntentsOfDescriptionLinks(textView, markwon.toMarkdown(markdownBlock), relatedInfo,
|
||||||
return changeIntentsOfDescriptionLinks(textView, textView.getText(), relatedInfo);
|
disposables);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,11 +164,14 @@ public final class TextLinkifier {
|
||||||
* @param spannableDescription the SpannableStringBuilder with the text of the
|
* @param spannableDescription the SpannableStringBuilder with the text of the
|
||||||
* content description
|
* content description
|
||||||
* @param relatedInfo what to open in the popup player when timestamps are clicked
|
* @param relatedInfo what to open in the popup player when timestamps are clicked
|
||||||
|
* @param disposables disposables created by the method are added here and their
|
||||||
|
* lifecycle should be handled by the calling class
|
||||||
*/
|
*/
|
||||||
private static void addClickListenersOnTimestamps(final Context context,
|
private static void addClickListenersOnTimestamps(final Context context,
|
||||||
@NonNull final SpannableStringBuilder
|
@NonNull final SpannableStringBuilder
|
||||||
spannableDescription,
|
spannableDescription,
|
||||||
final Info relatedInfo) {
|
final Info relatedInfo,
|
||||||
|
final CompositeDisposable disposables) {
|
||||||
final String descriptionText = spannableDescription.toString();
|
final String descriptionText = spannableDescription.toString();
|
||||||
final Matcher timestampsMatches = TIMESTAMPS_PATTERN.matcher(descriptionText);
|
final Matcher timestampsMatches = TIMESTAMPS_PATTERN.matcher(descriptionText);
|
||||||
|
|
||||||
|
@ -193,8 +196,8 @@ public final class TextLinkifier {
|
||||||
spannableDescription.setSpan(new ClickableSpan() {
|
spannableDescription.setSpan(new ClickableSpan() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(@NonNull final View view) {
|
public void onClick(@NonNull final View view) {
|
||||||
playOnPopup(new CompositeDisposable(), context, relatedInfo.getUrl(),
|
playOnPopup(context, relatedInfo.getUrl(), relatedInfo.getService(), seconds,
|
||||||
relatedInfo.getService(), seconds);
|
disposables);
|
||||||
}
|
}
|
||||||
}, timestampStart, timestampEnd, 0);
|
}, timestampStart, timestampEnd, 0);
|
||||||
}
|
}
|
||||||
|
@ -209,8 +212,8 @@ public final class TextLinkifier {
|
||||||
* with {@link ShareUtils#openUrlInBrowser(Context, String, boolean)}.
|
* with {@link ShareUtils#openUrlInBrowser(Context, String, boolean)}.
|
||||||
* This method will also add click listeners on timestamps in this description, which will play
|
* This method will also add click listeners on timestamps in this description, which will play
|
||||||
* the content in the popup player at the time indicated in the timestamp, by using
|
* the content in the popup player at the time indicated in the timestamp, by using
|
||||||
* {@link TextLinkifier#addClickListenersOnTimestamps(Context, SpannableStringBuilder, Info)}
|
* {@link TextLinkifier#addClickListenersOnTimestamps(Context, SpannableStringBuilder, Info,
|
||||||
* method and click listeners on hashtags, by using
|
* CompositeDisposable)} method and click listeners on hashtags, by using
|
||||||
* {@link TextLinkifier#addClickListenersOnHashtags(Context, SpannableStringBuilder, Info)},
|
* {@link TextLinkifier#addClickListenersOnHashtags(Context, SpannableStringBuilder, Info)},
|
||||||
* which will open a search on the current service with the hashtag.
|
* which will open a search on the current service with the hashtag.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -222,13 +225,14 @@ public final class TextLinkifier {
|
||||||
* @param relatedInfo if given, handle timestamps to open the stream in the popup player at
|
* @param relatedInfo if given, handle timestamps to open the stream in the popup player at
|
||||||
* the specific time, and hashtags to search for the term in the correct
|
* the specific time, and hashtags to search for the term in the correct
|
||||||
* service
|
* service
|
||||||
* @return a disposable to be stored somewhere and disposed when activity/fragment is destroyed
|
* @param disposables disposables created by the method are added here and their lifecycle
|
||||||
|
* should be handled by the calling class
|
||||||
*/
|
*/
|
||||||
@NonNull
|
private static void changeIntentsOfDescriptionLinks(final TextView textView,
|
||||||
private static Disposable changeIntentsOfDescriptionLinks(final TextView textView,
|
final CharSequence chars,
|
||||||
final CharSequence chars,
|
@Nullable final Info relatedInfo,
|
||||||
@Nullable final Info relatedInfo) {
|
final CompositeDisposable disposables) {
|
||||||
return Single.fromCallable(() -> {
|
disposables.add(Single.fromCallable(() -> {
|
||||||
final Context context = textView.getContext();
|
final Context context = textView.getContext();
|
||||||
|
|
||||||
// add custom click actions on web links
|
// add custom click actions on web links
|
||||||
|
@ -254,7 +258,7 @@ public final class TextLinkifier {
|
||||||
// add click actions on plain text timestamps only for description of contents,
|
// add click actions on plain text timestamps only for description of contents,
|
||||||
// unneeded for meta-info or other TextViews
|
// unneeded for meta-info or other TextViews
|
||||||
if (relatedInfo != null) {
|
if (relatedInfo != null) {
|
||||||
addClickListenersOnTimestamps(context, textBlockLinked, relatedInfo);
|
addClickListenersOnTimestamps(context, textBlockLinked, relatedInfo, disposables);
|
||||||
addClickListenersOnHashtags(context, textBlockLinked, relatedInfo);
|
addClickListenersOnHashtags(context, textBlockLinked, relatedInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +271,7 @@ public final class TextLinkifier {
|
||||||
Log.e(TAG, "Unable to linkify text", throwable);
|
Log.e(TAG, "Unable to linkify text", throwable);
|
||||||
// this should never happen, but if it does, just fallback to it
|
// this should never happen, but if it does, just fallback to it
|
||||||
setTextViewCharSequence(textView, chars);
|
setTextViewCharSequence(textView, chars);
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setTextViewCharSequence(@NonNull final TextView textView,
|
private static void setTextViewCharSequence(@NonNull final TextView textView,
|
||||||
|
|
Loading…
Add table
Reference in a new issue