From 19f9b4f502dea4b7bc5a14c3ee190c2096074219 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 20 Dec 2020 15:05:37 +0100 Subject: [PATCH] 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" />