Improve meta info layout and merge duplicate code

This commit is contained in:
Stypox 2020-12-20 15:05:37 +01:00
parent 0a831ec84e
commit 19f9b4f502
No known key found for this signature in database
GPG key ID: 4BDF1B40A49FDD23
7 changed files with 120 additions and 133 deletions

View file

@ -16,7 +16,6 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.provider.Settings; import android.provider.Settings;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify; import android.text.util.Linkify;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
@ -63,7 +62,6 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.download.DownloadDialog; import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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 android.text.TextUtils.isEmpty;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS; 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.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.globalScreenOrientationLocked;
import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired; import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired;
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
public final class VideoDetailFragment public final class VideoDetailFragment
extends BaseStateFragment<StreamInfo> extends BaseStateFragment<StreamInfo>
@ -221,9 +219,8 @@ public final class VideoDetailFragment
private TextView detailDurationView; private TextView detailDurationView;
private TextView detailPositionView; private TextView detailPositionView;
private LinearLayout detailMetadataInfo; private View detailMetaInfoSeparator;
private View detailMetadataInfoSeparator; private TextView detailMetaInfoTextView;
private TextView detailMetadataInfoText;
private LinearLayout videoDescriptionRootLayout; private LinearLayout videoDescriptionRootLayout;
private TextView videoUploadDateView; private TextView videoUploadDateView;
@ -651,9 +648,8 @@ public final class VideoDetailFragment
detailDurationView = rootView.findViewById(R.id.detail_duration_view); detailDurationView = rootView.findViewById(R.id.detail_duration_view);
detailPositionView = rootView.findViewById(R.id.detail_position_view); detailPositionView = rootView.findViewById(R.id.detail_position_view);
detailMetadataInfo = rootView.findViewById(R.id.detail_metadata_info); detailMetaInfoSeparator = rootView.findViewById(R.id.detail_meta_info_separator);
detailMetadataInfoSeparator = rootView.findViewById(R.id.detail_metadata_info_separator); detailMetaInfoTextView = rootView.findViewById(R.id.detail_meta_info_text_view);
detailMetadataInfoText = rootView.findViewById(R.id.detail_metadata_info_text);
videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout); videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout);
videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view); 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<MetaInfo> metaIfs = info.getMetaInfo();
final StringBuilder stringBuilder = new StringBuilder();
for (final MetaInfo mi: metaIfs) {
if (!isNullOrEmpty(mi.getTitle())) {
stringBuilder.append("<h2>").append(mi.getTitle()).append("</h2>");
}
stringBuilder.append(mi.getContent().getContent());
for (int i = 0; i < mi.getUrls().size(); i++) {
stringBuilder
.append(" <a href=\"").append(mi.getUrls().get(i)).append("\">")
.append(mi.getUrlTexts().get(i))
.append("</a>");
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("<br>");
}
}
}
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 = private final ViewTreeObserver.OnPreDrawListener preDrawListener =
new ViewTreeObserver.OnPreDrawListener() { new ViewTreeObserver.OnPreDrawListener() {
@Override @Override
@ -1606,7 +1566,7 @@ public final class VideoDetailFragment
prepareDescription(info.getDescription()); prepareDescription(info.getDescription());
updateProgressInfo(info); updateProgressInfo(info);
initThumbnailViews(info); initThumbnailViews(info);
setMetaInfo(info); showMetaInfoInTextView(info.getMetaInfo(), detailMetaInfoTextView, detailMetaInfoSeparator);
if (player == null || player.isPlayerStopped()) { if (player == null || player.isPlayerStopped()) {

View file

@ -9,7 +9,6 @@ import android.text.Editable;
import android.text.Html; import android.text.Html;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -80,8 +79,8 @@ import io.reactivex.rxjava3.subjects.PublishSubject;
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags; import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
import static java.util.Arrays.asList; 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.AnimationUtils.animateView;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage<?>> public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage<?>>
implements BackPressable { implements BackPressable {
@ -160,6 +159,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
private TextView correctSuggestion; private TextView correctSuggestion;
private TextView metaInfoTextView; private TextView metaInfoTextView;
private View metaInfoSeparator;
private View suggestionsPanel; private View suggestionsPanel;
private boolean suggestionsPanelVisible = false; private boolean suggestionsPanelVisible = false;
@ -276,7 +276,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
handleSearchSuggestion(); handleSearchSuggestion();
handleMetaInfo(); showMetaInfoInTextView(metaInfo == null ? null : Arrays.asList(metaInfo),
metaInfoTextView, metaInfoSeparator);
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) { if (suggestionDisposable == null || suggestionDisposable.isDisposed()) {
initSuggestionObserver(); initSuggestionObserver();
@ -362,7 +363,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear); searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear);
correctSuggestion = rootView.findViewById(R.id.correct_suggestion); correctSuggestion = rootView.findViewById(R.id.correct_suggestion);
metaInfoTextView = rootView.findViewById(R.id.search_meta_info); metaInfoTextView = rootView.findViewById(R.id.search_meta_info_text_view);
metaInfoSeparator = rootView.findViewById(R.id.search_meta_info_separator);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -988,7 +990,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
metaInfo = result.getMetaInfo().toArray(metaInfo); metaInfo = result.getMetaInfo().toArray(metaInfo);
handleSearchSuggestion(); handleSearchSuggestion();
handleMetaInfo();
showMetaInfoInTextView(result.getMetaInfo(), metaInfoTextView, metaInfoSeparator);
lastSearchedString = searchString; lastSearchedString = searchString;
nextPage = result.getNextPage(); nextPage = result.getNextPage();
@ -1036,39 +1039,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
} }
} }
private void handleMetaInfo() {
final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
requireContext());
final boolean showMetaInfo = sp.getBoolean(
requireContext().getString(R.string.show_meta_info_key), true);
if (metaInfo == null || metaInfo.length == 0 || !showMetaInfo) {
metaInfoTextView.setVisibility(View.GONE);
} else {
final StringBuilder stringBuilder = new StringBuilder();
for (final MetaInfo mi: metaInfo) {
if (!isNullOrEmpty(mi.getTitle())) {
stringBuilder.append("<h2>").append(mi.getTitle()).append("</h2>");
}
stringBuilder.append(mi.getContent().getContent());
for (int i = 0; i < mi.getUrls().size(); i++) {
stringBuilder
.append(" <a href=\"").append(mi.getUrls().get(i)).append("\">")
.append(mi.getUrlTexts().get(i))
.append("</a>");
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("<br>");
}
}
}
metaInfoTextView.setText(HtmlCompat.fromHtml(
stringBuilder.toString(), HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING));
metaInfoTextView.setMovementMethod(LinkMovementMethod.getInstance());
metaInfoTextView.setVisibility(View.VISIBLE);
}
}
@Override @Override
public void handleNextItems(final ListExtractor.InfoItemsPage<?> result) { public void handleNextItems(final ListExtractor.InfoItemsPage<?> result) {
showListFooter(false); showListFooter(false);

View file

@ -22,9 +22,16 @@ package org.schabi.newpipe.util;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Handler; import android.os.Handler;
import android.text.method.LinkMovementMethod;
import android.util.Log; import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast; 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.MainActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity; 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.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
import org.schabi.newpipe.extractor.ListInfo; import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService; 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.Maybe;
import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.core.Single;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public final class ExtractorHelper { public final class ExtractorHelper {
private static final String TAG = ExtractorHelper.class.getSimpleName(); private static final String TAG = ExtractorHelper.class.getSimpleName();
private static final InfoCache CACHE = InfoCache.getInstance(); 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<MetaInfo> 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("<b>").append(metaInfo.getTitle()).append("</b>")
.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("<br/><br/>");
}
stringBuilder
.append("<a href=\"").append(metaInfo.getUrls().get(i)).append("\">")
.append(capitalizeIfAllUppercase(metaInfo.getUrlTexts().get(i).trim()))
.append("</a>");
}
}
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();
}
}
} }

