Add button to allow selecting text in the description

Since now selection is disabled by default, this fixes #5453
This commit is contained in:
Stypox 2021-04-01 16:43:31 +02:00
parent d0d5373be9
commit bc3e43ac58
No known key found for this signature in database
GPG Key ID: 4BDF1B40A49FDD23
5 changed files with 136 additions and 29 deletions

View File

@ -5,11 +5,11 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.widget.TooltipCompat;
import androidx.core.text.HtmlCompat; import androidx.core.text.HtmlCompat;
import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.BaseFragment;
@ -35,6 +35,7 @@ public class DescriptionFragment extends BaseFragment {
StreamInfo streamInfo = null; StreamInfo streamInfo = null;
@Nullable @Nullable
Disposable descriptionDisposable = null; Disposable descriptionDisposable = null;
FragmentDescriptionBinding binding;
public DescriptionFragment() { public DescriptionFragment() {
} }
@ -47,11 +48,10 @@ public class DescriptionFragment extends BaseFragment {
public View onCreateView(@NonNull final LayoutInflater inflater, public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container, @Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) { @Nullable final Bundle savedInstanceState) {
final FragmentDescriptionBinding binding = binding = FragmentDescriptionBinding.inflate(inflater, container, false);
FragmentDescriptionBinding.inflate(inflater, container, false);
if (streamInfo != null) { if (streamInfo != null) {
setupUploadDate(binding.detailUploadDateView); setupUploadDate();
setupDescription(binding.detailDescriptionView); setupDescription();
setupMetadata(inflater, binding.detailMetadataLayout); setupMetadata(inflater, binding.detailMetadataLayout);
} }
return binding.getRoot(); return binding.getRoot();
@ -65,40 +65,82 @@ public class DescriptionFragment extends BaseFragment {
} }
} }
private void setupUploadDate(final TextView uploadDateTextView) {
private void setupUploadDate() {
if (streamInfo.getUploadDate() != null) { if (streamInfo.getUploadDate() != null) {
uploadDateTextView.setText(Localization binding.detailUploadDateView.setText(Localization
.localizeUploadDate(activity, streamInfo.getUploadDate().offsetDateTime())); .localizeUploadDate(activity, streamInfo.getUploadDate().offsetDateTime()));
} else { } else {
uploadDateTextView.setVisibility(View.GONE); binding.detailUploadDateView.setVisibility(View.GONE);
} }
} }
private void setupDescription(final TextView descriptionTextView) {
private void setupDescription() {
final Description description = streamInfo.getDescription(); final Description description = streamInfo.getDescription();
if (description == null || isEmpty(description.getContent()) if (description == null || isEmpty(description.getContent())
|| description == Description.emptyDescription) { || description == Description.emptyDescription) {
descriptionTextView.setVisibility(View.GONE); binding.detailDescriptionView.setVisibility(View.GONE);
binding.detailSelectDescriptionButton.setVisibility(View.GONE);
return; return;
} }
// start with disabled state. This also loads description content (!)
disableDescriptionSelection();
binding.detailSelectDescriptionButton.setOnClickListener(v -> {
if (binding.detailDescriptionNoteView.getVisibility() == View.VISIBLE) {
disableDescriptionSelection();
} else {
// enable selection only when button is clicked to prevent flickering
enableDescriptionSelection();
}
});
}
private void enableDescriptionSelection() {
binding.detailDescriptionNoteView.setVisibility(View.VISIBLE);
binding.detailDescriptionView.setTextIsSelectable(true);
final String buttonLabel = getString(R.string.description_select_disable);
binding.detailSelectDescriptionButton.setContentDescription(buttonLabel);
TooltipCompat.setTooltipText(binding.detailSelectDescriptionButton, buttonLabel);
binding.detailSelectDescriptionButton.setImageResource(R.drawable.ic_close);
}
private void disableDescriptionSelection() {
// show description content again, otherwise some links are not clickable
loadDescriptionContent();
binding.detailDescriptionNoteView.setVisibility(View.GONE);
binding.detailDescriptionView.setTextIsSelectable(false);
final String buttonLabel = getString(R.string.description_select_enable);
binding.detailSelectDescriptionButton.setContentDescription(buttonLabel);
TooltipCompat.setTooltipText(binding.detailSelectDescriptionButton, buttonLabel);
binding.detailSelectDescriptionButton.setImageResource(R.drawable.ic_select_all);
}
private void loadDescriptionContent() {
final Description description = streamInfo.getDescription();
switch (description.getType()) { switch (description.getType()) {
case Description.HTML: case Description.HTML:
descriptionDisposable = TextLinkifier.createLinksFromHtmlBlock(requireContext(), descriptionDisposable = TextLinkifier.createLinksFromHtmlBlock(requireContext(),
description.getContent(), descriptionTextView, description.getContent(), binding.detailDescriptionView,
HtmlCompat.FROM_HTML_MODE_LEGACY); HtmlCompat.FROM_HTML_MODE_LEGACY);
break; break;
case Description.MARKDOWN: case Description.MARKDOWN:
descriptionDisposable = TextLinkifier.createLinksFromMarkdownText(requireContext(), descriptionDisposable = TextLinkifier.createLinksFromMarkdownText(requireContext(),
description.getContent(), descriptionTextView); description.getContent(), binding.detailDescriptionView);
break; break;
case Description.PLAIN_TEXT: default: case Description.PLAIN_TEXT: default:
descriptionDisposable = TextLinkifier.createLinksFromPlainText(requireContext(), descriptionDisposable = TextLinkifier.createLinksFromPlainText(requireContext(),
description.getContent(), descriptionTextView); description.getContent(), binding.detailDescriptionView);
break; break;
} }
} }
private void setupMetadata(final LayoutInflater inflater, private void setupMetadata(final LayoutInflater inflater,
final LinearLayout layout) { final LinearLayout layout) {
addMetadataItem(inflater, layout, false, addMetadataItem(inflater, layout, false,
@ -138,21 +180,23 @@ public class DescriptionFragment extends BaseFragment {
return; return;
} }
final ItemMetadataBinding binding = ItemMetadataBinding.inflate(inflater, layout, false); final ItemMetadataBinding itemBinding
binding.metadataTypeView.setText(type); = ItemMetadataBinding.inflate(inflater, layout, false);
binding.metadataTypeView.setOnLongClickListener(v -> {
itemBinding.metadataTypeView.setText(type);
itemBinding.metadataTypeView.setOnLongClickListener(v -> {
ShareUtils.copyToClipboard(requireContext(), content); ShareUtils.copyToClipboard(requireContext(), content);
return true; return true;
}); });
if (linkifyContent) { if (linkifyContent) {
TextLinkifier.createLinksFromPlainText(layout.getContext(), content, TextLinkifier.createLinksFromPlainText(requireContext(),
binding.metadataContentView); content, itemBinding.metadataContentView);
} else { } else {
binding.metadataContentView.setText(content); itemBinding.metadataContentView.setText(content);
} }
layout.addView(binding.getRoot()); layout.addView(itemBinding.getRoot());
} }
private void addTagsMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { private void addTagsMetadataItem(final LayoutInflater inflater, final LinearLayout layout) {
@ -192,7 +236,7 @@ public class DescriptionFragment extends BaseFragment {
if (contentRes != 0) { if (contentRes != 0) {
addMetadataItem(inflater, layout, false, addMetadataItem(inflater, layout, false,
R.string.metadata_privacy, layout.getContext().getString(contentRes)); R.string.metadata_privacy, getString(contentRes));
} }
} }
} }

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#ffffff">
<path
android:fillColor="#ffffff"
android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#000000">
<path
android:fillColor="#000000"
android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/>
</vector>

