Merge branch 'dev'

This commit is contained in:
TobiGr 2020-07-26 14:40:57 +02:00
commit d6d8c7830c
11 changed files with 57 additions and 95 deletions

View File

@ -13,8 +13,8 @@ android {
resValue "string", "app_name", "NewPipe" resValue "string", "app_name", "NewPipe"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 29 targetSdkVersion 29
versionCode 951 versionCode 952
versionName "0.19.6" versionName "0.19.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -163,7 +163,7 @@ dependencies {
exclude module: 'support-annotations' exclude module: 'support-annotations'
} }
implementation 'com.github.TeamNewPipe:NewPipeExtractor:a70cb0283ffc3bba2709815673a5a7940aab0a3a' implementation 'com.github.TeamNewPipe:NewPipeExtractor:b4481dfec21cf39aabbb791290d30130235aeabd'
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
implementation "org.jsoup:jsoup:1.13.1" implementation "org.jsoup:jsoup:1.13.1"

View File

@ -9,6 +9,7 @@ import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache; import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
@ -37,7 +38,6 @@ import java.net.SocketException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import io.reactivex.annotations.NonNull;
import io.reactivex.exceptions.CompositeException; import io.reactivex.exceptions.CompositeException;
import io.reactivex.exceptions.MissingBackpressureException; import io.reactivex.exceptions.MissingBackpressureException;
import io.reactivex.exceptions.OnErrorNotImplementedException; import io.reactivex.exceptions.OnErrorNotImplementedException;

View File