View file

@ -57,7 +57,7 @@ import java.util.Locale;
public final class Localization { public final class Localization {
private static final String DOT_SEPARATOR = ""; public static final String DOT_SEPARATOR = "";
private static PrettyTime prettyTime; private static PrettyTime prettyTime;
private Localization() { } private Localization() { }

View file

@ -507,36 +507,21 @@
</LinearLayout> </LinearLayout>
<View <View
android:id="@+id/detail_metadata_info_separator" android:id="@+id/detail_meta_info_separator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1px" android:layout_height="1px"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:layout_marginRight="8dp" android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:background="?attr/separator_color" /> android:background="?attr/separator_color" />
<LinearLayout
android:id="@+id/detail_metadata_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
app:srcCompat="?attr/ic_info_outline" />
<TextView <TextView
android:id="@+id/detail_metadata_info_text" android:id="@+id/detail_meta_info_text_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:gravity="center"
android:padding="12dp"
android:textSize="@dimen/video_item_detail_description_text_size"
tools:text="Stream meta info with link" /> tools:text="Stream meta info with link" />
</LinearLayout>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -16,20 +16,29 @@
tools:text="Showing results for lorem ipsum dolor sit amet consectetur adipisci elit" /> tools:text="Showing results for lorem ipsum dolor sit amet consectetur adipisci elit" />
<TextView <TextView
android:id="@+id/search_meta_info" android:id="@+id/search_meta_info_text_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/correct_suggestion" android:layout_below="@id/correct_suggestion"
android:gravity="center"
android:padding="12dp" android:padding="12dp"
android:textSize="@dimen/search_suggestion_text_size" android:textSize="@dimen/video_item_detail_description_text_size"
tools:text="Get the latest information from the WHO about coronavirus." /> tools:text="Get the latest information from the WHO about coronavirus." />
<View
android:id="@+id/search_meta_info_separator"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_below="@id/search_meta_info_text_view"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/items_list" android:id="@+id/items_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_below="@+id/search_meta_info" android:layout_below="@+id/search_meta_info_separator"
android:scrollbars="vertical" android:scrollbars="vertical"
app:layoutManager="LinearLayoutManager" app:layoutManager="LinearLayoutManager"
tools:listitem="@layout/list_stream_item" /> tools:listitem="@layout/list_stream_item" />

View file

@ -492,38 +492,22 @@
</LinearLayout> </LinearLayout>
<View <View
android:id="@+id/detail_metadata_info_separator" android:id="@+id/detail_meta_info_separator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1px" android:layout_height="1px"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:layout_marginRight="8dp" android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:background="?attr/separator_color" /> android:background="?attr/separator_color" />
<LinearLayout
android:id="@+id/detail_metadata_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
app:srcCompat="?attr/ic_info_outline" />
<TextView <TextView
android:id="@+id/detail_metadata_info_text" android:id="@+id/detail_meta_info_text_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:gravity="center"
android:padding="12dp"
android:textSize="@dimen/video_item_detail_description_text_size"
tools:text="Stream meta info with link" /> tools:text="Stream meta info with link" />
</LinearLayout>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1px" android:layout_height="1px"