View File

@ -9,7 +9,8 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<TextView <TextView
android:id="@+id/detail_upload_date_view" android:id="@+id/detail_upload_date_view"
@ -17,30 +18,69 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="8dp"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_upload_date_text_size" android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@+id/detail_select_description_button"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="Published on Oct 2, 2009" /> tools:text="Published on Oct 2, 2009" />
<ImageView
android:id="@+id/detail_select_description_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="2dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/description_select_enable"
android:focusable="true"
android:padding="5dp"
app:layout_constraintBottom_toTopOf="@+id/barrier"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_select_all" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierAllowsGoneWidgets="false"
app:barrierDirection="top"
app:constraint_referenced_ids="detail_description_note_view,detail_description_view" />
<TextView
android:id="@+id/detail_description_note_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:text="@string/description_select_note"
android:textSize="12sp"
android:textStyle="italic"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/detail_upload_date_view"
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/detail_description_view" android:id="@+id/detail_description_view"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="true"
android:textSize="@dimen/video_item_detail_description_text_size" android:textSize="@dimen/video_item_detail_description_text_size"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/detail_upload_date_view" app:layout_constraintTop_toBottomOf="@+id/detail_description_note_view"
app:layout_constraintVertical_bias="0.0"
tools:text="Description Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum." /> tools:text="Description Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum." />
<LinearLayout <LinearLayout

View File

@ -716,6 +716,9 @@
<string name="night_theme_summary">Select your favorite night theme — %s</string> <string name="night_theme_summary">Select your favorite night theme — %s</string>
<string name="select_night_theme_toast">You can select your favorite night theme below</string> <string name="select_night_theme_toast">You can select your favorite night theme below</string>
<string name="download_has_started">Download has started</string> <string name="download_has_started">Download has started</string>
<string name="description_select_note">You can now select text inside the description. Note that the page may flicker and links may not be clickable while in selection mode.</string>
<string name="description_select_enable">Enable selecting text in the description</string>
<string name="description_select_disable">Disable selecting text in the description</string>
<string name="metadata_category">Category</string> <string name="metadata_category">Category</string>
<string name="metadata_tags">Tags</string> <string name="metadata_tags">Tags</string>
<string name="metadata_licence">Licence</string> <string name="metadata_licence">Licence</string>