Merge pull request #4534 from Stypox/secondary-controls

Add a secondary control panel to video detail fragment
This commit is contained in:
Tobias Groza 2021-01-17 20:02:36 +01:00 committed by GitHub
commit 156adaa1a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 578 additions and 344 deletions

View File

@ -35,7 +35,7 @@ public abstract class BaseFragment extends Fragment {
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onAttach(final Context context) {
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
activity = (AppCompatActivity) context;
}
@ -61,7 +61,7 @@ public abstract class BaseFragment extends Fragment {
@Override
public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
if (DEBUG) {
Log.d(TAG, "onViewCreated() called with: "
@ -73,7 +73,7 @@ public abstract class BaseFragment extends Fragment {
}
@Override
public void onSaveInstanceState(final Bundle outState) {
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}

View File

@ -10,6 +10,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@ -56,7 +57,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
private TextView errorTextView;
@Override
public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
doInitialLoadLogic();
}

View File

@ -0,0 +1,93 @@
package org.schabi.newpipe.fragments.detail;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.text.HtmlCompat;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.databinding.FragmentDescriptionBinding;
import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.TextLinkifier;
import icepick.State;
import io.reactivex.rxjava3.disposables.Disposable;
import static android.text.TextUtils.isEmpty;
public class DescriptionFragment extends BaseFragment {
@State
StreamInfo streamInfo = null;
@Nullable
Disposable descriptionDisposable = null;
public DescriptionFragment() {
}
public DescriptionFragment(final StreamInfo streamInfo) {
this.streamInfo = streamInfo;
}
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
final FragmentDescriptionBinding binding =
FragmentDescriptionBinding.inflate(inflater, container, false);
if (streamInfo != null) {
setupUploadDate(binding.detailUploadDateView);
setupDescription(binding.detailDescriptionView);
}
return binding.getRoot();
}
@Override
public void onDestroy() {
super.onDestroy();
if (descriptionDisposable != null) {
descriptionDisposable.dispose();
}
}
private void setupUploadDate(final TextView uploadDateTextView) {
if (streamInfo.getUploadDate() != null) {
uploadDateTextView.setText(Localization
.localizeUploadDate(activity, streamInfo.getUploadDate().offsetDateTime()));
} else {
uploadDateTextView.setVisibility(View.GONE);
}
}
private void setupDescription(final TextView descriptionTextView) {
final Description description = streamInfo.getDescription();
if (description == null || isEmpty(description.getContent())
|| description == Description.emptyDescription) {
descriptionTextView.setText("");
return;
}
switch (description.getType()) {
case Description.HTML:
descriptionDisposable = TextLinkifier.createLinksFromHtmlBlock(requireContext(),
description.getContent(), descriptionTextView,
HtmlCompat.FROM_HTML_MODE_LEGACY);
break;
case Description.MARKDOWN:
descriptionDisposable = TextLinkifier.createLinksFromMarkdownText(requireContext(),
description.getContent(), descriptionTextView);
break;
case Description.PLAIN_TEXT: default:
descriptionDisposable = TextLinkifier.createLinksFromPlainText(requireContext(),
description.getContent(), descriptionTextView);
break;
}
}
}

View File

@ -10,7 +10,10 @@ import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@ -18,6 +21,7 @@ import android.os.Looper;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@ -28,16 +32,17 @@ import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
import androidx.core.text.HtmlCompat;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceManager;
@ -45,6 +50,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.tabs.TabLayout;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
@ -60,14 +66,12 @@ import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.EmptyFragment;
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment;
import org.schabi.newpipe.ktx.AnimationType;
@ -90,15 +94,15 @@ import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.KoreUtil;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.TextLinkifier;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.LargeTextMovementMethod;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@ -115,6 +119,7 @@ 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.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateRotation;
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;
@ -130,8 +135,6 @@ public final class VideoDetailFragment
OnKeyDownListener {
public static final String KEY_SWITCHING_PLAYERS = "switching_players";
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
private static final int COMMENTS_UPDATE_FLAG = 0x2;
private static final float MAX_OVERLAY_ALPHA = 0.9f;
private static final float MAX_PLAYER_HEIGHT = 0.7f;
@ -148,13 +151,17 @@ public final class VideoDetailFragment
private static final String COMMENTS_TAB_TAG = "COMMENTS";
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
private static final String EMPTY_TAB_TAG = "EMPTY TAB";
private static final String DESCRIPTION_TAB_TAG = "DESCRIPTION TAB";
private boolean showRelatedStreams;
// tabs
private boolean showComments;
private boolean showRelatedStreams;
private boolean showDescription;
private String selectedTabTag;
private int updateFlags = 0;
@AttrRes @NonNull final List<Integer> tabIcons = new ArrayList<>();
@StringRes @NonNull final List<Integer> tabContentDescriptions = new ArrayList<>();
private boolean tabSettingsChanged = false;
private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates
@State
protected int serviceId = Constants.NO_SERVICE_ID;
@ -193,6 +200,7 @@ public final class VideoDetailFragment
private TabAdapter pageAdapter;
private ContentObserver settingsContentObserver;
@Nullable
private MainPlayer playerService;
private Player player;
@ -272,17 +280,13 @@ public final class VideoDetailFragment
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showRelatedStreams = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(getString(R.string.show_next_video_key), true);
showComments = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(getString(R.string.show_comments_key), true);
selectedTabTag = PreferenceManager.getDefaultSharedPreferences(activity)
.getString(getString(R.string.stream_info_selected_tab_key), COMMENTS_TAB_TAG);
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
showComments = prefs.getBoolean(getString(R.string.show_comments_key), true);
showRelatedStreams = prefs.getBoolean(getString(R.string.show_next_video_key), true);
showDescription = prefs.getBoolean(getString(R.string.show_description_key), true);
selectedTabTag = prefs.getString(
getString(R.string.stream_info_selected_tab_key), COMMENTS_TAB_TAG);
prefs.registerOnSharedPreferenceChangeListener(this);
setupBroadcastReceiver();
@ -327,17 +331,12 @@ public final class VideoDetailFragment
setupBrightness();
if (updateFlags != 0) {
if (!isLoading.get() && currentInfo != null) {
if ((updateFlags & RELATED_STREAMS_UPDATE_FLAG) != 0) {
startLoading(false);
}
if ((updateFlags & COMMENTS_UPDATE_FLAG) != 0) {
startLoading(false);
}
if (tabSettingsChanged) {
tabSettingsChanged = false;
initTabs();
if (currentInfo != null) {
updateTabs(currentInfo);
}
updateFlags = 0;
}
// Check if it was loading when the fragment was stopped/paused
@ -412,12 +411,15 @@ public final class VideoDetailFragment
@Override
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences,
final String key) {
if (key.equals(getString(R.string.show_next_video_key))) {
showRelatedStreams = sharedPreferences.getBoolean(key, true);
updateFlags |= RELATED_STREAMS_UPDATE_FLAG;
} else if (key.equals(getString(R.string.show_comments_key))) {
if (key.equals(getString(R.string.show_comments_key))) {
showComments = sharedPreferences.getBoolean(key, true);
updateFlags |= COMMENTS_UPDATE_FLAG;
tabSettingsChanged = true;
} else if (key.equals(getString(R.string.show_next_video_key))) {
showRelatedStreams = sharedPreferences.getBoolean(key, true);
tabSettingsChanged = true;
} else if (key.equals(getString(R.string.show_description_key))) {
showComments = sharedPreferences.getBoolean(key, true);
tabSettingsChanged = true;
}
}
@ -452,6 +454,30 @@ public final class VideoDetailFragment
this.openDownloadDialog();
}
break;
case R.id.detail_controls_share:
if (currentInfo != null) {
ShareUtils.shareText(requireContext(),
currentInfo.getName(), currentInfo.getUrl());
}
break;
case R.id.detail_controls_open_in_browser:
if (currentInfo != null) {
ShareUtils.openUrlInBrowser(requireContext(), currentInfo.getUrl());
}
break;
case R.id.detail_controls_play_with_kodi:
if (currentInfo != null) {
try {
NavigationHelper.playWithKore(
requireContext(), Uri.parse(currentInfo.getUrl()));
} catch (final Exception e) {
if (DEBUG) {
Log.i(TAG, "Failed to start kore", e);
}
KoreUtil.showInstallKoreDialog(requireContext());
}
}
break;
case R.id.detail_uploader_root_layout:
if (isEmpty(currentInfo.getSubChannelUrl())) {
if (!isEmpty(currentInfo.getUploaderUrl())) {
@ -471,7 +497,7 @@ public final class VideoDetailFragment
openVideoPlayer();
break;
case R.id.detail_title_root_layout:
toggleTitleAndDescription();
toggleTitleAndSecondaryControls();
break;
case R.id.overlay_thumbnail:
case R.id.overlay_metadata_layout:
@ -542,21 +568,19 @@ public final class VideoDetailFragment
return true;
}
private void toggleTitleAndDescription() {
if (binding.detailDescriptionRootLayout.getVisibility() == View.VISIBLE) {
binding.detailVideoTitleView.setMaxLines(1);
binding.detailDescriptionRootLayout.setVisibility(View.GONE);
binding.detailDescriptionView.setFocusable(false);
binding.detailToggleDescriptionView.setImageResource(
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_expand_more));
} else {
private void toggleTitleAndSecondaryControls() {
if (binding.detailSecondaryControlPanel.getVisibility() == View.GONE) {
binding.detailVideoTitleView.setMaxLines(10);
binding.detailDescriptionRootLayout.setVisibility(View.VISIBLE);
binding.detailDescriptionView.setFocusable(true);
binding.detailDescriptionView.setMovementMethod(new LargeTextMovementMethod());
binding.detailToggleDescriptionView.setImageResource(
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_expand_less));
animateRotation(binding.detailToggleSecondaryControlsView,
Player.DEFAULT_CONTROLS_DURATION, 180);
binding.detailSecondaryControlPanel.setVisibility(View.VISIBLE);
} else {
binding.detailVideoTitleView.setMaxLines(1);
animateRotation(binding.detailToggleSecondaryControlsView,
Player.DEFAULT_CONTROLS_DURATION, 0);
binding.detailSecondaryControlPanel.setVisibility(View.GONE);
}
updateTabLayoutVisibility();
}
/*//////////////////////////////////////////////////////////////////////////
@ -582,6 +606,9 @@ public final class VideoDetailFragment
binding.detailControlsBackground.setBackgroundColor(transparent);
binding.detailControlsPopup.setBackgroundColor(transparent);
binding.detailControlsDownload.setBackgroundColor(transparent);
binding.detailControlsShare.setBackgroundColor(transparent);
binding.detailControlsOpenInBrowser.setBackgroundColor(transparent);
binding.detailControlsPlayWithKodi.setBackgroundColor(transparent);
}
}
@ -589,21 +616,24 @@ public final class VideoDetailFragment
protected void initListeners() {
super.initListeners();
binding.detailTitleRootLayout.setOnClickListener(this);
binding.detailTitleRootLayout.setOnLongClickListener(this);
binding.detailUploaderRootLayout.setOnClickListener(this);
binding.detailUploaderRootLayout.setOnLongClickListener(this);
binding.detailTitleRootLayout.setOnClickListener(this);
binding.detailThumbnailRootLayout.setOnClickListener(this);
binding.detailControlsBackground.setOnClickListener(this);
binding.detailControlsBackground.setOnLongClickListener(this);
binding.detailControlsPopup.setOnClickListener(this);
binding.detailControlsPopup.setOnLongClickListener(this);
binding.detailControlsPlaylistAppend.setOnClickListener(this);
binding.detailControlsDownload.setOnClickListener(this);
binding.detailControlsDownload.setOnLongClickListener(this);
binding.detailControlsBackground.setLongClickable(true);
binding.detailControlsPopup.setLongClickable(true);
binding.detailControlsBackground.setOnLongClickListener(this);
binding.detailControlsPopup.setOnLongClickListener(this);
binding.detailControlsShare.setOnClickListener(this);
binding.detailControlsOpenInBrowser.setOnClickListener(this);
binding.detailControlsPlayWithKodi.setOnClickListener(this);
binding.detailControlsPlayWithKodi.setVisibility(KoreUtil.shouldShowPlayWithKodi(
requireContext(), serviceId) ? View.VISIBLE : View.GONE);
binding.overlayThumbnail.setOnClickListener(this);
binding.overlayThumbnail.setOnLongClickListener(this);
@ -616,6 +646,14 @@ public final class VideoDetailFragment
binding.detailControlsBackground.setOnTouchListener(getOnControlsTouchListener());
binding.detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
binding.appBarLayout.addOnOffsetChangedListener((layout, verticalOffset) -> {
// prevent useless updates to tab layout visibility if nothing changed
if (verticalOffset != lastAppBarVerticalOffset) {
lastAppBarVerticalOffset = verticalOffset;
updateTabLayoutVisibility();
}
});
setupBottomPlayer();
if (!PlayerHolder.bound) {
setHeightThumbnail();
@ -873,37 +911,88 @@ public final class VideoDetailFragment
});
}
/*//////////////////////////////////////////////////////////////////////////
// Tabs
//////////////////////////////////////////////////////////////////////////*/
private void initTabs() {
if (pageAdapter.getCount() != 0) {
selectedTabTag = pageAdapter.getItemTitle(binding.viewPager.getCurrentItem());
}
pageAdapter.clearAllItems();
tabIcons.clear();
tabContentDescriptions.clear();
if (shouldShowComments()) {
pageAdapter.addFragment(
CommentsFragment.getInstance(serviceId, url, title), COMMENTS_TAB_TAG);
tabIcons.add(R.drawable.ic_comment_white_24dp);
tabContentDescriptions.add(R.string.comments_tab_description);
}
if (showRelatedStreams && binding.relatedStreamsLayout == null) {
//temp empty fragment. will be updated in handleResult
pageAdapter.addFragment(new Fragment(), RELATED_TAB_TAG);
tabIcons.add(R.drawable.ic_art_track_white_24dp);
tabContentDescriptions.add(R.string.related_streams_tab_description);
}
if (pageAdapter.getCount() == 0) {
pageAdapter.addFragment(new EmptyFragment(), EMPTY_TAB_TAG);
if (showDescription) {
// temp empty fragment. will be updated in handleResult
pageAdapter.addFragment(new Fragment(), DESCRIPTION_TAB_TAG);
tabIcons.add(R.drawable.ic_description_white_24dp);
tabContentDescriptions.add(R.string.description_tab_description);
}
pageAdapter.notifyDataSetUpdate();
if (pageAdapter.getCount() < 2) {
binding.tabLayout.setVisibility(View.GONE);
} else {
if (pageAdapter.getCount() >= 2) {
final int position = pageAdapter.getItemPositionByTitle(selectedTabTag);
if (position != -1) {
binding.viewPager.setCurrentItem(position);
}
binding.tabLayout.setVisibility(View.VISIBLE);
updateTabIconsAndContentDescriptions();
}
updateTabLayoutVisibility();
}
/**
* To be called whenever {@link #pageAdapter} is modified, since that triggers a refresh in
* {@link FragmentVideoDetailBinding#tabLayout} resetting all tab's icons and content
* descriptions. This reads icons from {@link #tabIcons} and content descriptions from
* {@link #tabContentDescriptions}, which are all set in {@link #initTabs()}.
*/
private void updateTabIconsAndContentDescriptions() {
for (int i = 0; i < tabIcons.size(); ++i) {
final TabLayout.Tab tab = binding.tabLayout.getTabAt(i);
if (tab != null) {
tab.setIcon(tabIcons.get(i));
tab.setContentDescription(tabContentDescriptions.get(i));
}
}
}
private void updateTabs(@NonNull final StreamInfo info) {
if (showRelatedStreams) {
if (binding.relatedStreamsLayout == null) { // phone
pageAdapter.updateItem(RELATED_TAB_TAG,
RelatedVideosFragment.getInstance(info));
} else { // tablet + TV
getChildFragmentManager().beginTransaction()
.replace(R.id.relatedStreamsLayout,
RelatedVideosFragment.getInstance(info))
.commitAllowingStateLoss();
binding.relatedStreamsLayout.setVisibility(
player != null && player.isFullscreen() ? View.GONE : View.VISIBLE);
}
}
if (showDescription) {
pageAdapter.updateItem(DESCRIPTION_TAB_TAG,
new DescriptionFragment(info));
}
pageAdapter.notifyDataSetUpdate();
updateTabIconsAndContentDescriptions();
}
private boolean shouldShowComments() {
@ -917,8 +1006,40 @@ public final class VideoDetailFragment
}
}
public void updateTabLayoutVisibility() {
if (pageAdapter.getCount() < 2) {
binding.tabLayout.setVisibility(View.GONE);
} else {
binding.tabLayout.post(() -> {
if (getContext() != null) {
final Rect pagerHitRect = new Rect();
binding.viewPager.getHitRect(pagerHitRect);
final Point displaySize = new Point();
Objects.requireNonNull(ContextCompat.getSystemService(getContext(),
WindowManager.class)).getDefaultDisplay().getSize(displaySize);
final int viewPagerVisibleHeight = displaySize.y - pagerHitRect.top;
// see TabLayout.DEFAULT_HEIGHT
final float tabLayoutHeight = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 48, getResources().getDisplayMetrics());
if (viewPagerVisibleHeight > tabLayoutHeight * 2) {
// no translation at all when viewPagerVisibleHeight > tabLayout.height * 3
binding.tabLayout.setTranslationY(
Math.max(0, tabLayoutHeight * 3 - viewPagerVisibleHeight));
binding.tabLayout.setVisibility(View.VISIBLE);
} else {
binding.tabLayout.setVisibility(View.GONE);
}
}
});
}
}
public void scrollToTop() {
binding.appBarLayout.setExpanded(true, true);
updateTabLayoutVisibility();
}
/*//////////////////////////////////////////////////////////////////////////
@ -1114,29 +1235,6 @@ public final class VideoDetailFragment
binding.playerPlaceholder.requestLayout();
}
private void prepareDescription(final Description description) {
if (description == null || isEmpty(description.getContent())
|| description == Description.emptyDescription) {
return;
}
switch (description.getType()) {
case Description.HTML:
disposables.add(TextLinkifier.createLinksFromHtmlBlock(requireContext(),
description.getContent(), binding.detailDescriptionView,
HtmlCompat.FROM_HTML_MODE_LEGACY));
break;
case Description.MARKDOWN:
disposables.add(TextLinkifier.createLinksFromMarkdownText(requireContext(),
description.getContent(), binding.detailDescriptionView));
break;
case Description.PLAIN_TEXT: default:
disposables.add(TextLinkifier.createLinksFromPlainText(requireContext(),
description.getContent(), binding.detailDescriptionView));
break;
}
}
private final ViewTreeObserver.OnPreDrawListener preDrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
@ -1309,9 +1407,9 @@ public final class VideoDetailFragment
binding.detailVideoTitleView.setMaxLines(1);
animate(binding.detailVideoTitleView, true, 0);
binding.detailDescriptionRootLayout.setVisibility(View.GONE);
binding.detailToggleDescriptionView.setVisibility(View.GONE);
binding.detailToggleSecondaryControlsView.setVisibility(View.GONE);
binding.detailTitleRootLayout.setClickable(false);
binding.detailSecondaryControlPanel.setVisibility(View.GONE);
if (binding.relatedStreamsLayout != null) {
if (showRelatedStreams) {
@ -1335,20 +1433,8 @@ public final class VideoDetailFragment
currentInfo = info;
setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName(), playQueue);
if (showRelatedStreams) {
if (binding.relatedStreamsLayout == null) { //phone
pageAdapter.updateItem(RELATED_TAB_TAG,
RelatedVideosFragment.getInstance(info));
pageAdapter.notifyDataSetUpdate();
} else { //tablet
getChildFragmentManager().beginTransaction()
.replace(R.id.relatedStreamsLayout,
RelatedVideosFragment.getInstance(info))
.commitAllowingStateLoss();
binding.relatedStreamsLayout.setVisibility(
player != null && player.isFullscreen() ? View.GONE : View.VISIBLE);
}
}
updateTabs(info);
animate(binding.detailThumbnailPlayButton, true, 200);
binding.detailVideoTitleView.setText(title);
@ -1357,7 +1443,7 @@ public final class VideoDetailFragment
} else if (!isEmpty(info.getUploaderName())) {
displayUploaderAsSubChannel(info);
} else {
binding.detailUploadDateView.setVisibility(View.GONE);
binding.detailUploaderTextView.setVisibility(View.GONE);
binding.detailUploaderThumbnailView.setVisibility(View.GONE);
}
@ -1425,21 +1511,10 @@ public final class VideoDetailFragment
binding.detailDurationView.setVisibility(View.GONE);
}
binding.detailDescriptionView.setVisibility(View.GONE);
binding.detailTitleRootLayout.setClickable(true);
binding.detailToggleDescriptionView.setImageResource(
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_expand_more));
binding.detailToggleDescriptionView.setVisibility(View.VISIBLE);
binding.detailDescriptionRootLayout.setVisibility(View.GONE);
if (info.getUploadDate() != null) {
binding.detailUploadDateView.setText(Localization
.localizeUploadDate(activity, info.getUploadDate().offsetDateTime()));
binding.detailUploadDateView.setVisibility(View.VISIBLE);
} else {
binding.detailUploadDateView.setText(null);
binding.detailUploadDateView.setVisibility(View.GONE);
}
binding.detailToggleSecondaryControlsView.setRotation(0);
binding.detailToggleSecondaryControlsView.setVisibility(View.VISIBLE);
binding.detailSecondaryControlPanel.setVisibility(View.GONE);
sortedVideoStreams = ListHelper.getSortedStreamVideosList(
activity,
@ -1448,7 +1523,6 @@ public final class VideoDetailFragment
false);
selectedVideoStreamIndex = ListHelper
.getDefaultResolutionIndex(activity, sortedVideoStreams);
prepareDescription(info.getDescription());
updateProgressInfo(info);
initThumbnailViews(info);
disposables.add(showMetaInfoInTextView(info.getMetaInfo(), binding.detailMetaInfoTextView,
@ -1494,7 +1568,7 @@ public final class VideoDetailFragment
binding.detailSubChannelTextView.setText(info.getUploaderName());
binding.detailSubChannelTextView.setVisibility(View.VISIBLE);
binding.detailSubChannelTextView.setSelected(true);
binding.detailUploadDateView.setVisibility(View.GONE);
binding.detailUploaderTextView.setVisibility(View.GONE);
}
private void displayBothUploaderAndSubChannel(final StreamInfo info) {
@ -1505,12 +1579,12 @@ public final class VideoDetailFragment
binding.detailSubChannelThumbnailView.setVisibility(View.VISIBLE);
if (!isEmpty(info.getUploaderName())) {
binding.detailUploadDateView.setText(
binding.detailUploaderTextView.setText(
String.format(getString(R.string.video_detail_by), info.getUploaderName()));
binding.detailUploadDateView.setVisibility(View.VISIBLE);
binding.detailUploadDateView.setSelected(true);
binding.detailUploaderTextView.setVisibility(View.VISIBLE);
binding.detailUploaderTextView.setSelected(true);
} else {
binding.detailUploadDateView.setVisibility(View.GONE);
binding.detailUploaderTextView.setVisibility(View.GONE);
}
}

View File

@ -70,7 +70,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onAttach(final Context context) {
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
if (infoListAdapter == null) {
@ -186,7 +186,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
}
@Override
public void onSaveInstanceState(final Bundle bundle) {
public void onSaveInstanceState(@NonNull final Bundle bundle) {
super.onSaveInstanceState(bundle);
if (useDefaultStateSaving) {
savedState = StateSaver

View File

@ -106,7 +106,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onAttach(final Context context) {
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
subscriptionManager = new SubscriptionManager(activity);
}
@ -119,7 +119,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
}
@Override
public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
channelBinding = FragmentChannelBinding.bind(rootView);
}

View File

@ -1,6 +1,5 @@
package org.schabi.newpipe.fragments.list.comments;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
@ -37,11 +36,6 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onAttach(final Context context) {
super.onAttach(context);
}
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@ -52,9 +46,7 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
@Override
public void onDestroy() {
super.onDestroy();
if (disposables != null) {
disposables.clear();
}
disposables.clear();
}
/*//////////////////////////////////////////////////////////////////////////

View File

@ -196,7 +196,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onAttach(final Context context) {
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
suggestionListAdapter = new SuggestionListAdapter(activity);
@ -226,7 +226,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
}
@Override
public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
showSearchOnStart();
initSearchListeners();
@ -390,7 +390,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
}
@Override
public void onSaveInstanceState(final Bundle bundle) {
public void onSaveInstanceState(@NonNull final Bundle bundle) {
searchString = searchEditText != null
? searchEditText.getText().toString()
: searchString;

View File

@ -52,7 +52,7 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onAttach(final Context context) {
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
}
@ -182,7 +182,7 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
}
@Override
public void onSaveInstanceState(final Bundle outState) {
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(INFO_KEY, relatedStreamInfo);
}

View File

@ -27,7 +27,7 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior<FrameLayout>
private boolean skippingInterception = false;
private final List<Integer> skipInterceptionOfElements = Arrays.asList(
R.id.detail_content_root_layout, R.id.relatedStreamsLayout,
R.id.itemsListPanel, R.id.view_pager, R.id.bottomControls,
R.id.itemsListPanel, R.id.view_pager, R.id.tab_layout, R.id.bottomControls,
R.id.playPauseButton, R.id.playPreviousButton, R.id.playNextButton);
@Override

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray" />
</shape>
</item>
</layer-list>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="6dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M22,13h-8v-2h8v2zM22,7h-8v2h8L22,7zM14,17h8v-2h-8v2zM12,9v6c0,1.1 -0.9,2 -2,2L4,17c-1.1,0 -2,-0.9 -2,-2L2,9c0,-1.1 0.9,-2 2,-2h6c1.1,0 2,0.9 2,2zM10.5,15l-2.25,-3 -1.75,2.26 -1.25,-1.51L3.5,15h7z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2L4,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z"/>
</vector>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/dot_selected" android:state_selected="true" />
<item android:drawable="@drawable/dot_default" />
</selector>

View File

@ -193,7 +193,7 @@
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum. Nunc eleifend est quis ipsum porttitor egestas. Sed facilisis, nisl quis eleifend pellentesque, orci metus egestas dolor, at accumsan eros metus quis libero." />
<ImageView
android:id="@+id/detail_toggle_description_view"
android:id="@+id/detail_toggle_secondary_controls_view"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="top|end"
@ -426,6 +426,7 @@
</RelativeLayout>
</RelativeLayout>
<!-- CONTROLS -->
<LinearLayout
android:id="@+id/detail_control_panel"
android:layout_width="match_parent"
@ -433,13 +434,12 @@
android:descendantFocusability="afterDescendants"
android:focusable="true"
android:orientation="horizontal"
android:padding="6dp">
android:padding="@dimen/detail_control_padding">
<!-- CONTROLS -->
<TextView
android:id="@+id/detail_controls_playlist_append"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
@ -447,16 +447,15 @@
android:contentDescription="@string/append_playlist"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/controls_add_to_playlist_title"
android:textSize="12sp"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_playlist_add" />
<TextView
android:id="@+id/detail_controls_background"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
@ -464,16 +463,15 @@
android:contentDescription="@string/play_audio"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/controls_background_title"
android:textSize="12sp"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_headset" />
<TextView
android:id="@+id/detail_controls_popup"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
@ -481,16 +479,15 @@
android:contentDescription="@string/open_in_popup_mode"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/controls_popup_title"
android:textSize="12sp"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_popup" />
<TextView
android:id="@+id/detail_controls_download"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
@ -498,14 +495,76 @@
android:contentDescription="@string/controls_download_desc"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/download"
android:textSize="12sp"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_file_download" />
</LinearLayout>
<!-- SECONDARY CONTROLS -->
<LinearLayout
android:id="@+id/detail_secondary_control_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="afterDescendants"
android:focusable="true"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/detail_control_padding"
android:paddingBottom="@dimen/detail_control_padding"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/detail_controls_share"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/share"
android:focusable="true"
android:gravity="center"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/share"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_share" />
<TextView
android:id="@+id/detail_controls_open_in_browser"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/open_in_browser"
android:focusable="true"
android:gravity="center"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/open_in_browser"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_language" />
<TextView
android:id="@+id/detail_controls_play_with_kodi"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/play_with_kodi_title"
android:focusable="true"
android:gravity="center"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/play_with_kodi_title"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_cast" />
</LinearLayout>
<View
android:id="@+id/detail_meta_info_separator"
android:layout_width="match_parent"
@ -530,74 +589,26 @@
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
<!--DESCRIPTIONS-->
<LinearLayout
android:id="@+id/detail_description_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:descendantFocusability="afterDescendants"
android:focusable="true"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/detail_upload_date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textStyle="bold"
tools:text="Published on Oct 2, 2009" />
<TextView
android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginTop="3dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:focusable="false"
android:nextFocusUp="@+id/detail_control_panel"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="true"
android:textSize="@dimen/video_item_detail_description_text_size"
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." />
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:paddingBottom="48dp"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_gravity="bottom|center"
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp" />
</androidx.viewpager.widget.ViewPager>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
app:tabIconTint="?attr/colorAccent"
app:tabBackground="?attr/windowBackground"
app:tabGravity="fill"
app:elevation="16dp"/>
</org.schabi.newpipe.views.FocusAwareCoordinator>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/detail_upload_date_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Published on Oct 2, 2009" />
<TextView
android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="true"
android:textSize="@dimen/video_item_detail_description_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/detail_upload_date_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." />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -180,7 +180,7 @@
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum. Nunc eleifend est quis ipsum porttitor egestas. Sed facilisis, nisl quis eleifend pellentesque, orci metus egestas dolor, at accumsan eros metus quis libero." />
<ImageView
android:id="@+id/detail_toggle_description_view"
android:id="@+id/detail_toggle_secondary_controls_view"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="top|end"
@ -413,18 +413,18 @@
</RelativeLayout>
</RelativeLayout>
<!-- CONTROLS -->
<LinearLayout
android:id="@+id/detail_control_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="6dp">
android:padding="@dimen/detail_control_padding">
<!-- CONTROLS -->
<TextView
android:id="@+id/detail_controls_playlist_append"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
@ -432,16 +432,15 @@
android:contentDescription="@string/append_playlist"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/controls_add_to_playlist_title"
android:textSize="12sp"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_playlist_add" />
<TextView
android:id="@+id/detail_controls_background"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
@ -449,16 +448,15 @@
android:contentDescription="@string/play_audio"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/controls_background_title"
android:textSize="12sp"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_headset" />
<TextView
android:id="@+id/detail_controls_popup"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
@ -466,16 +464,15 @@
android:contentDescription="@string/open_in_popup_mode"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/controls_popup_title"
android:textSize="12sp"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_popup" />
<TextView
android:id="@+id/detail_controls_download"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
@ -483,14 +480,74 @@
android:contentDescription="@string/controls_download_desc"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/download"
android:textSize="12sp"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_file_download" />
</LinearLayout>
<!-- SECONDARY CONTROLS -->
<LinearLayout
android:id="@+id/detail_secondary_control_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/detail_control_padding"
android:paddingBottom="@dimen/detail_control_padding"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/detail_controls_share"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/share"
android:focusable="true"
android:gravity="center"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/share"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_share" />
<TextView
android:id="@+id/detail_controls_open_in_browser"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/open_in_browser"
android:focusable="true"
android:gravity="center"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/open_in_browser"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_language" />
<TextView
android:id="@+id/detail_controls_play_with_kodi"
android:layout_width="@dimen/detail_control_width"
android:layout_height="@dimen/detail_control_height"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/play_with_kodi_title"
android:focusable="true"
android:gravity="center"
android:paddingVertical="@dimen/detail_control_padding"
android:text="@string/play_with_kodi_title"
android:textSize="@dimen/detail_control_text_size"
app:drawableTopCompat="?attr/ic_cast" />
</LinearLayout>
<View
android:id="@+id/detail_meta_info_separator"
android:layout_width="match_parent"
@ -515,70 +572,26 @@
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
<!--DESCRIPTIONS-->
<LinearLayout
android:id="@+id/detail_description_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/detail_upload_date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textStyle="bold"
tools:text="Published on Oct 2, 2009" />
<TextView
android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginTop="3dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="true"
android:textSize="@dimen/video_item_detail_description_text_size"
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." />
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:paddingBottom="48dp"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_gravity="bottom|center"
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp" />
</androidx.viewpager.widget.ViewPager>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
app:tabIconTint="?attr/colorAccent"
app:tabBackground="?attr/windowBackground"
app:tabGravity="fill"
app:elevation="16dp"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -84,6 +84,11 @@
<!-- Paddings & Margins -->
<dimen name="video_item_detail_like_margin">5dp</dimen>
<dimen name="video_item_detail_error_panel_margin">50dp</dimen>
<!-- Control panel -->
<dimen name="detail_control_text_size">12sp</dimen>
<dimen name="detail_control_width">80dp</dimen>
<dimen name="detail_control_height">55dp</dimen>
<dimen name="detail_control_padding">6dp</dimen>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>

View File

@ -197,8 +197,9 @@
<!-- Content & History -->
<string name="show_search_suggestions_key" translatable="false">show_search_suggestions</string>
<string name="show_play_with_kodi_key" translatable="false">show_play_with_kodi</string>
<string name="show_next_video_key" translatable="false">show_next_video</string>
<string name="show_comments_key" translatable="false">show_comments</string>
<string name="show_next_video_key" translatable="false">show_next_video</string>
<string name="show_description_key" translatable="false">show_description</string>
<string name="show_meta_info_key" translatable="false">show_meta_info</string>
<string name="stream_info_selected_tab_key" translatable="false">stream_info_selected_tab</string>
<string name="show_hold_to_append_key" translatable="false">show_hold_to_append</string>

View File

@ -91,9 +91,12 @@
<string name="clear_queue_confirmation_summary">Switching from one player to another may replace your queue</string>
<string name="clear_queue_confirmation_description">The active player queue will be replaced</string>
<string name="download_thumbnail_title">Load thumbnails</string>
<string name="download_thumbnail_summary">Turn off to prevent loading thumbnails, saving data and memory usage. Changes clear both in-memory and on-disk image cache.</string>
<string name="show_comments_title">Show comments</string>
<string name="show_comments_summary">Turn off to hide comments</string>
<string name="download_thumbnail_summary">Turn off to prevent loading thumbnails, saving data and memory usage. Changes clear both in-memory and on-disk image cache.</string>
<string name="show_next_and_similar_title">Show \'Next\' and \'Similar\' videos</string>
<string name="show_description_title">Show description</string>
<string name="show_description_summary">Turn off to hide video description and additional information</string>
<string name="show_meta_info_title">Show meta info</string>
<string name="show_meta_info_summary">Turn off to hide meta info boxes with additional information about the stream creator, stream content or a search request.</string>
<string name="thumbnail_cache_wipe_complete_notice">Image cache wiped</string>
@ -124,7 +127,6 @@
<string name="resume_on_audio_focus_gain_summary">Continue playing after interruptions (e.g. phonecalls)</string>
<string name="download_dialog_title">Download</string>
<string name="autoplay_title">Autoplay</string>
<string name="show_next_and_similar_title">Show \'Next\' and \'Similar\' videos</string>
<string name="show_hold_to_append_title">Show \"Hold to append\" tip</string>
<string name="show_hold_to_append_summary">Show tip when pressing the background or the popup button in video \"Details:\"</string>
<string name="unsupported_url">Unsupported URL</string>
@ -279,6 +281,9 @@
<string name="detail_uploader_thumbnail_view_description">Uploader\'s avatar thumbnail</string>
<string name="detail_likes_img_view_description">Likes</string>
<string name="detail_dislikes_img_view_description">Dislikes</string>
<string name="comments_tab_description">Comments</string>
<string name="related_streams_tab_description">Related streams</string>
<string name="description_tab_description">Description</string>
<string name="use_tor_title">Use Tor</string>
<string name="use_tor_summary">(Experimental) Force download traffic through Tor for increased privacy (streaming videos not yet supported).</string>
<string name="report_error">Report error</string>

View File

@ -72,6 +72,13 @@
android:title="@string/download_thumbnail_title"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/show_comments_key"
android:summary="@string/show_comments_summary"
android:title="@string/show_comments_title"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/show_next_video_key"
@ -80,9 +87,9 @@
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/show_comments_key"
android:summary="@string/show_comments_summary"
android:title="@string/show_comments_title"
android:key="@string/show_description_key"
android:summary="@string/show_description_summary"
android:title="@string/show_description_title"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat