mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	refactor: common code from ChannelInfo/Description -> BaseInfoFragment
This commit is contained in:
		| @@ -0,0 +1,206 @@ | |||||||
|  | package org.schabi.newpipe.fragments.detail; | ||||||
|  |  | ||||||
|  | import static android.text.TextUtils.isEmpty; | ||||||
|  | import static org.schabi.newpipe.extractor.utils.Utils.isBlank; | ||||||
|  | import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD; | ||||||
|  |  | ||||||
|  | import android.os.Bundle; | ||||||
|  | import android.view.LayoutInflater; | ||||||
|  | import android.view.View; | ||||||
|  | import android.view.ViewGroup; | ||||||
|  | import android.widget.LinearLayout; | ||||||
|  |  | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.annotation.Nullable; | ||||||
|  | import androidx.annotation.StringRes; | ||||||
|  | import androidx.appcompat.widget.TooltipCompat; | ||||||
|  | import androidx.core.text.HtmlCompat; | ||||||
|  |  | ||||||
|  | import com.google.android.material.chip.Chip; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.BaseFragment; | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.databinding.FragmentDescriptionBinding; | ||||||
|  | import org.schabi.newpipe.databinding.ItemMetadataBinding; | ||||||
|  | import org.schabi.newpipe.databinding.ItemMetadataTagsBinding; | ||||||
|  | import org.schabi.newpipe.extractor.StreamingService; | ||||||
|  | import org.schabi.newpipe.extractor.stream.Description; | ||||||
|  | import org.schabi.newpipe.util.NavigationHelper; | ||||||
|  | import org.schabi.newpipe.util.external_communication.ShareUtils; | ||||||
|  | import org.schabi.newpipe.util.text.TextLinkifier; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import io.reactivex.rxjava3.disposables.CompositeDisposable; | ||||||
|  |  | ||||||
|  | public abstract class BaseInfoFragment extends BaseFragment { | ||||||
|  |     final CompositeDisposable descriptionDisposables = new CompositeDisposable(); | ||||||
|  |     FragmentDescriptionBinding binding; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public View onCreateView(@NonNull final LayoutInflater inflater, | ||||||
|  |                              @Nullable final ViewGroup container, | ||||||
|  |                              @Nullable final Bundle savedInstanceState) { | ||||||
|  |         binding = FragmentDescriptionBinding.inflate(inflater, container, false); | ||||||
|  |         setupDescription(); | ||||||
|  |         setupMetadata(inflater, binding.detailMetadataLayout); | ||||||
|  |         addTagsMetadataItem(inflater, binding.detailMetadataLayout); | ||||||
|  |         return binding.getRoot(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onDestroy() { | ||||||
|  |         descriptionDisposables.clear(); | ||||||
|  |         super.onDestroy(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get the description to display. | ||||||
|  |      * @return description object | ||||||
|  |      */ | ||||||
|  |     @Nullable | ||||||
|  |     protected abstract Description getDescription(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get the streaming service. Used for generating description links. | ||||||
|  |      * @return streaming service | ||||||
|  |      */ | ||||||
|  |     @Nullable | ||||||
|  |     protected abstract StreamingService getService(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get the streaming service ID. Used for tag links. | ||||||
|  |      * @return service ID | ||||||
|  |      */ | ||||||
|  |     protected abstract int getServiceId(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get the URL of the described video. Used for generating description links. | ||||||
|  |      * @return stream URL | ||||||
|  |      */ | ||||||
|  |     @Nullable | ||||||
|  |     protected abstract String getStreamUrl(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get the list of tags to display below the description. | ||||||
|  |      * @return tag list | ||||||
|  |      */ | ||||||
|  |     @Nullable | ||||||
|  |     public abstract List<String> getTags(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Add additional metadata to display. | ||||||
|  |      * @param inflater LayoutInflater | ||||||
|  |      * @param layout detailMetadataLayout | ||||||
|  |      */ | ||||||
|  |     protected abstract void setupMetadata(LayoutInflater inflater, LinearLayout layout); | ||||||
|  |  | ||||||
|  |     private void setupDescription() { | ||||||
|  |         final Description description = getDescription(); | ||||||
|  |         if (description == null || isEmpty(description.getContent()) | ||||||
|  |                 || description == Description.EMPTY_DESCRIPTION) { | ||||||
|  |             binding.detailDescriptionView.setVisibility(View.GONE); | ||||||
|  |             binding.detailSelectDescriptionButton.setVisibility(View.GONE); | ||||||
|  |             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 | ||||||
|  |         TextLinkifier.fromDescription(binding.detailDescriptionView, | ||||||
|  |                 getDescription(), HtmlCompat.FROM_HTML_MODE_LEGACY, | ||||||
|  |                 getService(), getStreamUrl(), | ||||||
|  |                 descriptionDisposables, SET_LINK_MOVEMENT_METHOD); | ||||||
|  |  | ||||||
|  |         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); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void addMetadataItem(final LayoutInflater inflater, | ||||||
|  |                                  final LinearLayout layout, | ||||||
|  |                                  final boolean linkifyContent, | ||||||
|  |                                  @StringRes final int type, | ||||||
|  |                                  @Nullable final String content) { | ||||||
|  |         if (isBlank(content)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         final ItemMetadataBinding itemBinding = | ||||||
|  |                 ItemMetadataBinding.inflate(inflater, layout, false); | ||||||
|  |  | ||||||
|  |         itemBinding.metadataTypeView.setText(type); | ||||||
|  |         itemBinding.metadataTypeView.setOnLongClickListener(v -> { | ||||||
|  |             ShareUtils.copyToClipboard(requireContext(), content); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         if (linkifyContent) { | ||||||
|  |             TextLinkifier.fromPlainText(itemBinding.metadataContentView, content, null, null, | ||||||
|  |                     descriptionDisposables, SET_LINK_MOVEMENT_METHOD); | ||||||
|  |         } else { | ||||||
|  |             itemBinding.metadataContentView.setText(content); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         itemBinding.metadataContentView.setClickable(true); | ||||||
|  |  | ||||||
|  |         layout.addView(itemBinding.getRoot()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void addTagsMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { | ||||||
|  |         final List<String> tags = getTags(); | ||||||
|  |  | ||||||
|  |         if (tags != null && !tags.isEmpty()) { | ||||||
|  |             final var itemBinding = ItemMetadataTagsBinding.inflate(inflater, layout, false); | ||||||
|  |  | ||||||
|  |             tags.stream().sorted(String.CASE_INSENSITIVE_ORDER).forEach(tag -> { | ||||||
|  |                 final Chip chip = (Chip) inflater.inflate(R.layout.chip, | ||||||
|  |                         itemBinding.metadataTagsChips, false); | ||||||
|  |                 chip.setText(tag); | ||||||
|  |                 chip.setOnClickListener(this::onTagClick); | ||||||
|  |                 chip.setOnLongClickListener(this::onTagLongClick); | ||||||
|  |                 itemBinding.metadataTagsChips.addView(chip); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             layout.addView(itemBinding.getRoot()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void onTagClick(final View chip) { | ||||||
|  |         if (getParentFragment() != null) { | ||||||
|  |             NavigationHelper.openSearchFragment(getParentFragment().getParentFragmentManager(), | ||||||
|  |                     getServiceId(), ((Chip) chip).getText().toString()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private boolean onTagLongClick(final View chip) { | ||||||
|  |         ShareUtils.copyToClipboard(requireContext(), ((Chip) chip).getText().toString()); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,46 +1,29 @@ | |||||||
| package org.schabi.newpipe.fragments.detail; | package org.schabi.newpipe.fragments.detail; | ||||||
|  |  | ||||||
| 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; | ||||||
| import static org.schabi.newpipe.extractor.utils.Utils.isBlank; |  | ||||||
| import static org.schabi.newpipe.util.Localization.getAppLocale; | import static org.schabi.newpipe.util.Localization.getAppLocale; | ||||||
| import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD; |  | ||||||
|  |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; |  | ||||||
| import android.widget.LinearLayout; | import android.widget.LinearLayout; | ||||||
|  |  | ||||||
| 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 com.google.android.material.chip.Chip; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.BaseFragment; |  | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.databinding.FragmentDescriptionBinding; | import org.schabi.newpipe.extractor.StreamingService; | ||||||
| import org.schabi.newpipe.databinding.ItemMetadataBinding; |  | ||||||
| import org.schabi.newpipe.databinding.ItemMetadataTagsBinding; |  | ||||||
| import org.schabi.newpipe.extractor.stream.Description; | import org.schabi.newpipe.extractor.stream.Description; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.util.Localization; | import org.schabi.newpipe.util.Localization; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; |  | ||||||
| import org.schabi.newpipe.util.external_communication.ShareUtils; | import java.util.List; | ||||||
| import org.schabi.newpipe.util.text.TextLinkifier; |  | ||||||
|  |  | ||||||
| import icepick.State; | import icepick.State; | ||||||
| import io.reactivex.rxjava3.disposables.CompositeDisposable; |  | ||||||
|  |  | ||||||
| public class DescriptionFragment extends BaseFragment { | public class DescriptionFragment extends BaseInfoFragment { | ||||||
|  |  | ||||||
|     @State |     @State | ||||||
|     StreamInfo streamInfo = null; |     StreamInfo streamInfo = null; | ||||||
|     final CompositeDisposable descriptionDisposables = new CompositeDisposable(); |  | ||||||
|     FragmentDescriptionBinding binding; |  | ||||||
|  |  | ||||||
|     public DescriptionFragment() { |     public DescriptionFragment() { | ||||||
|     } |     } | ||||||
| @@ -49,86 +32,56 @@ public class DescriptionFragment extends BaseFragment { | |||||||
|         this.streamInfo = streamInfo; |         this.streamInfo = streamInfo; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Nullable | ||||||
|     @Override |     @Override | ||||||
|     public View onCreateView(@NonNull final LayoutInflater inflater, |     protected Description getDescription() { | ||||||
|                              @Nullable final ViewGroup container, |         if (streamInfo == null) { | ||||||
|                              @Nullable final Bundle savedInstanceState) { |             return null; | ||||||
|         binding = FragmentDescriptionBinding.inflate(inflater, container, false); |  | ||||||
|         if (streamInfo != null) { |  | ||||||
|             setupUploadDate(); |  | ||||||
|             setupDescription(); |  | ||||||
|             setupMetadata(inflater, binding.detailMetadataLayout); |  | ||||||
|         } |         } | ||||||
|         return binding.getRoot(); |         return streamInfo.getDescription(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nullable | ||||||
|  |     @Override | ||||||
|  |     protected StreamingService getService() { | ||||||
|  |         if (streamInfo == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         return streamInfo.getService(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onDestroy() { |     protected int getServiceId() { | ||||||
|         descriptionDisposables.clear(); |         return streamInfo.getServiceId(); | ||||||
|         super.onDestroy(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Nullable | ||||||
|  |     @Override | ||||||
|  |     protected String getStreamUrl() { | ||||||
|  |         if (streamInfo == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         return streamInfo.getUrl(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private void setupUploadDate() { |     @Nullable | ||||||
|  |     @Override | ||||||
|  |     public List<String> getTags() { | ||||||
|  |         if (streamInfo == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         return streamInfo.getTags(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void setupMetadata(final LayoutInflater inflater, | ||||||
|  |                                  final LinearLayout layout) { | ||||||
|         if (streamInfo.getUploadDate() != null) { |         if (streamInfo.getUploadDate() != null) { | ||||||
|             binding.detailUploadDateView.setText(Localization |             binding.detailUploadDateView.setText(Localization | ||||||
|                     .localizeUploadDate(activity, streamInfo.getUploadDate().offsetDateTime())); |                     .localizeUploadDate(activity, streamInfo.getUploadDate().offsetDateTime())); | ||||||
|         } else { |         } else { | ||||||
|             binding.detailUploadDateView.setVisibility(View.GONE); |             binding.detailUploadDateView.setVisibility(View.GONE); | ||||||
|         } |         } | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     private void setupDescription() { |  | ||||||
|         final Description description = streamInfo.getDescription(); |  | ||||||
|         if (description == null || isEmpty(description.getContent()) |  | ||||||
|                 || description == Description.EMPTY_DESCRIPTION) { |  | ||||||
|             binding.detailDescriptionView.setVisibility(View.GONE); |  | ||||||
|             binding.detailSelectDescriptionButton.setVisibility(View.GONE); |  | ||||||
|             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 |  | ||||||
|         TextLinkifier.fromDescription(binding.detailDescriptionView, |  | ||||||
|                 streamInfo.getDescription(), HtmlCompat.FROM_HTML_MODE_LEGACY, |  | ||||||
|                 streamInfo.getService(), streamInfo.getUrl(), |  | ||||||
|                 descriptionDisposables, SET_LINK_MOVEMENT_METHOD); |  | ||||||
|  |  | ||||||
|         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 setupMetadata(final LayoutInflater inflater, |  | ||||||
|                                final LinearLayout layout) { |  | ||||||
|         addMetadataItem(inflater, layout, false, R.string.metadata_category, |         addMetadataItem(inflater, layout, false, R.string.metadata_category, | ||||||
|                 streamInfo.getCategory()); |                 streamInfo.getCategory()); | ||||||
|  |  | ||||||
| @@ -153,67 +106,6 @@ public class DescriptionFragment extends BaseFragment { | |||||||
|                 streamInfo.getHost()); |                 streamInfo.getHost()); | ||||||
|         addMetadataItem(inflater, layout, true, R.string.metadata_thumbnail_url, |         addMetadataItem(inflater, layout, true, R.string.metadata_thumbnail_url, | ||||||
|                 streamInfo.getThumbnailUrl()); |                 streamInfo.getThumbnailUrl()); | ||||||
|  |  | ||||||
|         addTagsMetadataItem(inflater, layout); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void addMetadataItem(final LayoutInflater inflater, |  | ||||||
|                                  final LinearLayout layout, |  | ||||||
|                                  final boolean linkifyContent, |  | ||||||
|                                  @StringRes final int type, |  | ||||||
|                                  @Nullable final String content) { |  | ||||||
|         if (isBlank(content)) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         final ItemMetadataBinding itemBinding = |  | ||||||
|                 ItemMetadataBinding.inflate(inflater, layout, false); |  | ||||||
|  |  | ||||||
|         itemBinding.metadataTypeView.setText(type); |  | ||||||
|         itemBinding.metadataTypeView.setOnLongClickListener(v -> { |  | ||||||
|             ShareUtils.copyToClipboard(requireContext(), content); |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         if (linkifyContent) { |  | ||||||
|             TextLinkifier.fromPlainText(itemBinding.metadataContentView, content, null, null, |  | ||||||
|                     descriptionDisposables, SET_LINK_MOVEMENT_METHOD); |  | ||||||
|         } else { |  | ||||||
|             itemBinding.metadataContentView.setText(content); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         itemBinding.metadataContentView.setClickable(true); |  | ||||||
|  |  | ||||||
|         layout.addView(itemBinding.getRoot()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void addTagsMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { |  | ||||||
|         if (streamInfo.getTags() != null && !streamInfo.getTags().isEmpty()) { |  | ||||||
|             final var itemBinding = ItemMetadataTagsBinding.inflate(inflater, layout, false); |  | ||||||
|  |  | ||||||
|             streamInfo.getTags().stream().sorted(String.CASE_INSENSITIVE_ORDER).forEach(tag -> { |  | ||||||
|                 final Chip chip = (Chip) inflater.inflate(R.layout.chip, |  | ||||||
|                         itemBinding.metadataTagsChips, false); |  | ||||||
|                 chip.setText(tag); |  | ||||||
|                 chip.setOnClickListener(this::onTagClick); |  | ||||||
|                 chip.setOnLongClickListener(this::onTagLongClick); |  | ||||||
|                 itemBinding.metadataTagsChips.addView(chip); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             layout.addView(itemBinding.getRoot()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void onTagClick(final View chip) { |  | ||||||
|         if (getParentFragment() != null) { |  | ||||||
|             NavigationHelper.openSearchFragment(getParentFragment().getParentFragmentManager(), |  | ||||||
|                     streamInfo.getServiceId(), ((Chip) chip).getText().toString()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private boolean onTagLongClick(final View chip) { |  | ||||||
|         ShareUtils.copyToClipboard(requireContext(), ((Chip) chip).getText().toString()); |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void addPrivacyMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { |     private void addPrivacyMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { | ||||||
|   | |||||||
| @@ -1,44 +1,28 @@ | |||||||
| package org.schabi.newpipe.fragments.list.channel; | package org.schabi.newpipe.fragments.list.channel; | ||||||
|  |  | ||||||
| import static org.schabi.newpipe.extractor.stream.StreamExtractor.UNKNOWN_SUBSCRIBER_COUNT; | import static org.schabi.newpipe.extractor.stream.StreamExtractor.UNKNOWN_SUBSCRIBER_COUNT; | ||||||
| import static org.schabi.newpipe.extractor.utils.Utils.isBlank; |  | ||||||
|  |  | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.os.Bundle; |  | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.View; |  | ||||||
| import android.view.ViewGroup; |  | ||||||
| import android.widget.LinearLayout; | import android.widget.LinearLayout; | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; |  | ||||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||||
| import androidx.annotation.StringRes; |  | ||||||
|  |  | ||||||
| import com.google.android.material.chip.Chip; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.BaseFragment; |  | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.databinding.FragmentChannelInfoBinding; | import org.schabi.newpipe.extractor.StreamingService; | ||||||
| import org.schabi.newpipe.databinding.ItemMetadataBinding; |  | ||||||
| import org.schabi.newpipe.databinding.ItemMetadataTagsBinding; |  | ||||||
| import org.schabi.newpipe.extractor.channel.ChannelInfo; | import org.schabi.newpipe.extractor.channel.ChannelInfo; | ||||||
|  | import org.schabi.newpipe.extractor.stream.Description; | ||||||
|  | import org.schabi.newpipe.fragments.detail.BaseInfoFragment; | ||||||
| import org.schabi.newpipe.util.Localization; | import org.schabi.newpipe.util.Localization; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; |  | ||||||
| import org.schabi.newpipe.util.external_communication.ShareUtils; |  | ||||||
| import org.schabi.newpipe.util.external_communication.TextLinkifier; |  | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import icepick.State; | import icepick.State; | ||||||
| import io.reactivex.rxjava3.disposables.CompositeDisposable; |  | ||||||
|  |  | ||||||
| public class ChannelInfoFragment extends BaseFragment { | public class ChannelInfoFragment extends BaseInfoFragment { | ||||||
|     @State |     @State | ||||||
|     protected ChannelInfo channelInfo; |     protected ChannelInfo channelInfo; | ||||||
|  |  | ||||||
|     private final CompositeDisposable disposables = new CompositeDisposable(); |  | ||||||
|     private FragmentChannelInfoBinding binding; |  | ||||||
|  |  | ||||||
|     public static ChannelInfoFragment getInstance(final ChannelInfo channelInfo) { |     public static ChannelInfoFragment getInstance(final ChannelInfo channelInfo) { | ||||||
|         final ChannelInfoFragment fragment = new ChannelInfoFragment(); |         final ChannelInfoFragment fragment = new ChannelInfoFragment(); | ||||||
|         fragment.channelInfo = channelInfo; |         fragment.channelInfo = channelInfo; | ||||||
| @@ -49,96 +33,51 @@ public class ChannelInfoFragment extends BaseFragment { | |||||||
|         super(); |         super(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Nullable | ||||||
|     @Override |     @Override | ||||||
|     public View onCreateView(@NonNull final LayoutInflater inflater, |     protected Description getDescription() { | ||||||
|                              @Nullable final ViewGroup container, |         if (channelInfo == null) { | ||||||
|                              final Bundle savedInstanceState) { |             return null; | ||||||
|         binding = FragmentChannelInfoBinding.inflate(inflater, container, false); |  | ||||||
|         loadDescription(); |  | ||||||
|         setupMetadata(inflater, binding.detailMetadataLayout); |  | ||||||
|         return binding.getRoot(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onDestroy() { |  | ||||||
|         super.onDestroy(); |  | ||||||
|         disposables.clear(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void loadDescription() { |  | ||||||
|         final String description = channelInfo.getDescription(); |  | ||||||
|  |  | ||||||
|         if (description == null || description.isEmpty()) { |  | ||||||
|             binding.descriptionTitle.setVisibility(View.GONE); |  | ||||||
|             binding.descriptionView.setVisibility(View.GONE); |  | ||||||
|         } else { |  | ||||||
|             TextLinkifier.createLinksFromPlainText( |  | ||||||
|                     binding.descriptionView, description, null, disposables); |  | ||||||
|         } |         } | ||||||
|  |         return new Description(channelInfo.getDescription(), Description.PLAIN_TEXT); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void setupMetadata(final LayoutInflater inflater, |     @Nullable | ||||||
|                                final LinearLayout layout) { |     @Override | ||||||
|  |     protected StreamingService getService() { | ||||||
|  |         if (channelInfo == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         return channelInfo.getService(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected int getServiceId() { | ||||||
|  |         return channelInfo.getServiceId(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nullable | ||||||
|  |     @Override | ||||||
|  |     protected String getStreamUrl() { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nullable | ||||||
|  |     @Override | ||||||
|  |     public List<String> getTags() { | ||||||
|  |         if (channelInfo == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         return channelInfo.getTags(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void setupMetadata(final LayoutInflater inflater, | ||||||
|  |                                  final LinearLayout layout) { | ||||||
|         final Context context = getContext(); |         final Context context = getContext(); | ||||||
|  |  | ||||||
|         if (channelInfo.getSubscriberCount() != UNKNOWN_SUBSCRIBER_COUNT) { |         if (channelInfo.getSubscriberCount() != UNKNOWN_SUBSCRIBER_COUNT) { | ||||||
|             addMetadataItem(inflater, layout, R.string.metadata_subscribers, |             addMetadataItem(inflater, layout, false, R.string.metadata_subscribers, | ||||||
|                     Localization.localizeNumber(context, channelInfo.getSubscriberCount())); |                     Localization.localizeNumber(context, channelInfo.getSubscriberCount())); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         addTagsMetadataItem(inflater, layout); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void addMetadataItem(final LayoutInflater inflater, |  | ||||||
|                                  final LinearLayout layout, |  | ||||||
|                                  @StringRes final int type, |  | ||||||
|                                  @Nullable final String content) { |  | ||||||
|         if (isBlank(content)) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         final ItemMetadataBinding itemBinding = |  | ||||||
|                 ItemMetadataBinding.inflate(inflater, layout, false); |  | ||||||
|  |  | ||||||
|         itemBinding.metadataTypeView.setText(type); |  | ||||||
|         itemBinding.metadataTypeView.setOnLongClickListener(v -> { |  | ||||||
|             ShareUtils.copyToClipboard(requireContext(), content); |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         itemBinding.metadataContentView.setText(content); |  | ||||||
|  |  | ||||||
|         layout.addView(itemBinding.getRoot()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void addTagsMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { |  | ||||||
|         final List<String> tags = channelInfo.getTags(); |  | ||||||
|  |  | ||||||
|         if (!tags.isEmpty()) { |  | ||||||
|             final var itemBinding = ItemMetadataTagsBinding.inflate(inflater, layout, false); |  | ||||||
|  |  | ||||||
|             tags.stream().sorted(String.CASE_INSENSITIVE_ORDER).forEach(tag -> { |  | ||||||
|                 final Chip chip = (Chip) inflater.inflate(R.layout.chip, |  | ||||||
|                         itemBinding.metadataTagsChips, false); |  | ||||||
|                 chip.setText(tag); |  | ||||||
|                 chip.setOnClickListener(this::onTagClick); |  | ||||||
|                 chip.setOnLongClickListener(this::onTagLongClick); |  | ||||||
|                 itemBinding.metadataTagsChips.addView(chip); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             layout.addView(itemBinding.getRoot()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void onTagClick(final View chip) { |  | ||||||
|         if (getParentFragment() != null) { |  | ||||||
|             NavigationHelper.openSearchFragment(getParentFragment().getParentFragmentManager(), |  | ||||||
|                     channelInfo.getServiceId(), ((Chip) chip).getText().toString()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private boolean onTagLongClick(final View chip) { |  | ||||||
|         ShareUtils.copyToClipboard(requireContext(), ((Chip) chip).getText().toString()); |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 ThetaDev
					ThetaDev