@ -365,10 +365,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
public void onSaveInstanceState(final Bundle outState) { public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
// Check if the next video label and video is visible,
// if it is, include the two elements in the next check
int nextCount = currentInfo != null && currentInfo.getNextVideo() != null ? 2 : 0;
if (!isLoading.get() && currentInfo != null && isVisible()) { if (!isLoading.get() && currentInfo != null && isVisible()) {
outState.putSerializable(INFO_KEY, currentInfo); outState.putSerializable(INFO_KEY, currentInfo);
} }
@ -1018,14 +1014,14 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
} }
private void prepareDescription(final Description description) { private void prepareDescription(final Description description) {
if (TextUtils.isEmpty(description.getContent()) if (description == null || TextUtils.isEmpty(description.getContent())
|| description == Description.emptyDescription) { || description == Description.emptyDescription) {
return; return;
} }
if (description.getType() == Description.HTML) { if (description.getType() == Description.HTML) {
disposables.add(Single.just(description.getContent()) disposables.add(Single.just(description.getContent())
.map((@io.reactivex.annotations.NonNull String descriptionText) -> { .map((@NonNull String descriptionText) -> {
Spanned parsedDescription; Spanned parsedDescription;
if (Build.VERSION.SDK_INT >= 24) { if (Build.VERSION.SDK_INT >= 24) {
parsedDescription = Html.fromHtml(descriptionText, 0); parsedDescription = Html.fromHtml(descriptionText, 0);
@ -1037,7 +1033,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
}) })
.subscribeOn(Schedulers.computation()) .subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe((@io.reactivex.annotations.NonNull Spanned spanned) -> { .subscribe((@NonNull Spanned spanned) -> {
videoDescriptionView.setText(spanned); videoDescriptionView.setText(spanned);
videoDescriptionView.setVisibility(View.VISIBLE); videoDescriptionView.setVisibility(View.VISIBLE);
})); }));

View File

@ -158,11 +158,10 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doFinally(this::allowDownwardFocusScroll) .doFinally(this::allowDownwardFocusScroll)
.subscribe((@io.reactivex.annotations.NonNull .subscribe((@NonNull ListExtractor.InfoItemsPage InfoItemsPage) -> {
ListExtractor.InfoItemsPage InfoItemsPage) -> {
isLoading.set(false); isLoading.set(false);
handleNextItems(InfoItemsPage); handleNextItems(InfoItemsPage);
}, (@io.reactivex.annotations.NonNull Throwable throwable) -> { }, (@NonNull Throwable throwable) -> {
isLoading.set(false); isLoading.set(false);
onError(throwable); onError(throwable);
}); });

View File

@ -9,7 +9,6 @@ import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Switch; import android.widget.Switch;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -40,9 +39,7 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private View headerRootLayout; private View headerRootLayout;
private Switch aSwitch; private Switch autoplaySwitch;
private boolean mIsVisibleToUser = false;
public static RelatedVideosFragment getInstance(final StreamInfo info) { public static RelatedVideosFragment getInstance(final StreamInfo info) {
RelatedVideosFragment instance = new RelatedVideosFragment(); RelatedVideosFragment instance = new RelatedVideosFragment();
@ -50,12 +47,6 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
return instance; return instance;
} }
@Override
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
mIsVisibleToUser = isVisibleToUser;
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// LifeCycle // LifeCycle
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -81,22 +72,18 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
} }
protected View getListHeader() { protected View getListHeader() {
if (relatedStreamInfo != null && relatedStreamInfo.getNextStream() != null) { if (relatedStreamInfo != null && relatedStreamInfo.getRelatedItems() != null) {
headerRootLayout = activity.getLayoutInflater() headerRootLayout = activity.getLayoutInflater()
.inflate(R.layout.related_streams_header, itemsList, false); .inflate(R.layout.related_streams_header, itemsList, false);
aSwitch = headerRootLayout.findViewById(R.id.autoplay_switch); autoplaySwitch = headerRootLayout.findViewById(R.id.autoplay_switch);
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext()); final SharedPreferences pref = PreferenceManager
Boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false); .getDefaultSharedPreferences(getContext());
aSwitch.setChecked(autoplay); final boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false);
aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { autoplaySwitch.setChecked(autoplay);
@Override autoplaySwitch.setOnCheckedChangeListener((compoundButton, b) ->
public void onCheckedChanged(final CompoundButton compoundButton,
final boolean b) {
PreferenceManager.getDefaultSharedPreferences(getContext()).edit() PreferenceManager.getDefaultSharedPreferences(getContext()).edit()
.putBoolean(getString(R.string.auto_queue_key), b).apply(); .putBoolean(getString(R.string.auto_queue_key), b).apply());
}
});
return headerRootLayout; return headerRootLayout;
} else { } else {
return null; return null;
@ -105,7 +92,7 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
@Override @Override
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() { protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
return Single.fromCallable(() -> ListExtractor.InfoItemsPage.emptyPage()); return Single.fromCallable(ListExtractor.InfoItemsPage::emptyPage);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -216,8 +203,8 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
final String s) { final String s) {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false); boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false);
if (null != aSwitch) { if (autoplaySwitch != null) {
aSwitch.setChecked(autoplay); autoplaySwitch.setChecked(autoplay);
} }
} }

View File

@ -132,17 +132,17 @@ public final class PlayerHelper {
} }
/** /**
* Given a {@link StreamInfo} and the existing queue items, provide the * Given a {@link StreamInfo} and the existing queue items,
* {@link SinglePlayQueue} consisting of the next video for auto queuing. * provide the {@link SinglePlayQueue} consisting of the next video for auto queueing.
* <p> * <p>
* This method detects and prevents cycle by naively checking if a * This method detects and prevents cycles by naively checking
* candidate next video's url already exists in the existing items. * if a candidate next video's url already exists in the existing items.
* </p> * </p>
* <p> * <p>
* To select the next video, {@link StreamInfo#getNextVideo()} is first * The first item in {@link StreamInfo#getRelatedStreams()} is checked first.
* checked. If it is nonnull and is not part of the existing items, then * If it is non-null and is not part of the existing items, it will be used as the next stream.
* it will be used as the next video. Otherwise, an random item with * Otherwise, a random item with non-repeating url will be selected
* non-repeating url will be selected from the {@link StreamInfo#getRelatedStreams()}. * from the {@link StreamInfo#getRelatedStreams()}.
* </p> * </p>
* *
* @param info currently playing stream * @param info currently playing stream
@ -152,27 +152,28 @@ public final class PlayerHelper {
@Nullable @Nullable
public static PlayQueue autoQueueOf(@NonNull final StreamInfo info, public static PlayQueue autoQueueOf(@NonNull final StreamInfo info,
@NonNull final List<PlayQueueItem> existingItems) { @NonNull final List<PlayQueueItem> existingItems) {
Set<String> urls = new HashSet<>(existingItems.size()); final Set<String> urls = new HashSet<>(existingItems.size());
for (final PlayQueueItem item : existingItems) { for (final PlayQueueItem item : existingItems) {
urls.add(item.getUrl()); urls.add(item.getUrl());
} }
final StreamInfoItem nextVideo = info.getNextVideo();
if (nextVideo != null && !urls.contains(nextVideo.getUrl())) {
return getAutoQueuedSinglePlayQueue(nextVideo);
}
final List<InfoItem> relatedItems = info.getRelatedStreams(); final List<InfoItem> relatedItems = info.getRelatedStreams();
if (relatedItems == null) { if (relatedItems == null) {
return null; return null;
} }
List<StreamInfoItem> autoQueueItems = new ArrayList<>(); if (relatedItems.get(0) != null && relatedItems.get(0) instanceof StreamInfoItem
for (final InfoItem item : info.getRelatedStreams()) { && !urls.contains(relatedItems.get(0).getUrl())) {
return getAutoQueuedSinglePlayQueue((StreamInfoItem) relatedItems.get(0));
}
final List<StreamInfoItem> autoQueueItems = new ArrayList<>();
for (final InfoItem item : relatedItems) {
if (item instanceof StreamInfoItem && !urls.contains(item.getUrl())) { if (item instanceof StreamInfoItem && !urls.contains(item.getUrl())) {
autoQueueItems.add((StreamInfoItem) item); autoQueueItems.add((StreamInfoItem) item);
} }
} }
Collections.shuffle(autoQueueItems); Collections.shuffle(autoQueueItems);
return autoQueueItems.isEmpty() return autoQueueItems.isEmpty()
? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0)); ? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0));

View File

@ -2,6 +2,8 @@ package org.schabi.newpipe.player.playqueue;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.ListInfo; import org.schabi.newpipe.extractor.ListInfo;
@ -13,7 +15,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import io.reactivex.SingleObserver; import io.reactivex.SingleObserver;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue { abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue {

View File

@ -6,6 +6,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
@ -20,7 +21,6 @@ import org.schabi.newpipe.util.FallbackViewHolder;
import java.util.List; import java.util.List;
import io.reactivex.Observer; import io.reactivex.Observer;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
/** /**

View File

@ -4,16 +4,12 @@ import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListInfo; import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
public class RelatedStreamInfo extends ListInfo<InfoItem> { public class RelatedStreamInfo extends ListInfo<InfoItem> {
private StreamInfoItem nextStream;
public RelatedStreamInfo(final int serviceId, final ListLinkHandler listUrlIdHandler, public RelatedStreamInfo(final int serviceId, final ListLinkHandler listUrlIdHandler,
final String name) { final String name) {
super(serviceId, listUrlIdHandler, name); super(serviceId, listUrlIdHandler, name);
@ -25,20 +21,8 @@ public class RelatedStreamInfo extends ListInfo<InfoItem> {
RelatedStreamInfo relatedStreamInfo = new RelatedStreamInfo( RelatedStreamInfo relatedStreamInfo = new RelatedStreamInfo(
info.getServiceId(), handler, info.getName()); info.getServiceId(), handler, info.getName());
List<InfoItem> streams = new ArrayList<>(); List<InfoItem> streams = new ArrayList<>();
if (info.getNextVideo() != null) {
streams.add(info.getNextVideo());
}
streams.addAll(info.getRelatedStreams()); streams.addAll(info.getRelatedStreams());
relatedStreamInfo.setRelatedItems(streams); relatedStreamInfo.setRelatedItems(streams);
relatedStreamInfo.setNextStream(info.getNextVideo());
return relatedStreamInfo; return relatedStreamInfo;
} }
public StreamInfoItem getNextStream() {
return nextStream;
}
public void setNextStream(final StreamInfoItem nextStream) {
this.nextStream = nextStream;
}
} }

View File

@ -74,44 +74,26 @@ public final class FocusOverlayView extends Drawable implements
@Override @Override
public void onGlobalFocusChanged(final View oldFocus, final View newFocus) { public void onGlobalFocusChanged(final View oldFocus, final View newFocus) {
int l = focusRect.left; if (newFocus != null) {
int r = focusRect.right;
int t = focusRect.top;
int b = focusRect.bottom;
if (newFocus != null && newFocus.getWidth() > 0 && newFocus.getHeight() > 0) {
newFocus.getGlobalVisibleRect(focusRect);
focused = new WeakReference<>(newFocus); focused = new WeakReference<>(newFocus);
} else { } else {
focusRect.setEmpty();
focused = null; focused = null;
} }
if (l != focusRect.left || r != focusRect.right updateRect();
|| t != focusRect.top || b != focusRect.bottom) {
invalidateSelf();
}
focused = new WeakReference<>(newFocus);
animator.sendEmptyMessageDelayed(0, 1000); animator.sendEmptyMessageDelayed(0, 1000);
} }
private void updateRect() { private void updateRect() {
if (focused == null) { View focusedView = focused == null ? null : this.focused.get();
return;
}
View focusedView = this.focused.get();
int l = focusRect.left; int l = focusRect.left;
int r = focusRect.right; int r = focusRect.right;
int t = focusRect.top; int t = focusRect.top;
int b = focusRect.bottom; int b = focusRect.bottom;
if (focusedView != null) { if (focusedView != null && isShown(focusedView)) {
focusedView.getGlobalVisibleRect(focusRect); focusedView.getGlobalVisibleRect(focusRect);
} else { } else {
focusRect.setEmpty(); focusRect.setEmpty();
@ -123,6 +105,10 @@ public final class FocusOverlayView extends Drawable implements
} }
} }
private boolean isShown(@NonNull final View view) {
return view.getWidth() != 0 && view.getHeight() != 0 && view.isShown();
}
@Override @Override
public void onDraw() { public void onDraw() {
updateRect(); updateRect();
@ -223,6 +209,7 @@ public final class FocusOverlayView extends Drawable implements
observer.addOnGlobalFocusChangeListener(overlay); observer.addOnGlobalFocusChangeListener(overlay);
observer.addOnGlobalLayoutListener(overlay); observer.addOnGlobalLayoutListener(overlay);
observer.addOnTouchModeChangeListener(overlay); observer.addOnTouchModeChangeListener(overlay);
observer.addOnDrawListener(overlay);
overlay.setCurrentFocus(decor.getFocusedChild()); overlay.setCurrentFocus(decor.getFocusedChild());

View File

@ -0,0 +1,7 @@
Improved
• Auto-play is available for all services (instead of only for YouTube)
Fixed
• Fixed related streams by supporting YouTube's new continuations
• Fixed age restricted YouTube videos
• [Android TV] Fixed lingering focus highlight overlay