diff --git a/app/src/main/java/android/support/design/widget/FlingBehavior.java b/app/src/main/java/android/support/design/widget/FlingBehavior.java new file mode 100644 index 000000000..217e3ae3a --- /dev/null +++ b/app/src/main/java/android/support/design/widget/FlingBehavior.java @@ -0,0 +1,143 @@ +package android.support.design.widget; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.util.AttributeSet; +import android.view.View; + +// check this https://github.com/ToDou/appbarlayout-spring-behavior/blob/master/appbarspring/src/main/java/android/support/design/widget/AppBarFlingFixBehavior.java +public final class FlingBehavior extends AppBarLayout.Behavior { + + private ValueAnimator mOffsetAnimator; + private static final int MAX_OFFSET_ANIMATION_DURATION = 600; // ms + + public FlingBehavior() { + } + + public FlingBehavior(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) { + return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type); + } + + @Override + public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) { + if (dy != 0) { + int val = child.getBottom(); + if (val != 0) { + int min, max; + if (dy < 0) { + // We're scrolling down + } else { + // We're scrolling up + min = -child.getUpNestedPreScrollRange(); + max = 0; + consumed[1] = scroll(coordinatorLayout, child, dy, min, max); + } + } + } + } + + @Override + public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) { + super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type); + } + + @Override + public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) { + super.onStopNestedScroll(coordinatorLayout, abl, target, type); + } + + @Override + public boolean onMeasureChild(CoordinatorLayout parent, AppBarLayout child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { + return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); + } + + @Override + public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout abl) { + return super.onSaveInstanceState(parent, abl); + } + + @Override + public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout, Parcelable state) { + super.onRestoreInstanceState(parent, appBarLayout, state); + } + + @Override + public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull AppBarLayout child, @NonNull View target, float velocityX, float velocityY) { + + if (velocityY != 0) { + if (velocityY < 0) { + // We're flinging down + int val = child.getBottom(); + if (val != 0) { + final int targetScroll = + +child.getDownNestedPreScrollRange(); + animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY); + } + + } else { + // We're flinging up + int val = child.getBottom(); + if (val != 0) { + final int targetScroll = -child.getUpNestedPreScrollRange(); + if (getTopBottomOffsetForScrollingSibling() > targetScroll) { + animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY); + } + } + } + } + + return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); + } + + private void animateOffsetTo(final CoordinatorLayout coordinatorLayout, + final AppBarLayout child, final int offset, float velocity) { + final int distance = Math.abs(getTopBottomOffsetForScrollingSibling() - offset); + + final int duration; + velocity = Math.abs(velocity); + if (velocity > 0) { + duration = 3 * Math.round(1000 * (distance / velocity)); + } else { + final float distanceRatio = (float) distance / child.getHeight(); + duration = (int) ((distanceRatio + 1) * 150); + } + + animateOffsetWithDuration(coordinatorLayout, child, offset, duration); + } + + private void animateOffsetWithDuration(final CoordinatorLayout coordinatorLayout, + final AppBarLayout child, final int offset, final int duration) { + final int currentOffset = getTopBottomOffsetForScrollingSibling(); + if (currentOffset == offset) { + if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) { + mOffsetAnimator.cancel(); + } + return; + } + + if (mOffsetAnimator == null) { + mOffsetAnimator = new ValueAnimator(); + mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); + mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + setHeaderTopBottomOffset(coordinatorLayout, child, + (Integer) animator.getAnimatedValue()); + } + }); + } else { + mOffsetAnimator.cancel(); + } + + mOffsetAnimator.setDuration(Math.min(duration, MAX_OFFSET_ANIMATION_DURATION)); + mOffsetAnimator.setIntValues(currentOffset, offset); + mOffsetAnimator.start(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdaptor.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdaptor.java index 2dd7071be..3ed247e50 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdaptor.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdaptor.java @@ -1,10 +1,8 @@ package org.schabi.newpipe.fragments.detail; -import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.PagerAdapter; import java.util.ArrayList; import java.util.List; @@ -29,12 +27,6 @@ public class TabAdaptor extends FragmentPagerAdapter { return mFragmentList.size(); } - @Nullable - @Override - public CharSequence getPageTitle(int position) { - return mFragmentTitleList.get(position); - } - @Override public long getItemId(int position) { // give an ID different from position when position has been changed diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 76047b725..d3f602ebd 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -29,7 +29,6 @@ import android.text.method.LinkMovementMethod; import android.text.util.Linkify; import android.util.DisplayMetrics; import android.util.Log; -import android.util.TypedValue; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -39,7 +38,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.FrameLayout; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; @@ -56,8 +54,6 @@ import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.download.DownloadDialog; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.StreamingService; -import org.schabi.newpipe.extractor.comments.CommentsInfo; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ParsingException; @@ -72,7 +68,6 @@ import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.list.comments.CommentsFragment; import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment; -import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.local.history.HistoryRecordManager; @@ -91,11 +86,9 @@ import org.schabi.newpipe.util.InfoCache; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.StreamItemAdapter; import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; -import org.schabi.newpipe.util.ThemeHelper; import java.io.Serializable; import java.util.Collection; @@ -964,24 +957,6 @@ public class VideoDetailFragment })); } - private View getSeparatorView() { - View separator = new View(activity); - LinearLayout.LayoutParams params = - new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1); - int m8 = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics()); - int m5 = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()); - params.setMargins(m8, m5, m8, m5); - separator.setLayoutParams(params); - - TypedValue typedValue = new TypedValue(); - activity.getTheme().resolveAttribute(R.attr.separator_color, typedValue, true); - separator.setBackgroundColor(typedValue.data); - - return separator; - } - private void setHeightThumbnail() { final DisplayMetrics metrics = getResources().getDisplayMetrics(); boolean isPortrait = metrics.heightPixels > metrics.widthPixels; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index cd557c931..672d8c2be 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -22,9 +22,9 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; -import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.util.NavigationHelper; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedVideosFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedVideosFragment.java index 08a6a3bc3..44c7c6787 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedVideosFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedVideosFragment.java @@ -1,7 +1,9 @@ package org.schabi.newpipe.fragments.list.videos; import android.content.Context; +import android.content.SharedPreferences; import android.os.Bundle; +import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.LayoutInflater; @@ -9,34 +11,31 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.Switch; import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor; -import org.schabi.newpipe.extractor.ListInfo; import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.comments.CommentsInfo; -import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.kiosk.KioskInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; -import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.report.UserAction; -import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.RelatedStreamInfo; -import java.util.List; +import java.io.Serializable; import io.reactivex.Single; import io.reactivex.disposables.CompositeDisposable; -public class RelatedVideosFragment extends BaseListInfoFragment { +public class RelatedVideosFragment extends BaseListInfoFragment implements SharedPreferences.OnSharedPreferenceChangeListener{ private CompositeDisposable disposables = new CompositeDisposable(); private RelatedStreamInfo relatedStreamInfo; /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ + private View headerRootLayout; + private Switch aSwitch; @@ -74,6 +73,28 @@ public class RelatedVideosFragment extends BaseListInfoFragment loadMoreItemsLogic() { return Single.fromCallable(() -> ListExtractor.InfoItemsPage.emptyPage()); @@ -145,6 +166,33 @@ public class RelatedVideosFragment extends BaseListInfoFragment { + private StreamInfoItem nextStream; public RelatedStreamInfo(int serviceId, ListLinkHandler listUrlIdHandler, String name) { super(serviceId, listUrlIdHandler, name); @@ -17,7 +21,21 @@ public class RelatedStreamInfo extends ListInfo { public static RelatedStreamInfo getInfo(StreamInfo info) { ListLinkHandler handler = new ListLinkHandler(info.getOriginalUrl(), info.getUrl(), info.getId(), Collections.emptyList(), null); RelatedStreamInfo relatedStreamInfo = new RelatedStreamInfo(info.getServiceId(), handler, info.getName()); - relatedStreamInfo.setRelatedItems(info.getRelatedStreams()); - return relatedStreamInfo; + List streams = new ArrayList<>(); + if(info.getNextVideo() != null){ + streams.add(info.getNextVideo()); + } + streams.addAll(info.getRelatedStreams()); + relatedStreamInfo.setRelatedItems(streams); + relatedStreamInfo.setNextStream(info.getNextVideo()); + return relatedStreamInfo; + } + + public StreamInfoItem getNextStream() { + return nextStream; + } + + public void setNextStream(StreamInfoItem nextStream) { + this.nextStream = nextStream; } } diff --git a/app/src/main/res/drawable/default_dot.xml b/app/src/main/res/drawable/default_dot.xml new file mode 100644 index 000000000..3380dca3b --- /dev/null +++ b/app/src/main/res/drawable/default_dot.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selected_dot.xml b/app/src/main/res/drawable/selected_dot.xml new file mode 100644 index 000000000..017e99d43 --- /dev/null +++ b/app/src/main/res/drawable/selected_dot.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_selector.xml b/app/src/main/res/drawable/tab_selector.xml new file mode 100644 index 000000000..b7307674b --- /dev/null +++ b/app/src/main/res/drawable/tab_selector.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_comments.xml b/app/src/main/res/layout/fragment_comments.xml index 57ca28688..9ace63d4d 100644 --- a/app/src/main/res/layout/fragment_comments.xml +++ b/app/src/main/res/layout/fragment_comments.xml @@ -65,6 +65,7 @@ android:layout_width="match_parent" android:layout_height="4dp" android:background="?attr/toolbar_shadow_drawable" - android:layout_alignParentTop="true"/> + android:layout_alignParentTop="true" + android:visibility="gone"/> diff --git a/app/src/main/res/layout/fragment_related_streams.xml b/app/src/main/res/layout/fragment_related_streams.xml index 36c9c1d31..c12630392 100644 --- a/app/src/main/res/layout/fragment_related_streams.xml +++ b/app/src/main/res/layout/fragment_related_streams.xml @@ -65,6 +65,7 @@ android:layout_width="match_parent" android:layout_height="4dp" android:background="?attr/toolbar_shadow_drawable" - android:layout_alignParentTop="true"/> + android:layout_alignParentTop="true" + android:visibility="gone"/> diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml index 25eac35cf..906246bd0 100644 --- a/app/src/main/res/layout/fragment_video_detail.xml +++ b/app/src/main/res/layout/fragment_video_detail.xml @@ -10,12 +10,16 @@ + android:layout_height="match_parent" + android:fitsSystemWindows="true"> + android:layout_height="wrap_content" + android:fitsSystemWindows="true" + app:elevation="0dp" + app:layout_behavior="android.support.design.widget.FlingBehavior"> - - - - + + + + + + diff --git a/app/src/main/res/layout/related_streams_header.xml b/app/src/main/res/layout/related_streams_header.xml new file mode 100644 index 000000000..5be7c928d --- /dev/null +++ b/app/src/main/res/layout/related_streams_header.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bd00ddce1..354778e6d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -95,7 +95,8 @@ Resume on focus gain Continue playing after interruptions (e.g. phone calls) Download - Next video + Up next + Autoplay Show \'next\' and \'similar\' videos Show \"hold to append\" tip Show tip when background or popup button is pressed on video details page