From 0a831ec84e7a3c822ce1822d97bc9b934a510f33 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Tue, 15 Dec 2020 17:41:21 +0100 Subject: [PATCH 1/2] Display meta info about search query, stream creator or topic Closes #4614 --- app/build.gradle | 2 +- .../newpipe/fragments/MainFragment.java | 2 +- .../fragments/detail/VideoDetailFragment.java | 77 +++++++++++++++---- .../fragments/list/search/SearchFragment.java | 48 ++++++++++++ .../fragment_video_detail.xml | 32 ++++++++ app/src/main/res/layout/fragment_search.xml | 14 +++- .../main/res/layout/fragment_video_detail.xml | 33 ++++++++ app/src/main/res/values/settings_keys.xml | 1 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/content_settings.xml | 7 ++ 10 files changed, 200 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 15561b2d2..fadfc3221 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -179,7 +179,7 @@ dependencies { // NewPipe dependencies // You can use a local version by uncommenting a few lines in settings.gradle - implementation 'com.github.TeamNewPipe:NewPipeExtractor:85fa006214b003f21eacb76c445a167732f19981' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:79b5aa9760da52020821b68e2af41a9238943304' implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" implementation "org.jsoup:jsoup:1.13.1" diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 866b324ec..a77109f86 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -3,7 +3,6 @@ package org.schabi.newpipe.fragments; import android.content.Context; import android.content.res.ColorStateList; import android.os.Bundle; -import androidx.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -19,6 +18,7 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapterMenuWorkaround; +import androidx.preference.PreferenceManager; import androidx.viewpager.widget.ViewPager; import com.google.android.material.tabs.TabLayout; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 427cff06e..b9cb4ab2b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -16,7 +16,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.provider.Settings; -import android.text.TextUtils; +import android.text.method.LinkMovementMethod; import android.text.util.Linkify; import android.util.DisplayMetrics; import android.util.Log; @@ -63,6 +63,7 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.download.DownloadDialog; import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -122,8 +123,10 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; +import static android.text.TextUtils.isEmpty; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS; import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked; import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired; import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; @@ -218,6 +221,10 @@ public final class VideoDetailFragment private TextView detailDurationView; private TextView detailPositionView; + private LinearLayout detailMetadataInfo; + private View detailMetadataInfoSeparator; + private TextView detailMetadataInfoText; + private LinearLayout videoDescriptionRootLayout; private TextView videoUploadDateView; private TextView videoDescriptionView; @@ -508,8 +515,8 @@ public final class VideoDetailFragment } break; case R.id.detail_uploader_root_layout: - if (TextUtils.isEmpty(currentInfo.getSubChannelUrl())) { - if (!TextUtils.isEmpty(currentInfo.getUploaderUrl())) { + if (isEmpty(currentInfo.getSubChannelUrl())) { + if (!isEmpty(currentInfo.getUploaderUrl())) { openChannel(currentInfo.getUploaderUrl(), currentInfo.getUploaderName()); } @@ -583,7 +590,7 @@ public final class VideoDetailFragment } break; case R.id.detail_uploader_root_layout: - if (TextUtils.isEmpty(currentInfo.getSubChannelUrl())) { + if (isEmpty(currentInfo.getSubChannelUrl())) { Log.w(TAG, "Can't open parent channel because we got no parent channel URL"); } else { @@ -644,6 +651,10 @@ public final class VideoDetailFragment detailDurationView = rootView.findViewById(R.id.detail_duration_view); detailPositionView = rootView.findViewById(R.id.detail_position_view); + detailMetadataInfo = rootView.findViewById(R.id.detail_metadata_info); + detailMetadataInfoSeparator = rootView.findViewById(R.id.detail_metadata_info_separator); + detailMetadataInfoText = rootView.findViewById(R.id.detail_metadata_info_text); + videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout); videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view); videoDescriptionView = rootView.findViewById(R.id.detail_description_view); @@ -748,7 +759,7 @@ public final class VideoDetailFragment private void initThumbnailViews(@NonNull final StreamInfo info) { thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); - if (!TextUtils.isEmpty(info.getThumbnailUrl())) { + if (!isEmpty(info.getThumbnailUrl())) { final String infoServiceName = NewPipe.getNameOfService(info.getServiceId()); final ImageLoadingListener onFailListener = new SimpleImageLoadingListener() { @Override @@ -763,12 +774,12 @@ public final class VideoDetailFragment ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, onFailListener); } - if (!TextUtils.isEmpty(info.getSubChannelAvatarUrl())) { + if (!isEmpty(info.getSubChannelAvatarUrl())) { IMAGE_LOADER.displayImage(info.getSubChannelAvatarUrl(), subChannelThumb, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); } - if (!TextUtils.isEmpty(info.getUploaderAvatarUrl())) { + if (!isEmpty(info.getUploaderAvatarUrl())) { IMAGE_LOADER.displayImage(info.getUploaderAvatarUrl(), uploaderThumb, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); } @@ -1217,7 +1228,7 @@ public final class VideoDetailFragment } private void prepareDescription(final Description description) { - if (description == null || TextUtils.isEmpty(description.getContent()) + if (description == null || isEmpty(description.getContent()) || description == Description.emptyDescription) { return; } @@ -1247,6 +1258,42 @@ public final class VideoDetailFragment } } + private void setMetaInfo(final StreamInfo info) { + final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( + requireContext()); + final boolean showMetaInfo = sp.getBoolean( + requireContext().getString(R.string.show_meta_info_key), true); + if (info.getMetaInfo().isEmpty() || !showMetaInfo) { + detailMetadataInfo.setVisibility(View.GONE); + detailMetadataInfoSeparator.setVisibility(View.GONE); + } else { + final List metaIfs = info.getMetaInfo(); + final StringBuilder stringBuilder = new StringBuilder(); + for (final MetaInfo mi: metaIfs) { + if (!isNullOrEmpty(mi.getTitle())) { + stringBuilder.append("

").append(mi.getTitle()).append("

"); + } + stringBuilder.append(mi.getContent().getContent()); + for (int i = 0; i < mi.getUrls().size(); i++) { + stringBuilder + .append(" ") + .append(mi.getUrlTexts().get(i)) + .append(""); + if (i < mi.getUrls().size() - 1 && mi.getUrls().size() > 1) { + // append line break to all but the last URL if there are multiple URLs + stringBuilder.append("
"); + } + } + } + + detailMetadataInfoSeparator.setVisibility(View.VISIBLE); + detailMetadataInfoText.setText(HtmlCompat.fromHtml( + stringBuilder.toString(), HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING)); + detailMetadataInfoText.setMovementMethod(LinkMovementMethod.getInstance()); + detailMetadataInfo.setVisibility(View.VISIBLE); + } + } + private final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { @Override @@ -1462,9 +1509,9 @@ public final class VideoDetailFragment animateView(thumbnailPlayButton, true, 200); videoTitleTextView.setText(title); - if (!TextUtils.isEmpty(info.getSubChannelName())) { + if (!isEmpty(info.getSubChannelName())) { displayBothUploaderAndSubChannel(info); - } else if (!TextUtils.isEmpty(info.getUploaderName())) { + } else if (!isEmpty(info.getUploaderName())) { displayUploaderAsSubChannel(info); } else { uploaderTextView.setVisibility(View.GONE); @@ -1559,6 +1606,8 @@ public final class VideoDetailFragment prepareDescription(info.getDescription()); updateProgressInfo(info); initThumbnailViews(info); + setMetaInfo(info); + if (player == null || player.isPlayerStopped()) { updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl()); @@ -1610,7 +1659,7 @@ public final class VideoDetailFragment subChannelThumb.setVisibility(View.VISIBLE); - if (!TextUtils.isEmpty(info.getUploaderName())) { + if (!isEmpty(info.getUploaderName())) { uploaderTextView.setText( String.format(getString(R.string.video_detail_by), info.getUploaderName())); uploaderTextView.setVisibility(View.VISIBLE); @@ -2305,10 +2354,10 @@ public final class VideoDetailFragment private void updateOverlayData(@Nullable final String overlayTitle, @Nullable final String uploader, @Nullable final String thumbnailUrl) { - overlayTitleTextView.setText(TextUtils.isEmpty(overlayTitle) ? "" : overlayTitle); - overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader); + overlayTitleTextView.setText(isEmpty(title) ? "" : title); + overlayChannelTextView.setText(isEmpty(uploader) ? "" : uploader); overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); - if (!TextUtils.isEmpty(thumbnailUrl)) { + if (!isEmpty(thumbnailUrl)) { IMAGE_LOADER.displayImage(thumbnailUrl, overlayThumbnailImageView, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, null); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 02dbf176b..f44e5e330 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -9,6 +9,7 @@ import android.text.Editable; import android.text.Html; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -39,6 +40,7 @@ import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.database.history.model.SearchHistoryEntry; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor; +import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.StreamingService; @@ -78,6 +80,7 @@ import io.reactivex.rxjava3.subjects.PublishSubject; import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags; import static java.util.Arrays.asList; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.util.AnimationUtils.animateView; public class SearchFragment extends BaseListFragment> @@ -129,6 +132,9 @@ public class SearchFragment extends BaseListFragment cannot be bundled without creating some containers + metaInfo = new MetaInfo[result.getMetaInfo().size()]; + metaInfo = result.getMetaInfo().toArray(metaInfo); + handleSearchSuggestion(); + handleMetaInfo(); lastSearchedString = searchString; nextPage = result.getNextPage(); @@ -1021,6 +1036,39 @@ public class SearchFragment extends BaseListFragment").append(mi.getTitle()).append(""); + } + stringBuilder.append(mi.getContent().getContent()); + for (int i = 0; i < mi.getUrls().size(); i++) { + stringBuilder + .append(" ") + .append(mi.getUrlTexts().get(i)) + .append(""); + if (i < mi.getUrls().size() - 1 && mi.getUrls().size() > 1) { + // append line break to all but the last URL if there are multiple URLs + stringBuilder.append("
"); + } + } + } + + metaInfoTextView.setText(HtmlCompat.fromHtml( + stringBuilder.toString(), HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING)); + metaInfoTextView.setMovementMethod(LinkMovementMethod.getInstance()); + metaInfoTextView.setVisibility(View.VISIBLE); + } + } + @Override public void handleNextItems(final ListExtractor.InfoItemsPage result) { showListFooter(false); diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml index 8aee89ab0..d59d2db73 100644 --- a/app/src/main/res/layout-large-land/fragment_video_detail.xml +++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml @@ -506,6 +506,38 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml index 0df85fe95..3bf2bb025 100644 --- a/app/src/main/res/layout/fragment_video_detail.xml +++ b/app/src/main/res/layout/fragment_video_detail.xml @@ -491,6 +491,39 @@ + + + + + + + + + + show_play_with_kodi show_next_video show_comments + show_meta_info stream_info_selected_tab show_hold_to_append content_language diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ecb690044..d2dd8afd5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,6 +93,8 @@ Show comments Turn off to hide comments Turn off to prevent loading thumbnails, saving data and memory usage. Changes clear both in-memory and on-disk image cache. + Show meta info + Turn off to hide meta info boxes with additional information about the stream creator, stream content or a search request. Image cache wiped Wipe cached metadata Remove all cached webpage data diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml index c885366ec..914fb2e59 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -85,6 +85,13 @@ android:title="@string/show_comments_title" app:iconSpaceReserved="false" /> + + Date: Sun, 20 Dec 2020 15:05:37 +0100 Subject: [PATCH 2/2] Improve meta info layout and merge duplicate code --- .../fragments/detail/VideoDetailFragment.java | 52 ++---------- .../fragments/list/search/SearchFragment.java | 46 ++--------- .../schabi/newpipe/util/ExtractorHelper.java | 79 +++++++++++++++++++ .../org/schabi/newpipe/util/Localization.java | 2 +- .../fragment_video_detail.xml | 29 ++----- app/src/main/res/layout/fragment_search.xml | 15 +++- .../main/res/layout/fragment_video_detail.xml | 30 ++----- 7 files changed, 120 insertions(+), 133 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index b9cb4ab2b..6560ab404 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -16,7 +16,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.provider.Settings; -import android.text.method.LinkMovementMethod; import android.text.util.Linkify; import android.util.DisplayMetrics; import android.util.Log; @@ -63,7 +62,6 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.download.DownloadDialog; import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -126,11 +124,11 @@ import io.reactivex.rxjava3.schedulers.Schedulers; import static android.text.TextUtils.isEmpty; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS; import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked; import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired; import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; public final class VideoDetailFragment extends BaseStateFragment @@ -221,9 +219,8 @@ public final class VideoDetailFragment private TextView detailDurationView; private TextView detailPositionView; - private LinearLayout detailMetadataInfo; - private View detailMetadataInfoSeparator; - private TextView detailMetadataInfoText; + private View detailMetaInfoSeparator; + private TextView detailMetaInfoTextView; private LinearLayout videoDescriptionRootLayout; private TextView videoUploadDateView; @@ -651,9 +648,8 @@ public final class VideoDetailFragment detailDurationView = rootView.findViewById(R.id.detail_duration_view); detailPositionView = rootView.findViewById(R.id.detail_position_view); - detailMetadataInfo = rootView.findViewById(R.id.detail_metadata_info); - detailMetadataInfoSeparator = rootView.findViewById(R.id.detail_metadata_info_separator); - detailMetadataInfoText = rootView.findViewById(R.id.detail_metadata_info_text); + detailMetaInfoSeparator = rootView.findViewById(R.id.detail_meta_info_separator); + detailMetaInfoTextView = rootView.findViewById(R.id.detail_meta_info_text_view); videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout); videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view); @@ -1258,42 +1254,6 @@ public final class VideoDetailFragment } } - private void setMetaInfo(final StreamInfo info) { - final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( - requireContext()); - final boolean showMetaInfo = sp.getBoolean( - requireContext().getString(R.string.show_meta_info_key), true); - if (info.getMetaInfo().isEmpty() || !showMetaInfo) { - detailMetadataInfo.setVisibility(View.GONE); - detailMetadataInfoSeparator.setVisibility(View.GONE); - } else { - final List metaIfs = info.getMetaInfo(); - final StringBuilder stringBuilder = new StringBuilder(); - for (final MetaInfo mi: metaIfs) { - if (!isNullOrEmpty(mi.getTitle())) { - stringBuilder.append("

").append(mi.getTitle()).append("

"); - } - stringBuilder.append(mi.getContent().getContent()); - for (int i = 0; i < mi.getUrls().size(); i++) { - stringBuilder - .append(" ") - .append(mi.getUrlTexts().get(i)) - .append(""); - if (i < mi.getUrls().size() - 1 && mi.getUrls().size() > 1) { - // append line break to all but the last URL if there are multiple URLs - stringBuilder.append("
"); - } - } - } - - detailMetadataInfoSeparator.setVisibility(View.VISIBLE); - detailMetadataInfoText.setText(HtmlCompat.fromHtml( - stringBuilder.toString(), HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING)); - detailMetadataInfoText.setMovementMethod(LinkMovementMethod.getInstance()); - detailMetadataInfo.setVisibility(View.VISIBLE); - } - } - private final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { @Override @@ -1606,7 +1566,7 @@ public final class VideoDetailFragment prepareDescription(info.getDescription()); updateProgressInfo(info); initThumbnailViews(info); - setMetaInfo(info); + showMetaInfoInTextView(info.getMetaInfo(), detailMetaInfoTextView, detailMetaInfoSeparator); if (player == null || player.isPlayerStopped()) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index f44e5e330..2dac6d11b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -9,7 +9,6 @@ import android.text.Editable; import android.text.Html; import android.text.TextUtils; import android.text.TextWatcher; -import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -80,8 +79,8 @@ import io.reactivex.rxjava3.subjects.PublishSubject; import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags; import static java.util.Arrays.asList; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; public class SearchFragment extends BaseListFragment> implements BackPressable { @@ -160,6 +159,7 @@ public class SearchFragment extends BaseListFragment").append(mi.getTitle()).append(""); - } - stringBuilder.append(mi.getContent().getContent()); - for (int i = 0; i < mi.getUrls().size(); i++) { - stringBuilder - .append(" ") - .append(mi.getUrlTexts().get(i)) - .append(""); - if (i < mi.getUrls().size() - 1 && mi.getUrls().size() > 1) { - // append line break to all but the last URL if there are multiple URLs - stringBuilder.append("
"); - } - } - } - - metaInfoTextView.setText(HtmlCompat.fromHtml( - stringBuilder.toString(), HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING)); - metaInfoTextView.setMovementMethod(LinkMovementMethod.getInstance()); - metaInfoTextView.setVisibility(View.VISIBLE); - } - } - @Override public void handleNextItems(final ListExtractor.InfoItemsPage result) { showListFooter(false); diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index 650c5ae11..1f1b94545 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -22,9 +22,16 @@ package org.schabi.newpipe.util; import android.content.Context; import android.content.Intent; import android.os.Handler; +import android.text.method.LinkMovementMethod; import android.util.Log; +import android.view.View; +import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.Nullable; +import androidx.core.text.HtmlCompat; +import androidx.preference.PreferenceManager; + import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.ReCaptchaActivity; @@ -32,6 +39,7 @@ import org.schabi.newpipe.extractor.Info; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; import org.schabi.newpipe.extractor.ListInfo; +import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.StreamingService; @@ -60,6 +68,8 @@ import java.util.List; import io.reactivex.rxjava3.core.Maybe; import io.reactivex.rxjava3.core.Single; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; + public final class ExtractorHelper { private static final String TAG = ExtractorHelper.class.getSimpleName(); private static final InfoCache CACHE = InfoCache.getInstance(); @@ -306,4 +316,73 @@ public final class ExtractorHelper { } }); } + + /** + * Formats the text contained in the meta info list as HTML and puts it into the text view, + * while also making the separator visible. If the list is null or empty, or the user chose not + * to see meta information, both the text view and the separator are hidden + * @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 metaInfoSeparator another view to be shown or hidden accordingly to the text view + */ + public static void showMetaInfoInTextView(@Nullable final List metaInfos, + final TextView metaInfoTextView, + final View metaInfoSeparator) { + final Context context = metaInfoTextView.getContext(); + final boolean showMetaInfo = PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(context.getString(R.string.show_meta_info_key), true); + + if (!showMetaInfo || metaInfos == null || metaInfos.isEmpty()) { + metaInfoTextView.setVisibility(View.GONE); + metaInfoSeparator.setVisibility(View.GONE); + + } else { + final StringBuilder stringBuilder = new StringBuilder(); + for (final MetaInfo metaInfo : metaInfos) { + if (!isNullOrEmpty(metaInfo.getTitle())) { + stringBuilder.append("").append(metaInfo.getTitle()).append("") + .append(Localization.DOT_SEPARATOR); + } + + String content = metaInfo.getContent().getContent().trim(); + if (content.endsWith(".")) { + content = content.substring(0, content.length() - 1); // remove . at end + } + stringBuilder.append(content); + + for (int i = 0; i < metaInfo.getUrls().size(); i++) { + if (i == 0) { + stringBuilder.append(Localization.DOT_SEPARATOR); + } else { + stringBuilder.append("

"); + } + + stringBuilder + .append("") + .append(capitalizeIfAllUppercase(metaInfo.getUrlTexts().get(i).trim())) + .append(""); + } + } + + metaInfoTextView.setText(HtmlCompat.fromHtml(stringBuilder.toString(), + HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING)); + metaInfoTextView.setMovementMethod(LinkMovementMethod.getInstance()); + metaInfoTextView.setVisibility(View.VISIBLE); + metaInfoSeparator.setVisibility(View.VISIBLE); + } + } + + private static String capitalizeIfAllUppercase(final String text) { + for (int i = 0; i < text.length(); i++) { + if (Character.isLowerCase(text.charAt(i))) { + return text; // there is at least a lowercase letter -> not all uppercase + } + } + + if (text.isEmpty()) { + return text; + } else { + return text.substring(0, 1).toUpperCase() + text.substring(1).toLowerCase(); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 710827864..978f558c4 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -57,7 +57,7 @@ import java.util.Locale; public final class Localization { - private static final String DOT_SEPARATOR = " • "; + public static final String DOT_SEPARATOR = " • "; private static PrettyTime prettyTime; private Localization() { } diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml index d59d2db73..d90c782ef 100644 --- a/app/src/main/res/layout-large-land/fragment_video_detail.xml +++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml @@ -507,36 +507,21 @@ - - - - - - + android:gravity="center" + android:padding="12dp" + android:textSize="@dimen/video_item_detail_description_text_size" + tools:text="Stream meta info with link" /> + diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml index 3bf2bb025..758a88f19 100644 --- a/app/src/main/res/layout/fragment_video_detail.xml +++ b/app/src/main/res/layout/fragment_video_detail.xml @@ -492,37 +492,21 @@ - - - - - - - + android:gravity="center" + android:padding="12dp" + android:textSize="@dimen/video_item_detail_description_text_size" + tools:text="Stream meta info with link" />