() {
- @Override
- public void accept(@NonNull Throwable throwable) throws Exception {
- Log.e(TAG, "Subscription Update Fatal Error: ", throwable);
- Toast.makeText(getContext(), R.string.subscription_update_failed, Toast.LENGTH_SHORT).show();
- }
- };
-
- return subscriptionService.updateChannelInfo(serviceId, channelUrl, info)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(onComplete, onError);
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Utils
- //////////////////////////////////////////////////////////////////////////*/
-
- private String buildSubscriberString(long count) {
- String out = NumberFormat.getNumberInstance().format(count);
- out += " " + getString(count > 1 ? R.string.subscriber_plural : R.string.subscriber);
- return out;
- }
-
- private void loadPage(int page) {
- if (DEBUG) Log.d(TAG, "loadPage() called with: page = [" + page + "]");
- if (currentChannelWorker != null && currentChannelWorker.isRunning()) currentChannelWorker.cancel();
- isLoading.set(true);
- pageNumber = page;
- infoListAdapter.showFooter(false);
-
- animateView(loadingProgressBar, true, 200);
- animateView(errorPanel, false, 200);
-
- imageLoader.cancelDisplayTask(headerChannelBanner);
- imageLoader.cancelDisplayTask(headerAvatarView);
-
- headerSubscribeButton.setVisibility(View.GONE);
- headerSubscribersTextView.setVisibility(View.GONE);
-
- headerTitleView.setText(channelName != null ? channelName : "");
- headerChannelBanner.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.channel_banner));
- headerAvatarView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
- if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(channelName != null ? channelName : "");
-
- currentChannelWorker = new ChannelExtractorWorker(activity, serviceId, channelUrl, page, false, this);
- currentChannelWorker.start();
- }
-
- private void loadMoreVideos() {
- if (DEBUG) Log.d(TAG, "loadMoreVideos() called");
- if (currentChannelWorker != null && currentChannelWorker.isRunning()) currentChannelWorker.cancel();
- isLoading.set(true);
- currentChannelWorker = new ChannelExtractorWorker(activity, serviceId, channelUrl, pageNumber, true, this);
- currentChannelWorker.start();
- }
-
- private void setChannel(int serviceId, String channelUrl, String name) {
- this.serviceId = serviceId;
- this.channelUrl = channelUrl;
- this.channelName = name;
- }
-
- private void handleChannelInfo(ChannelInfo info, boolean onlyVideos, boolean addVideos) {
- currentChannelInfo = info;
-
- animateView(errorPanel, false, 300);
- animateView(channelVideosList, true, 200);
- animateView(loadingProgressBar, false, 200);
-
- if (!onlyVideos) {
- feedUrl = info.feed_url;
- if (activity.getSupportActionBar() != null) activity.getSupportActionBar().invalidateOptionsMenu();
-
- headerRootLayout.setVisibility(View.VISIBLE);
- //animateView(loadingProgressBar, false, 200, null);
-
- if (!TextUtils.isEmpty(info.channel_name)) {
- if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(info.channel_name);
- headerTitleView.setText(info.channel_name);
- channelName = info.channel_name;
- } else channelName = "";
-
- if (!TextUtils.isEmpty(info.banner_url)) {
- imageLoader.displayImage(info.banner_url, headerChannelBanner, displayImageOptions, new ImageErrorLoadingListener(activity, getView(), info.service_id));
- }
-
- if (!TextUtils.isEmpty(info.avatar_url)) {
- headerAvatarView.setVisibility(View.VISIBLE);
- imageLoader.displayImage(info.avatar_url, headerAvatarView, displayImageOptions, new ImageErrorLoadingListener(activity, getView(), info.service_id));
- }
-
- if (info.subscriberCount != -1) {
- headerSubscribersTextView.setText(buildSubscriberString(info.subscriberCount));
- headerSubscribersTextView.setVisibility(View.VISIBLE);
- } else headerSubscribersTextView.setVisibility(View.GONE);
-
- if (disposables != null) disposables.clear();
- if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
- disposables.add( updateSubscription(serviceId, channelUrl, info) );
- monitorSubscription(serviceId, channelUrl, info);
-
- infoListAdapter.showFooter(true);
- }
-
- hasNextPage = info.hasNextPage;
- if (!hasNextPage) infoListAdapter.showFooter(false);
-
- //if (!listRestored) {
- if (addVideos) infoListAdapter.addInfoItemList(info.related_streams);
- //}
- }
-
- @Override
- protected void setErrorMessage(String message, boolean showRetryButton) {
- super.setErrorMessage(message, showRetryButton);
-
- animateView(channelVideosList, false, 200);
- currentChannelInfo = null;
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // OnChannelInfoReceiveListener
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public void onReceive(ChannelInfo info, boolean onlyVideos) {
- if (DEBUG) Log.d(TAG, "onReceive() called with: info = [" + info + "]");
- if (info == null || isRemoving() || !isVisible()) return;
-
- handleChannelInfo(info, onlyVideos, true);
- isLoading.set(false);
- }
-
- @Override
- public void onError(int messageId) {
- if (DEBUG) Log.d(TAG, "onError() called with: messageId = [" + messageId + "]");
- setErrorMessage(getString(messageId), true);
- }
-
- @Override
- public void onUnrecoverableError(Exception exception) {
- if (DEBUG) Log.d(TAG, "onUnrecoverableError() called with: exception = [" + exception + "]");
- activity.finish();
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/ActionBarHandler.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/ActionBarHandler.java
index 77896b475..27bffca2d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/ActionBarHandler.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/ActionBarHandler.java
@@ -12,12 +12,12 @@ import android.widget.AdapterView;
import android.widget.Spinner;
import org.schabi.newpipe.R;
-import org.schabi.newpipe.extractor.stream_info.VideoStream;
-import org.schabi.newpipe.util.Utils;
+import org.schabi.newpipe.extractor.stream.VideoStream;
+import org.schabi.newpipe.util.ListHelper;
import java.util.List;
-/**
+/*
* Created by Christian Schabesberger on 18.08.15.
*
* Copyright (C) Christian Schabesberger 2015
@@ -68,7 +68,7 @@ class ActionBarHandler {
public void setupStreamList(final List videoStreams, Spinner toolbarSpinner) {
if (activity == null) return;
- selectedVideoStream = Utils.getDefaultResolution(activity, videoStreams);
+ selectedVideoStream = ListHelper.getDefaultResolutionIndex(activity, videoStreams);
boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(activity.getString(R.string.use_external_video_player_key), false);
toolbarSpinner.setAdapter(new SpinnerToolbarAdapter(activity, videoStreams, isExternalPlayerEnabled));
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/SpinnerToolbarAdapter.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/SpinnerToolbarAdapter.java
index f93512fc7..94fe2cf5b 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/SpinnerToolbarAdapter.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/SpinnerToolbarAdapter.java
@@ -11,7 +11,7 @@ import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
-import org.schabi.newpipe.extractor.stream_info.VideoStream;
+import org.schabi.newpipe.extractor.stream.VideoStream;
import java.util.List;
@@ -57,8 +57,8 @@ public class SpinnerToolbarAdapter extends BaseAdapter {
convertView = LayoutInflater.from(context).inflate(R.layout.resolutions_spinner_item, parent, false);
}
- ImageView woSoundIcon = (ImageView) convertView.findViewById(R.id.wo_sound_icon);
- TextView text = (TextView) convertView.findViewById(android.R.id.text1);
+ ImageView woSoundIcon = convertView.findViewById(R.id.wo_sound_icon);
+ TextView text = convertView.findViewById(android.R.id.text1);
VideoStream item = (VideoStream) getItem(position);
text.setText(MediaFormat.getNameById(item.format) + " " + item.resolution);
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
index 647637fec..f50f805c2 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
@@ -1,24 +1,25 @@
package org.schabi.newpipe.fragments.detail;
-import org.schabi.newpipe.extractor.stream_info.StreamInfo;
-
import java.io.Serializable;
-
-@SuppressWarnings("WeakerAccess")
-public class StackItem implements Serializable {
+class StackItem implements Serializable {
+ private int serviceId;
private String title, url;
- private StreamInfo info;
- public StackItem(String url, String title) {
- this.title = title;
+ StackItem(int serviceId, String url, String title) {
+ this.serviceId = serviceId;
this.url = url;
+ this.title = title;
}
public void setTitle(String title) {
this.title = title;
}
+ public int getServiceId() {
+ return serviceId;
+ }
+
public String getTitle() {
return title;
}
@@ -27,16 +28,8 @@ public class StackItem implements Serializable {
return url;
}
- public void setInfo(StreamInfo info) {
- this.info = info;
- }
-
- public StreamInfo getInfo() {
- return info;
- }
-
@Override
public String toString() {
- return getUrl() + " > " + getTitle();
+ return getServiceId() + ":" + getUrl() + " > " + getTitle();
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/StreamInfoCache.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/StreamInfoCache.java
deleted file mode 100644
index c7bf80245..000000000
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/StreamInfoCache.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.schabi.newpipe.fragments.detail;
-
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-import android.util.Log;
-
-import org.schabi.newpipe.MainActivity;
-import org.schabi.newpipe.extractor.stream_info.StreamInfo;
-
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-
-
-@SuppressWarnings("WeakerAccess")
-public class StreamInfoCache {
- private static String TAG = "StreamInfoCache@";
- private static final boolean DEBUG = MainActivity.DEBUG;
- private static final StreamInfoCache instance = new StreamInfoCache();
- private static final int MAX_ITEMS_ON_CACHE = 20;
-
- private final LinkedHashMap myCache = new LinkedHashMap<>();
-
- private StreamInfoCache() {
- TAG += "" + Integer.toHexString(hashCode());
- }
-
- public static StreamInfoCache getInstance() {
- if (DEBUG) Log.d(TAG, "getInstance() called");
- return instance;
- }
-
- public boolean hasKey(@NonNull String url) {
- if (DEBUG) Log.d(TAG, "hasKey() called with: url = [" + url + "]");
- return !TextUtils.isEmpty(url) && myCache.containsKey(url) && myCache.get(url) != null;
- }
-
- public StreamInfo getFromKey(@NonNull String url) {
- if (DEBUG) Log.d(TAG, "getFromKey() called with: url = [" + url + "]");
- return myCache.get(url);
- }
-
- public void putInfo(@NonNull StreamInfo info) {
- if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
- putInfo(info.webpage_url, info);
- }
-
- public void putInfo(@NonNull String url, @NonNull StreamInfo info) {
- if (DEBUG) Log.d(TAG, "putInfo() called with: url = [" + url + "], info = [" + info + "]");
- myCache.put(url, info);
- }
-
- public void removeInfo(@NonNull StreamInfo info) {
- if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
- myCache.remove(info.webpage_url);
- }
-
- public void removeInfo(@NonNull String url) {
- if (DEBUG) Log.d(TAG, "removeInfo() called with: url = [" + url + "]");
- myCache.remove(url);
- }
-
- @SuppressWarnings("unused")
- public void clearCache() {
- if (DEBUG) Log.d(TAG, "clearCache() called");
- myCache.clear();
- }
-
- public void removeOldEntries() {
- if (DEBUG) Log.d(TAG, "removeOldEntries() called , size = " + getSize());
- if (getSize() > MAX_ITEMS_ON_CACHE) {
- Iterator iterator = myCache.keySet().iterator();
- while (iterator.hasNext()) {
- iterator.next();
- iterator.remove();
- if (DEBUG) Log.d(TAG, "getSize() = " + getSize());
- if (getSize() <= MAX_ITEMS_ON_CACHE) break;
- }
- }
- }
-
- public int getSize() {
- return myCache.size();
- }
-
-}
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 2f5a3fc5c..5f954cad2 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
@@ -1,18 +1,14 @@
package org.schabi.newpipe.fragments.detail;
import android.app.Activity;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
import android.preference.PreferenceManager;
+import android.support.annotation.DrawableRes;
import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
@@ -24,6 +20,7 @@ import android.text.Spanned;
import android.text.TextUtils;
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.Gravity;
@@ -46,76 +43,82 @@ import com.nirhart.parallaxscroll.views.ParallaxScrollView;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
-import org.schabi.newpipe.ImageErrorLoadingListener;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe;
-import org.schabi.newpipe.extractor.stream_info.AudioStream;
-import org.schabi.newpipe.extractor.stream_info.StreamInfo;
-import org.schabi.newpipe.extractor.stream_info.VideoStream;
-import org.schabi.newpipe.fragments.BaseFragment;
+import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
+import org.schabi.newpipe.extractor.exceptions.ParsingException;
+import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
+import org.schabi.newpipe.extractor.stream.AudioStream;
+import org.schabi.newpipe.extractor.stream.StreamInfo;
+import org.schabi.newpipe.extractor.stream.StreamInfoItem;
+import org.schabi.newpipe.extractor.stream.VideoStream;
+import org.schabi.newpipe.fragments.BackPressable;
+import org.schabi.newpipe.fragments.BaseStateFragment;
+import org.schabi.newpipe.history.HistoryListener;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.player.MainVideoPlayer;
-import org.schabi.newpipe.player.PlayVideoActivity;
import org.schabi.newpipe.player.PopupVideoPlayer;
+import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
-import org.schabi.newpipe.util.Constants;
+import org.schabi.newpipe.util.ExtractorHelper;
+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.PermissionHelper;
-import org.schabi.newpipe.util.Utils;
-import org.schabi.newpipe.workers.StreamExtractorWorker;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Stack;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import icepick.State;
+import io.reactivex.Single;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.Consumer;
+import io.reactivex.functions.Function;
+import io.reactivex.schedulers.Schedulers;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
-public class VideoDetailFragment extends BaseFragment implements StreamExtractorWorker.OnStreamInfoReceivedListener, SharedPreferences.OnSharedPreferenceChangeListener, View.OnClickListener {
- private final String TAG = "VideoDetailFragment@" + Integer.toHexString(hashCode());
+public class VideoDetailFragment extends BaseStateFragment implements BackPressable, SharedPreferences.OnSharedPreferenceChangeListener, View.OnClickListener {
+ public static final String AUTO_PLAY = "auto_play";
// Amount of videos to show on start
private static final int INITIAL_RELATED_VIDEOS = 8;
-
private static final String KORE_PACKET = "org.xbmc.kore";
- private static final String STACK_KEY = "stack_key";
- private static final String INFO_KEY = "info_key";
- private static final String WAS_RELATED_EXPANDED_KEY = "was_related_expanded_key";
- public static final String AUTO_PLAY = "auto_play";
-
- private String thousand;
- private String million;
- private String billion;
-
- private ArrayList sortedStreamVideosList;
private ActionBarHandler actionBarHandler;
+ private ArrayList sortedStreamVideosList;
private InfoItemBuilder infoItemBuilder = null;
- private StreamInfo currentStreamInfo = null;
- private StreamExtractorWorker curExtractorWorker;
-
- private String videoTitle;
- private String videoUrl;
- private int serviceId = -1;
+ private int updateFlags = 0;
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4;
- private int updateFlags = 0;
private boolean autoPlayEnabled;
private boolean showRelatedStreams;
private boolean wasRelatedStreamsExpanded = false;
- private Handler uiHandler;
- private Handler backgroundHandler;
- private HandlerThread backgroundHandlerThread;
+ @State
+ protected int serviceId = -1;
+ @State
+ protected String name;
+ @State
+ protected String url;
+
+ private StreamInfo currentInfo;
+ private Disposable currentWorker;
+ private CompositeDisposable disposables = new CompositeDisposable();
/*//////////////////////////////////////////////////////////////////////////
// Views
@@ -156,24 +159,15 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
private LinearLayout relatedStreamRootLayout;
private LinearLayout relatedStreamsView;
private ImageButton relatedStreamExpandButton;
- private OnVideoPlayListener onVideoPlayedListener;
/*////////////////////////////////////////////////////////////////////////*/
- public static VideoDetailFragment getInstance(int serviceId, String url) {
- return getInstance(serviceId, url, "");
- }
-
- public static VideoDetailFragment getInstance(int serviceId, String videoUrl, String videoTitle) {
- VideoDetailFragment instance = getInstance();
- instance.selectVideo(serviceId, videoUrl, videoTitle);
+ public static VideoDetailFragment getInstance(int serviceId, String videoUrl, String name) {
+ VideoDetailFragment instance = new VideoDetailFragment();
+ instance.setInitialData(serviceId, videoUrl, name);
return instance;
}
- public static VideoDetailFragment getInstance() {
- return new VideoDetailFragment();
- }
-
/*//////////////////////////////////////////////////////////////////////////
// Fragment's Lifecycle
//////////////////////////////////////////////////////////////////////////*/
@@ -181,179 +175,69 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
-
- if (savedInstanceState != null) {
- videoTitle = savedInstanceState.getString(Constants.KEY_TITLE);
- videoUrl = savedInstanceState.getString(Constants.KEY_URL);
- serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, 0);
- wasRelatedStreamsExpanded = savedInstanceState.getBoolean(WAS_RELATED_EXPANDED_KEY, false);
- Serializable serializable = savedInstanceState.getSerializable(STACK_KEY);
- if (serializable instanceof Stack) {
- //noinspection unchecked
- Stack list = (Stack) serializable;
- stack.clear();
- stack.addAll(list);
- }
-
- Serializable serial = savedInstanceState.getSerializable(INFO_KEY);
- if (serial instanceof StreamInfo) currentStreamInfo = (StreamInfo) serial;
- }
+ setHasOptionsMenu(true);
showRelatedStreams = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(getString(R.string.show_next_video_key), true);
PreferenceManager.getDefaultSharedPreferences(activity).registerOnSharedPreferenceChangeListener(this);
-
- thousand = getString(R.string.short_thousand);
- million = getString(R.string.short_million);
- billion = getString(R.string.short_billion);
-
- if (uiHandler == null) {
- uiHandler = new Handler(Looper.getMainLooper(), new UICallback());
- }
- if (backgroundHandler == null) {
- HandlerThread handlerThread = new HandlerThread("VideoDetailFragment-BG");
- handlerThread.start();
- backgroundHandlerThread = handlerThread;
- backgroundHandler = new Handler(handlerThread.getLooper(), new BackgroundCallback(uiHandler, getContext()));
- }
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
return inflater.inflate(R.layout.fragment_video_detail, container, false);
}
@Override
- public void onViewCreated(View rootView, Bundle savedInstanceState) {
- super.onViewCreated(rootView, savedInstanceState);
- if (currentStreamInfo == null) selectAndLoadVideo(serviceId, videoUrl, videoTitle);
- else prepareAndLoad(currentStreamInfo, false);
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- onVideoPlayedListener = (OnVideoPlayListener) context;
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- onVideoPlayedListener = null;
+ public void onPause() {
+ super.onPause();
+ if (currentWorker != null) currentWorker.dispose();
}
@Override
public void onResume() {
super.onResume();
- // Currently only used for enable/disable related videos
- // but can be extended for other live settings changes
if (updateFlags != 0) {
- if (!isLoading.get() && currentStreamInfo != null) {
- if ((updateFlags & RELATED_STREAMS_UPDATE_FLAG) != 0) initRelatedVideos(currentStreamInfo);
- if ((updateFlags & RESOLUTIONS_MENU_UPDATE_FLAG) != 0) setupActionBarHandler(currentStreamInfo);
+ if (!isLoading.get() && currentInfo != null) {
+ if ((updateFlags & RELATED_STREAMS_UPDATE_FLAG) != 0) initRelatedVideos(currentInfo);
+ if ((updateFlags & RESOLUTIONS_MENU_UPDATE_FLAG) != 0) setupActionBarHandler(currentInfo);
}
if ((updateFlags & TOOLBAR_ITEMS_UPDATE_FLAG) != 0 && actionBarHandler != null) actionBarHandler.updateItemsVisibility();
updateFlags = 0;
}
- // Check if it was loading when the activity was stopped/paused,
- // because when this happen, the curExtractorWorker is cancelled
- if (wasLoading.getAndSet(false)) selectAndLoadVideo(serviceId, videoUrl, videoTitle);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- wasLoading.set(curExtractorWorker != null && curExtractorWorker.isRunning());
- if (curExtractorWorker != null && curExtractorWorker.isRunning()) curExtractorWorker.cancel();
- StreamInfoCache.getInstance().removeOldEntries();
+ // Check if it was loading when the fragment was stopped/paused,
+ if (wasLoading.getAndSet(false)) {
+ selectAndLoadVideo(serviceId, url, name);
+ }
}
@Override
public void onDestroy() {
super.onDestroy();
- if (backgroundHandlerThread != null) {
- backgroundHandlerThread.quit();
- }
- backgroundHandlerThread = null;
- backgroundHandler = null;
PreferenceManager.getDefaultSharedPreferences(activity).unregisterOnSharedPreferenceChangeListener(this);
+
+ if (currentWorker != null) currentWorker.dispose();
+ if (disposables != null) disposables.clear();
+ currentWorker = null;
+ disposables = null;
}
@Override
public void onDestroyView() {
if (DEBUG) Log.d(TAG, "onDestroyView() called");
- thumbnailImageView.setImageBitmap(null);
- relatedStreamsView.removeAllViews();
spinnerToolbar.setOnItemSelectedListener(null);
-
- spinnerToolbar = null;
-
- parallaxScrollRootView = null;
- contentRootLayoutHiding = null;
-
- thumbnailBackgroundButton = null;
- thumbnailImageView = null;
- thumbnailPlayButton = null;
-
- videoTitleRoot = null;
- videoTitleTextView = null;
- videoTitleToggleArrow = null;
- videoCountView = null;
-
- detailControlsBackground = null;
- detailControlsPopup = null;
-
- videoDescriptionRootLayout = null;
- videoUploadDateView = null;
- videoDescriptionView = null;
-
- uploaderRootLayout = null;
- uploaderTextView = null;
- uploaderThumb = null;
-
- thumbsUpTextView = null;
- thumbsUpImageView = null;
- thumbsDownTextView = null;
- thumbsDownImageView = null;
- thumbsDisabledTextView = null;
-
- nextStreamTitle = null;
- relatedStreamRootLayout = null;
- relatedStreamsView = null;
- relatedStreamExpandButton = null;
-
+ spinnerToolbar.setAdapter(null);
super.onDestroyView();
}
- @Override
- public void onSaveInstanceState(Bundle outState) {
- if (DEBUG) Log.d(TAG, "onSaveInstanceState() called with: outState = [" + outState + "]");
- outState.putString(Constants.KEY_URL, videoUrl);
- outState.putString(Constants.KEY_TITLE, videoTitle);
- outState.putInt(Constants.KEY_SERVICE_ID, serviceId);
- outState.putSerializable(STACK_KEY, stack);
-
- int nextCount = currentStreamInfo != null && currentStreamInfo.next_video != null ? 2 : 0;
- if (relatedStreamsView != null && relatedStreamsView.getChildCount() > INITIAL_RELATED_VIDEOS + nextCount) {
- outState.putSerializable(WAS_RELATED_EXPANDED_KEY, true);
- }
-
- if (!isLoading.get() && (curExtractorWorker == null || !curExtractorWorker.isRunning())) {
- outState.putSerializable(INFO_KEY, currentStreamInfo);
- }
- }
-
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case ReCaptchaActivity.RECAPTCHA_REQUEST:
if (resultCode == Activity.RESULT_OK) {
- NavigationHelper.openVideoDetailFragment(getFragmentManager(), serviceId, videoUrl, videoTitle);
+ NavigationHelper.openVideoDetailFragment(getFragmentManager(), serviceId, url, name);
} else Log.e(TAG, "ReCaptcha failed");
break;
default:
@@ -367,7 +251,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
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.preferred_video_format_key))
+ } else if (key.equals(getString(R.string.default_video_format_key))
|| key.equals(getString(R.string.default_resolution_key))
|| key.equals(getString(R.string.show_higher_resolutions_key))
|| key.equals(getString(R.string.use_external_video_player_key))) {
@@ -377,114 +261,85 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
}
}
+ /*//////////////////////////////////////////////////////////////////////////
+ // State Saving
+ //////////////////////////////////////////////////////////////////////////*/
+
+ private static final String INFO_KEY = "info_key";
+ private static final String STACK_KEY = "stack_key";
+ private static final String WAS_RELATED_EXPANDED_KEY = "was_related_expanded_key";
+
+ @Override
+ public void onSaveInstanceState(Bundle 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.next_video != null ? 2 : 0;
+ if (relatedStreamsView != null && relatedStreamsView.getChildCount() > INITIAL_RELATED_VIDEOS + nextCount) {
+ outState.putSerializable(WAS_RELATED_EXPANDED_KEY, true);
+ }
+
+ if (!isLoading.get() && currentInfo != null && isVisible()) {
+ outState.putSerializable(INFO_KEY, currentInfo);
+ }
+
+ outState.putSerializable(STACK_KEY, stack);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(@NonNull Bundle savedState) {
+ super.onRestoreInstanceState(savedState);
+
+ wasRelatedStreamsExpanded = savedState.getBoolean(WAS_RELATED_EXPANDED_KEY, false);
+ Serializable serializable = savedState.getSerializable(INFO_KEY);
+ if (serializable instanceof StreamInfo) {
+ //noinspection unchecked
+ currentInfo = (StreamInfo) serializable;
+ InfoCache.getInstance().putInfo(currentInfo);
+ }
+
+ serializable = savedState.getSerializable(STACK_KEY);
+ if (serializable instanceof Collection) {
+ //noinspection unchecked
+ stack.addAll((Collection extends StackItem>) serializable);
+ }
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// OnClick
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onClick(View v) {
- if (isLoading.get() || currentStreamInfo == null) return;
+ if (isLoading.get() || currentInfo == null) return;
switch (v.getId()) {
case R.id.detail_controls_background:
- openInBackground();
+ openBackgroundPlayer();
break;
case R.id.detail_controls_popup:
- openInPopup();
+ openPopupPlayer();
break;
case R.id.detail_uploader_root_layout:
- if (currentStreamInfo.channel_url == null || currentStreamInfo.channel_url.isEmpty()) {
+ if (currentInfo.uploader_url == null || currentInfo.uploader_url.isEmpty()) {
Log.w(TAG, "Can't open channel because we got no channel URL");
} else {
- NavigationHelper.openChannelFragment(getFragmentManager(), currentStreamInfo.service_id, currentStreamInfo.channel_url, currentStreamInfo.uploader);
+ NavigationHelper.openChannelFragment(getFragmentManager(), currentInfo.service_id, currentInfo.uploader_url, currentInfo.uploader_name);
}
break;
case R.id.detail_thumbnail_root_layout:
- playVideo(currentStreamInfo);
+ openVideoPlayer();
break;
case R.id.detail_title_root_layout:
toggleTitleAndDescription();
break;
case R.id.detail_related_streams_expand:
- toggleExpandRelatedVideos(currentStreamInfo);
+ toggleExpandRelatedVideos(currentInfo);
break;
}
}
- @Override
- protected void reloadContent() {
- if (DEBUG) Log.d(TAG, "reloadContent() called");
- if (currentStreamInfo != null) StreamInfoCache.getInstance().removeInfo(currentStreamInfo);
- currentStreamInfo = null;
- for (StackItem stackItem : stack) if (stackItem.getUrl().equals(videoUrl)) stackItem.setInfo(null);
- prepareAndLoad(null, true);
- }
-
- private void openInBackground() {
- if (isLoading.get()) return;
-
- boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
- .getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
- Intent intent;
- AudioStream audioStream = currentStreamInfo.audio_streams.get(Utils.getPreferredAudioFormat(activity, currentStreamInfo.audio_streams));
- onVideoPlayedListener.onBackgroundPlayed(currentStreamInfo, audioStream);
-
- if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
- activity.startService(NavigationHelper.getOpenBackgroundPlayerIntent(activity, currentStreamInfo, audioStream));
- Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
- } else {
- intent = new Intent();
- try {
- intent.setAction(Intent.ACTION_VIEW);
- intent.setDataAndType(Uri.parse(audioStream.url),
- MediaFormat.getMimeById(audioStream.format));
- intent.putExtra(Intent.EXTRA_TITLE, currentStreamInfo.title);
- intent.putExtra("title", currentStreamInfo.title);
- // HERE !!!
- activity.startActivity(intent);
- } catch (Exception e) {
- e.printStackTrace();
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setMessage(R.string.no_player_found)
- .setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent();
- intent.setAction(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(activity.getString(R.string.fdroid_vlc_url)));
- activity.startActivity(intent);
- }
- })
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Log.i(TAG, "You unlocked a secret unicorn.");
- }
- });
- builder.create().show();
- Log.e(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
- e.printStackTrace();
- }
- }
- }
-
- private void openInPopup() {
- if (isLoading.get()) return;
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
- Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
- TextView messageView = (TextView) toast.getView().findViewById(android.R.id.message);
- if (messageView != null) messageView.setGravity(Gravity.CENTER);
- toast.show();
- return;
- }
-
- onVideoPlayedListener.onVideoPlayed(getSelectedVideoStream(), currentStreamInfo);
- Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
- Intent mIntent = NavigationHelper.getOpenVideoPlayerIntent(activity, PopupVideoPlayer.class, currentStreamInfo, actionBarHandler.getSelectedVideoStream());
- activity.startService(mIntent);
- }
-
private void toggleTitleAndDescription() {
if (videoDescriptionRootLayout.getVisibility() == View.VISIBLE) {
videoTitleTextView.setMaxLines(1);
@@ -506,7 +361,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
if (relatedStreamsView.getChildCount() > initialCount) {
relatedStreamsView.removeViews(initialCount, relatedStreamsView.getChildCount() - (initialCount));
- relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, getResourceIdFromAttr(R.attr.expand)));
+ relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
return;
}
@@ -516,71 +371,70 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
//Log.d(TAG, "i = " + i);
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
}
- relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, getResourceIdFromAttr(R.attr.collapse)));
+ relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.collapse)));
}
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
+ @Override
protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
- spinnerToolbar = (Spinner) toolbar.findViewById(R.id.toolbar_spinner);
+ spinnerToolbar = activity.findViewById(R.id.toolbar).findViewById(R.id.toolbar_spinner);
- parallaxScrollRootView = (ParallaxScrollView) rootView.findViewById(R.id.detail_main_content);
+ parallaxScrollRootView = rootView.findViewById(R.id.detail_main_content);
thumbnailBackgroundButton = rootView.findViewById(R.id.detail_thumbnail_root_layout);
- thumbnailImageView = (ImageView) rootView.findViewById(R.id.detail_thumbnail_image_view);
- thumbnailPlayButton = (ImageView) rootView.findViewById(R.id.detail_thumbnail_play_button);
+ thumbnailImageView = rootView.findViewById(R.id.detail_thumbnail_image_view);
+ thumbnailPlayButton = rootView.findViewById(R.id.detail_thumbnail_play_button);
- contentRootLayoutHiding = (LinearLayout) rootView.findViewById(R.id.detail_content_root_hiding);
+ contentRootLayoutHiding = rootView.findViewById(R.id.detail_content_root_hiding);
videoTitleRoot = rootView.findViewById(R.id.detail_title_root_layout);
- videoTitleTextView = (TextView) rootView.findViewById(R.id.detail_video_title_view);
- videoTitleToggleArrow = (ImageView) rootView.findViewById(R.id.detail_toggle_description_view);
- videoCountView = (TextView) rootView.findViewById(R.id.detail_view_count_view);
+ videoTitleTextView = rootView.findViewById(R.id.detail_video_title_view);
+ videoTitleToggleArrow = rootView.findViewById(R.id.detail_toggle_description_view);
+ videoCountView = rootView.findViewById(R.id.detail_view_count_view);
- detailControlsBackground = (TextView) rootView.findViewById(R.id.detail_controls_background);
- detailControlsPopup = (TextView) rootView.findViewById(R.id.detail_controls_popup);
+ detailControlsBackground = rootView.findViewById(R.id.detail_controls_background);
+ detailControlsPopup = rootView.findViewById(R.id.detail_controls_popup);
- videoDescriptionRootLayout = (LinearLayout) rootView.findViewById(R.id.detail_description_root_layout);
- videoUploadDateView = (TextView) rootView.findViewById(R.id.detail_upload_date_view);
- videoDescriptionView = (TextView) rootView.findViewById(R.id.detail_description_view);
+ videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout);
+ videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view);
+ videoDescriptionView = rootView.findViewById(R.id.detail_description_view);
+ videoDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());
+ videoDescriptionView.setAutoLinkMask(Linkify.WEB_URLS);
- //thumbsRootLayout = (LinearLayout) rootView.findViewById(R.id.detail_thumbs_root_layout);
- thumbsUpTextView = (TextView) rootView.findViewById(R.id.detail_thumbs_up_count_view);
- thumbsUpImageView = (ImageView) rootView.findViewById(R.id.detail_thumbs_up_img_view);
- thumbsDownTextView = (TextView) rootView.findViewById(R.id.detail_thumbs_down_count_view);
- thumbsDownImageView = (ImageView) rootView.findViewById(R.id.detail_thumbs_down_img_view);
- thumbsDisabledTextView = (TextView) rootView.findViewById(R.id.detail_thumbs_disabled_view);
+ //thumbsRootLayout = rootView.findViewById(R.id.detail_thumbs_root_layout);
+ thumbsUpTextView = rootView.findViewById(R.id.detail_thumbs_up_count_view);
+ thumbsUpImageView = rootView.findViewById(R.id.detail_thumbs_up_img_view);
+ thumbsDownTextView = rootView.findViewById(R.id.detail_thumbs_down_count_view);
+ thumbsDownImageView = rootView.findViewById(R.id.detail_thumbs_down_img_view);
+ thumbsDisabledTextView = rootView.findViewById(R.id.detail_thumbs_disabled_view);
uploaderRootLayout = rootView.findViewById(R.id.detail_uploader_root_layout);
- uploaderTextView = (TextView) rootView.findViewById(R.id.detail_uploader_text_view);
- uploaderThumb = (ImageView) rootView.findViewById(R.id.detail_uploader_thumbnail_view);
+ uploaderTextView = rootView.findViewById(R.id.detail_uploader_text_view);
+ uploaderThumb = rootView.findViewById(R.id.detail_uploader_thumbnail_view);
- relatedStreamRootLayout = (LinearLayout) rootView.findViewById(R.id.detail_related_streams_root_layout);
- nextStreamTitle = (TextView) rootView.findViewById(R.id.detail_next_stream_title);
- relatedStreamsView = (LinearLayout) rootView.findViewById(R.id.detail_related_streams_view);
+ relatedStreamRootLayout = rootView.findViewById(R.id.detail_related_streams_root_layout);
+ nextStreamTitle = rootView.findViewById(R.id.detail_next_stream_title);
+ relatedStreamsView = rootView.findViewById(R.id.detail_related_streams_view);
- relatedStreamExpandButton = ((ImageButton) rootView.findViewById(R.id.detail_related_streams_expand));
+ relatedStreamExpandButton = rootView.findViewById(R.id.detail_related_streams_expand);
actionBarHandler = new ActionBarHandler(activity);
- videoDescriptionView.setAutoLinkMask(Linkify.WEB_URLS);
- videoDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());
-
infoItemBuilder = new InfoItemBuilder(activity);
-
setHeightThumbnail();
}
+ @Override
protected void initListeners() {
super.initListeners();
- infoItemBuilder.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
+ infoItemBuilder.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
- public void selected(int serviceId, String url, String title) {
- //NavigationHelper.openVideoDetail(activity, url, serviceId);
- selectAndLoadVideo(serviceId, url, title);
+ public void selected(StreamInfoItem selectedItem) {
+ selectAndLoadVideo(selectedItem.service_id, selectedItem.url, selectedItem.name);
}
});
@@ -593,18 +447,18 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
}
private void initThumbnailViews(StreamInfo info) {
+ thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
if (info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
- imageLoader.displayImage(info.thumbnail_url, thumbnailImageView, displayImageOptions, new SimpleImageLoadingListener() {
+ imageLoader.displayImage(info.thumbnail_url, thumbnailImageView, DISPLAY_THUMBNAIL_OPTIONS, new SimpleImageLoadingListener() {
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
- ErrorActivity.reportError(activity, failReason.getCause(), null, activity.findViewById(android.R.id.content), ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE, NewPipe.getNameOfService(currentStreamInfo.service_id), imageUri, R.string.could_not_load_thumbnails));
+ ErrorActivity.reportError(activity, failReason.getCause(), null, activity.findViewById(android.R.id.content), ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE, NewPipe.getNameOfService(currentInfo.service_id), imageUri, R.string.could_not_load_thumbnails));
}
});
- } else thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
+ }
- if (info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) {
- imageLoader.displayImage(info.uploader_thumbnail_url, uploaderThumb, displayImageOptions,
- new ImageErrorLoadingListener(activity, activity.findViewById(android.R.id.content), info.service_id));
+ if (info.uploader_avatar_url != null && !info.uploader_avatar_url.isEmpty()) {
+ imageLoader.displayImage(info.uploader_avatar_url, uploaderThumb, DISPLAY_AVATAR_OPTIONS);
}
}
@@ -632,7 +486,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
relatedStreamRootLayout.setVisibility(View.VISIBLE);
relatedStreamExpandButton.setVisibility(View.VISIBLE);
- relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, getResourceIdFromAttr(R.attr.expand)));
+ relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
} else {
if (info.next_video == null) relatedStreamRootLayout.setVisibility(View.GONE);
relatedStreamExpandButton.setVisibility(View.GONE);
@@ -655,20 +509,19 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- return actionBarHandler.onItemSelected(item) || super.onOptionsItemSelected(item);
+ return (!isLoading.get() && actionBarHandler.onItemSelected(item)) || super.onOptionsItemSelected(item);
}
private void setupActionBarHandler(final StreamInfo info) {
- sortedStreamVideosList = Utils.getSortedStreamVideosList(activity, info.video_streams, info.video_only_streams, false);
+ if (DEBUG) Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
+ sortedStreamVideosList = new ArrayList<>(ListHelper.getSortedStreamVideosList(activity, info.video_streams, info.video_only_streams, false));
actionBarHandler.setupStreamList(sortedStreamVideosList, spinnerToolbar);
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
- if (isLoading.get()) return;
-
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
- intent.putExtra(Intent.EXTRA_TEXT, info.webpage_url);
+ intent.putExtra(Intent.EXTRA_TEXT, info.url);
intent.setType("text/plain");
startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
}
@@ -677,11 +530,9 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
actionBarHandler.setOnOpenInBrowserListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
- if (isLoading.get()) return;
-
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(info.webpage_url));
+ intent.setData(Uri.parse(info.url));
startActivity(Intent.createChooser(intent, activity.getString(R.string.choose_browser)));
}
});
@@ -689,12 +540,10 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
actionBarHandler.setOnPlayWithKodiListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
- if (isLoading.get()) return;
-
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setPackage(KORE_PACKET);
- intent.setData(Uri.parse(info.webpage_url.replace("https", "http")));
+ intent.setData(Uri.parse(info.url.replace("https", "http")));
activity.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
@@ -723,8 +572,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
actionBarHandler.setOnDownloadListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
-
- if (isLoading.get() || !PermissionHelper.checkStoragePermissions(activity)) {
+ if (!PermissionHelper.checkStoragePermissions(activity)) {
return;
}
@@ -747,58 +595,257 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
* Stack that contains the "navigation history".
* The peek is the current video.
*/
- private final Stack stack = new Stack<>();
+ protected LinkedList stack = new LinkedList<>();
public void clearHistory() {
stack.clear();
}
- public void pushToStack(String videoUrl, String videoTitle) {
- if (DEBUG) Log.d(TAG, "pushToStack() called with: videoUrl = [" + videoUrl + "], videoTitle = [" + videoTitle + "]");
- if (stack.size() > 0 && stack.peek().getUrl().equals(videoUrl)) return;
- stack.push(new StackItem(videoUrl, videoTitle));
+ public void pushToStack(int serviceId, String videoUrl, String name) {
+ if (DEBUG) {
+ Log.d(TAG, "pushToStack() called with: serviceId = [" + serviceId + "], videoUrl = [" + videoUrl + "], name = [" + name + "]");
+ }
+
+ if (stack.size() > 0 && stack.peek().getServiceId() == serviceId && stack.peek().getUrl().equals(videoUrl)) {
+ Log.d(TAG, "pushToStack() called with: serviceId == peek.serviceId = [" + serviceId + "], videoUrl == peek.getUrl = [" + videoUrl + "]");
+ return;
+ } else {
+ Log.d(TAG, "pushToStack() wasn't equal");
+ }
+
+ stack.push(new StackItem(serviceId, videoUrl, name));
}
- public void setTitleToUrl(String videoUrl, String videoTitle) {
- if (videoTitle != null && !videoTitle.isEmpty()) {
+ public void setTitleToUrl(int serviceId, String videoUrl, String name) {
+ if (name != null && !name.isEmpty()) {
for (StackItem stackItem : stack) {
- if (stackItem.getUrl().equals(videoUrl)) stackItem.setTitle(videoTitle);
+ if (stack.peek().getServiceId() == serviceId && stackItem.getUrl().equals(videoUrl)) stackItem.setTitle(name);
}
}
}
- public void setStreamInfoToUrl(String videoUrl, StreamInfo info) {
- if (info != null) {
- for (StackItem stackItem : stack) {
- if (stackItem.getUrl().equals(videoUrl)) stackItem.setInfo(info);
- }
- }
- }
-
- public boolean onActivityBackPressed() {
- if (DEBUG) Log.d(TAG, "onActivityBackPressed() called");
+ @Override
+ public boolean onBackPressed() {
+ if (DEBUG) Log.d(TAG, "onBackPressed() called");
// That means that we are on the start of the stack,
// return false to let the MainActivity handle the onBack
- if (stack.size() == 1) return false;
+ if (stack.size() <= 1) return false;
// Remove top
stack.pop();
- // Get url from the new top
+ // Get stack item from the new top
StackItem peek = stack.peek();
- if (peek.getInfo() != null) {
- final StreamInfo streamInfo = peek.getInfo();
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- selectAndHandleInfo(streamInfo);
- }
- });
- } else {
- selectAndLoadVideo(0, peek.getUrl(), !TextUtils.isEmpty(peek.getTitle()) ? peek.getTitle() : "");
- }
+ selectAndLoadVideo(peek.getServiceId(), peek.getUrl(), !TextUtils.isEmpty(peek.getTitle()) ? peek.getTitle() : "");
return true;
}
+ /*//////////////////////////////////////////////////////////////////////////
+ // Info loading and handling
+ //////////////////////////////////////////////////////////////////////////*/
+
+ @Override
+ protected void doInitialLoadLogic() {
+ if (currentInfo == null) prepareAndLoadInfo();
+ else prepareAndHandleInfo(currentInfo, false);
+ }
+
+ public void selectAndLoadVideo(int serviceId, String videoUrl, String name) {
+ setInitialData(serviceId, videoUrl, name);
+ prepareAndLoadInfo();
+ }
+
+ public void prepareAndHandleInfo(final StreamInfo info, boolean scrollToTop) {
+ if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
+
+ setInitialData(info.service_id, info.url, info.name);
+ pushToStack(serviceId, url, name);
+ showLoading();
+
+ Log.d(TAG, "prepareAndHandleInfo() called parallaxScrollRootView.getScrollY(): " + parallaxScrollRootView.getScrollY());
+ final boolean greaterThanThreshold = parallaxScrollRootView.getScrollY() > (int)
+ (getResources().getDisplayMetrics().heightPixels * .1f);
+
+ if (scrollToTop) parallaxScrollRootView.smoothScrollTo(0, 0);
+ animateView(contentRootLayoutHiding, false, greaterThanThreshold ? 250 : 0, 0, new Runnable() {
+ @Override
+ public void run() {
+ handleResult(info);
+ showContentWithAnimation(120, 0, .01f);
+ }
+ });
+ }
+
+ protected void prepareAndLoadInfo() {
+ parallaxScrollRootView.smoothScrollTo(0, 0);
+ pushToStack(serviceId, url, name);
+ startLoading(false);
+ }
+
+ @Override
+ public void startLoading(boolean forceLoad) {
+ super.startLoading(forceLoad);
+
+ currentInfo = null;
+ if (currentWorker != null) currentWorker.dispose();
+
+ currentWorker = ExtractorHelper.getStreamInfo(serviceId, url, forceLoad)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Consumer() {
+ @Override
+ public void accept(@NonNull StreamInfo result) throws Exception {
+ isLoading.set(false);
+ currentInfo = result;
+ showContentWithAnimation(120, 0, 0);
+ handleResult(result);
+ }
+ }, new Consumer() {
+ @Override
+ public void accept(@NonNull Throwable throwable) throws Exception {
+ isLoading.set(false);
+ onError(throwable);
+ }
+ });
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Play Utils
+ //////////////////////////////////////////////////////////////////////////*/
+
+ private void openBackgroundPlayer() {
+ AudioStream audioStream = currentInfo.audio_streams.get(ListHelper.getDefaultAudioFormat(activity, currentInfo.audio_streams));
+
+ if (activity instanceof HistoryListener) {
+ ((HistoryListener) activity).onAudioPlayed(currentInfo, audioStream);
+ }
+
+ boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
+ .getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
+
+ if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
+ openNormalBackgroundPlayer(audioStream);
+ } else {
+ openExternalBackgroundPlayer(audioStream);
+ }
+ }
+
+ private void openPopupPlayer() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
+ Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
+ TextView messageView = toast.getView().findViewById(android.R.id.message);
+ if (messageView != null) messageView.setGravity(Gravity.CENTER);
+ toast.show();
+ return;
+ }
+
+ if (activity instanceof HistoryListener) {
+ ((HistoryListener) activity).onVideoPlayed(currentInfo, getSelectedVideoStream());
+ }
+
+ Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
+ Intent mIntent = NavigationHelper.getOpenVideoPlayerIntent(activity, PopupVideoPlayer.class, currentInfo, actionBarHandler.getSelectedVideoStream());
+ activity.startService(mIntent);
+ }
+
+ private void openVideoPlayer() {
+ VideoStream selectedVideoStream = getSelectedVideoStream();
+
+ if (activity instanceof HistoryListener) {
+ ((HistoryListener) activity).onVideoPlayed(currentInfo, selectedVideoStream);
+ }
+
+ if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
+ openExternalVideoPlayer(selectedVideoStream);
+ } else {
+ openNormalPlayer(selectedVideoStream);
+ }
+ }
+
+
+ private void openNormalBackgroundPlayer(AudioStream audioStream) {
+ activity.startService(NavigationHelper.getOpenBackgroundPlayerIntent(activity, currentInfo, audioStream));
+ Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
+ }
+
+ private void openExternalBackgroundPlayer(AudioStream audioStream) {
+ Intent intent;
+ intent = new Intent();
+ try {
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse(audioStream.url), MediaFormat.getMimeById(audioStream.format));
+ intent.putExtra(Intent.EXTRA_TITLE, currentInfo.name);
+ intent.putExtra("title", currentInfo.name);
+ activity.startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setMessage(R.string.no_player_found)
+ .setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(activity.getString(R.string.fdroid_vlc_url)));
+ activity.startActivity(intent);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.i(TAG, "You unlocked a secret unicorn.");
+ }
+ });
+ builder.create().show();
+ Log.e(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
+ e.printStackTrace();
+ }
+ }
+
+ private void openNormalPlayer(VideoStream selectedVideoStream) {
+ Intent mIntent;
+ boolean useOldPlayer = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(getString(R.string.use_old_player_key), false)
+ || (Build.VERSION.SDK_INT < 16);
+ if (!useOldPlayer) {
+ // ExoPlayer
+ mIntent = NavigationHelper.getOpenVideoPlayerIntent(activity, MainVideoPlayer.class, currentInfo, actionBarHandler.getSelectedVideoStream());
+ } else {
+ // Internal Player
+ mIntent = new Intent(activity, PlayVideoActivity.class)
+ .putExtra(PlayVideoActivity.VIDEO_TITLE, currentInfo.name)
+ .putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
+ .putExtra(PlayVideoActivity.VIDEO_URL, currentInfo.url)
+ .putExtra(PlayVideoActivity.START_POSITION, currentInfo.start_position);
+ }
+ startActivity(mIntent);
+ }
+
+ private void openExternalVideoPlayer(VideoStream selectedVideoStream) {
+ // External Player
+ Intent intent = new Intent();
+ try {
+ intent.setAction(Intent.ACTION_VIEW)
+ .setDataAndType(Uri.parse(selectedVideoStream.url), MediaFormat.getMimeById(selectedVideoStream.format))
+ .putExtra(Intent.EXTRA_TITLE, currentInfo.name)
+ .putExtra("title", currentInfo.name);
+ this.startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setMessage(R.string.no_player_found)
+ .setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent()
+ .setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse(getString(R.string.fdroid_vlc_url)));
+ startActivity(intent);
+ }
+ })
+ .setNegativeButton(R.string.cancel, null);
+ builder.create().show();
+ }
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@@ -807,247 +854,38 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
this.autoPlayEnabled = autoplay;
}
- public void selectVideo(int serviceId, String videoUrl, String videoTitle) {
- this.videoUrl = videoUrl;
- this.videoTitle = videoTitle;
- this.serviceId = serviceId;
- }
-
- public void selectAndHandleInfo(StreamInfo info) {
- selectAndHandleInfo(info, true);
- }
-
- public void selectAndHandleInfo(StreamInfo info, boolean scrollToTop) {
- if (DEBUG) Log.d(TAG, "selectAndHandleInfo() called with: info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
- selectVideo(info.service_id, info.webpage_url, info.title);
- prepareAndLoad(info, scrollToTop);
- }
-
- public void selectAndLoadVideo(int serviceId, String videoUrl, String videoTitle) {
- selectAndLoadVideo(serviceId, videoUrl, videoTitle, true);
- }
-
- public void selectAndLoadVideo(int serviceId, String videoUrl, String videoTitle, boolean scrollToTop) {
- if (DEBUG) {
- Log.d(TAG, "selectAndLoadVideo() called with: serviceId = [" + serviceId + "], videoUrl = [" + videoUrl + "], videoTitle = [" + videoTitle + "], scrollToTop = [" + scrollToTop + "]");
- }
-
- selectVideo(serviceId, videoUrl, videoTitle);
- prepareAndLoad(null, scrollToTop);
- }
-
- /**
- * Prepare the UI for loading the info.
- * If the argument info is not null, it'll be passed in {@link #handleStreamInfo(StreamInfo, boolean)}.
- * If it is, check if the cache contains the info already.
- * If the cache doesn't have the info, load from the network.
- *
- * @param info info to prepare and load, can be null
- * @param scrollToTop whether or not scroll the scrollView to y = 0
- */
- public void prepareAndLoad(StreamInfo info, boolean scrollToTop) {
- if (DEBUG) Log.d(TAG, "prepareAndLoad() called with: info = [" + info + "]");
- isLoading.set(true);
-
- // Only try to get from the cache if the passed info IS null
- if (info == null && StreamInfoCache.getInstance().hasKey(videoUrl)) {
- info = StreamInfoCache.getInstance().getFromKey(videoUrl);
- }
-
- if (info != null) selectVideo(info.service_id, info.webpage_url, info.title);
- pushToStack(videoUrl, videoTitle);
-
- if (curExtractorWorker != null && curExtractorWorker.isRunning()) curExtractorWorker.cancel();
- animateView(spinnerToolbar, false, 200);
- animateView(errorPanel, false, 200);
-
- videoTitleTextView.setText(videoTitle != null ? videoTitle : "");
- videoTitleTextView.setMaxLines(1);
- animateView(videoTitleTextView, true, 0);
-
- videoDescriptionRootLayout.setVisibility(View.GONE);
- videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
- videoTitleToggleArrow.setVisibility(View.GONE);
- videoTitleRoot.setClickable(false);
-
- animateView(thumbnailPlayButton, false, 50);
- imageLoader.cancelDisplayTask(thumbnailImageView);
- imageLoader.cancelDisplayTask(uploaderThumb);
- thumbnailImageView.setImageBitmap(null);
- uploaderThumb.setImageBitmap(null);
-
- if (info != null) {
- final StreamInfo infoFinal = info;
- final boolean greaterThanThreshold = parallaxScrollRootView.getScrollY() >
- (int) (getResources().getDisplayMetrics().heightPixels * .1f);
-
- if (scrollToTop) {
- if (greaterThanThreshold) parallaxScrollRootView.smoothScrollTo(0, 0);
- else parallaxScrollRootView.scrollTo(0, 0);
- }
-
- animateView(contentRootLayoutHiding, false, greaterThanThreshold ? 250 : 0, 0, new Runnable() {
- @Override
- public void run() {
- handleStreamInfo(infoFinal, false);
- isLoading.set(false);
- showContentWithAnimation(greaterThanThreshold ? 120 : 200, 0, .02f);
- }
- });
- } else {
- if (scrollToTop) parallaxScrollRootView.smoothScrollTo(0, 0);
- curExtractorWorker = new StreamExtractorWorker(activity, serviceId, videoUrl, this);
- curExtractorWorker.start();
- animateView(loadingProgressBar, true, 200);
- animateView(contentRootLayoutHiding, false, 200);
- }
- }
-
- private void handleStreamInfo(@NonNull final StreamInfo info, boolean fromNetwork) {
- if (DEBUG) Log.d(TAG, "handleStreamInfo() called with: info = [" + info + "]");
- currentStreamInfo = info;
- selectVideo(info.service_id, info.webpage_url, info.title);
-
- loadingProgressBar.setVisibility(View.GONE);
- animateView(thumbnailPlayButton, true, 200);
-
- // Since newpipe is designed to work even if certain information is not available,
- // the UI has to react on missing information.
- if (fromNetwork) videoTitleTextView.setText(info.title);
-
- if (!TextUtils.isEmpty(info.uploader)) uploaderTextView.setText(info.uploader);
- uploaderTextView.setVisibility(!TextUtils.isEmpty(info.uploader) ? View.VISIBLE : View.GONE);
- uploaderThumb.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
-
- if (info.view_count >= 0) videoCountView.setText(Localization.localizeViewCount(info.view_count, activity));
- videoCountView.setVisibility(info.view_count >= 0 ? View.VISIBLE : View.GONE);
-
- if (info.dislike_count == -1 && info.like_count == -1) {
- thumbsDownImageView.setVisibility(View.VISIBLE);
- thumbsUpImageView.setVisibility(View.VISIBLE);
- thumbsUpTextView.setVisibility(View.GONE);
- thumbsDownTextView.setVisibility(View.GONE);
-
- thumbsDisabledTextView.setVisibility(View.VISIBLE);
- } else {
- thumbsDisabledTextView.setVisibility(View.GONE);
-
- if (info.dislike_count >= 0) thumbsDownTextView.setText(getShortCount((long) info.dislike_count));
- thumbsDownTextView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
- thumbsDownImageView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
-
- if (info.like_count >= 0) thumbsUpTextView.setText(getShortCount((long) info.like_count));
- thumbsUpTextView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
- thumbsUpImageView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
- }
-
-
- videoDescriptionView.setVisibility(View.GONE);
- videoDescriptionRootLayout.setVisibility(View.GONE);
- videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
- videoTitleToggleArrow.setVisibility(View.VISIBLE);
- videoTitleRoot.setClickable(true);
-
- animateView(spinnerToolbar, true, 500);
- setupActionBarHandler(info);
- initThumbnailViews(info);
- initRelatedVideos(info);
- if (wasRelatedStreamsExpanded) {
- toggleExpandRelatedVideos(currentStreamInfo);
- wasRelatedStreamsExpanded = false;
- }
- setTitleToUrl(info.webpage_url, info.title);
- setStreamInfoToUrl(info.webpage_url, info);
-
- prepareDescription(info.description);
- prepareUploadDate(info.upload_date);
-
- if (autoPlayEnabled) {
- playVideo(info);
- // Only auto play in the first open
- autoPlayEnabled = false;
- }
- }
-
- private void prepareUploadDate(final String uploadDate) {
- // Hide until date is prepared or forever if no date is supplied
- videoUploadDateView.setVisibility(View.GONE);
- if (!TextUtils.isEmpty(uploadDate)) {
- backgroundHandler.sendMessage(Message.obtain(backgroundHandler, BackgroundCallback.MESSAGE_UPLOADER_DATE, uploadDate));
- }
- }
-
- private void prepareDescription(final String descriptionHtml) {
- // Send the unparsed description to the handler as a message
- if (!TextUtils.isEmpty(descriptionHtml)) {
- backgroundHandler.sendMessage(Message.obtain(backgroundHandler, BackgroundCallback.MESSAGE_DESCRIPTION, descriptionHtml));
- }
- }
-
- /**
- * Get the currently selected video stream
- * @return the selected video stream
- */
private VideoStream getSelectedVideoStream() {
return sortedStreamVideosList.get(actionBarHandler.getSelectedVideoStream());
}
- public void playVideo(StreamInfo info) {
- // ----------- THE MAGIC MOMENT ---------------
- VideoStream selectedVideoStream = getSelectedVideoStream();
-
- if(onVideoPlayedListener != null) {
- onVideoPlayedListener.onVideoPlayed(selectedVideoStream, info);
+ private void prepareDescription(final String descriptionHtml) {
+ if (TextUtils.isEmpty(descriptionHtml)) {
+ return;
}
- if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
-
- // External Player
- Intent intent = new Intent();
- try {
- intent.setAction(Intent.ACTION_VIEW)
- .setDataAndType(Uri.parse(selectedVideoStream.url), MediaFormat.getMimeById(selectedVideoStream.format))
- .putExtra(Intent.EXTRA_TITLE, info.title)
- .putExtra("title", info.title);
- this.startActivity(intent);
- } catch (Exception e) {
- e.printStackTrace();
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setMessage(R.string.no_player_found)
- .setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent()
- .setAction(Intent.ACTION_VIEW)
- .setData(Uri.parse(getString(R.string.fdroid_vlc_url)));
- startActivity(intent);
- }
- })
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- }
- });
- builder.create().show();
- }
- } else {
- Intent mIntent;
- boolean useOldPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
- .getBoolean(getString(R.string.use_old_player_key), false)
- || (Build.VERSION.SDK_INT < 16);
- if (!useOldPlayer) {
- // ExoPlayer
- mIntent = NavigationHelper.getOpenVideoPlayerIntent(activity, MainVideoPlayer.class, info, actionBarHandler.getSelectedVideoStream());
- } else {
- // Internal Player
- mIntent = new Intent(activity, PlayVideoActivity.class)
- .putExtra(PlayVideoActivity.VIDEO_TITLE, info.title)
- .putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
- .putExtra(PlayVideoActivity.VIDEO_URL, info.webpage_url)
- .putExtra(PlayVideoActivity.START_POSITION, info.start_position);
- }
- startActivity(mIntent);
- }
+ disposables.add(Single.just(descriptionHtml)
+ .map(new Function() {
+ @Override
+ public Spanned apply(@io.reactivex.annotations.NonNull String description) throws Exception {
+ Spanned parsedDescription;
+ if (Build.VERSION.SDK_INT >= 24) {
+ parsedDescription = Html.fromHtml(description, 0);
+ } else {
+ //noinspection deprecation
+ parsedDescription = Html.fromHtml(description);
+ }
+ return parsedDescription;
+ }
+ })
+ .subscribeOn(Schedulers.computation())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Consumer() {
+ @Override
+ public void accept(@io.reactivex.annotations.NonNull Spanned spanned) throws Exception {
+ videoDescriptionView.setText(spanned);
+ videoDescriptionView.setVisibility(View.VISIBLE);
+ }
+ }));
}
private View getSeparatorView() {
@@ -1059,36 +897,24 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
separator.setLayoutParams(params);
TypedValue typedValue = new TypedValue();
- activity.getTheme().resolveAttribute(R.attr.separatorColor, typedValue, true);
+ activity.getTheme().resolveAttribute(R.attr.separator_color, typedValue, true);
separator.setBackgroundColor(typedValue.data);
return separator;
}
private void setHeightThumbnail() {
- boolean isPortrait = getResources().getDisplayMetrics().heightPixels > getResources().getDisplayMetrics().widthPixels;
- int height = isPortrait ? (int) (getResources().getDisplayMetrics().widthPixels / (16.0f / 9.0f))
- : (int) (getResources().getDisplayMetrics().heightPixels / 2f);
+ final DisplayMetrics metrics = getResources().getDisplayMetrics();
+ boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
+ int height = isPortrait ? (int) (metrics.widthPixels / (16.0f / 9.0f)) : (int) (metrics.heightPixels / 2f);
thumbnailImageView.setScaleType(isPortrait ? ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.FIT_CENTER);
thumbnailImageView.setLayoutParams(new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
thumbnailImageView.setMinimumHeight(height);
}
- public String getShortCount(Long viewCount) {
- if (viewCount >= 1000000000) {
- return Long.toString(viewCount / 1000000000) + billion;
- } else if (viewCount >= 1000000) {
- return Long.toString(viewCount / 1000000) + million;
- } else if (viewCount >= 1000) {
- return Long.toString(viewCount / 1000) + thousand;
- } else {
- return Long.toString(viewCount);
- }
- }
-
private void showContentWithAnimation(long duration, long delay, @FloatRange(from = 0.0f, to = 1.0f) float translationPercent) {
int translationY = (int) (getResources().getDisplayMetrics().heightPixels *
- (translationPercent > 0.0f ? translationPercent : .12f));
+ (translationPercent > 0.0f ? translationPercent : .06f));
contentRootLayoutHiding.animate().setListener(null).cancel();
contentRootLayoutHiding.setAlpha(0f);
@@ -1114,8 +940,15 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
}
}
+ protected void setInitialData(int serviceId, String url, String name) {
+ this.serviceId = serviceId;
+ this.url = url;
+ this.name = !TextUtils.isEmpty(name) ? name : "";
+ }
+
private void setErrorImage(final int imageResource) {
if (thumbnailImageView == null || activity == null) return;
+
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, imageResource));
animateView(thumbnailImageView, false, 0, 0, new Runnable() {
@Override
@@ -1126,51 +959,133 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
}
@Override
- protected void setErrorMessage(String message, boolean showRetryButton) {
- super.setErrorMessage(message, showRetryButton);
+ public void showError(String message, boolean showRetryButton) {
+ showError(message, showRetryButton, R.drawable.not_available_monkey);
+ }
- if (!TextUtils.isEmpty(videoUrl)) StreamInfoCache.getInstance().removeInfo(videoUrl);
- currentStreamInfo = null;
+ protected void showError(String message, boolean showRetryButton, @DrawableRes int imageError) {
+ super.showError(message, showRetryButton);
+ setErrorImage(imageError);
}
/*//////////////////////////////////////////////////////////////////////////
- // OnStreamInfoReceivedListener callbacks
+ // Contract
//////////////////////////////////////////////////////////////////////////*/
@Override
- public void onReceive(StreamInfo info) {
- if (DEBUG) Log.d(TAG, "onReceive() called with: info = [" + info + "]");
- if (info == null || isRemoving() || !isVisible()) return;
+ public void showLoading() {
+ super.showLoading();
- handleStreamInfo(info, true);
- showContentWithAnimation(300, 0, 0);
- animateView(loadingProgressBar, false, 200);
+ animateView(contentRootLayoutHiding, false, 200);
+ animateView(spinnerToolbar, false, 200);
+ animateView(thumbnailPlayButton, false, 50);
- StreamInfoCache.getInstance().putInfo(info);
- isLoading.set(false);
+ videoTitleTextView.setText(name != null ? name : "");
+ videoTitleTextView.setMaxLines(1);
+ animateView(videoTitleTextView, true, 0);
+ videoDescriptionRootLayout.setVisibility(View.GONE);
+ videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
+ videoTitleToggleArrow.setVisibility(View.GONE);
+ videoTitleRoot.setClickable(false);
+
+ imageLoader.cancelDisplayTask(thumbnailImageView);
+ imageLoader.cancelDisplayTask(uploaderThumb);
+ thumbnailImageView.setImageBitmap(null);
+ uploaderThumb.setImageBitmap(null);
}
@Override
- public void onError(int messageId) {
- if (DEBUG) Log.d(TAG, "onError() called with: messageId = [" + messageId + "]");
- //Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
- setErrorImage(R.drawable.not_available_monkey);
- setErrorMessage(getString(messageId), true);
+ public void handleResult(@NonNull StreamInfo info) {
+ super.handleResult(info);
+
+ setInitialData(info.service_id, info.url, info.name);
+ pushToStack(serviceId, url, name);
+
+ animateView(thumbnailPlayButton, true, 200);
+ videoTitleTextView.setText(name);
+
+ if (!TextUtils.isEmpty(info.uploader_name)) uploaderTextView.setText(info.uploader_name);
+ uploaderTextView.setVisibility(!TextUtils.isEmpty(info.uploader_name) ? View.VISIBLE : View.GONE);
+ uploaderThumb.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
+
+ if (info.view_count >= 0) videoCountView.setText(Localization.localizeViewCount(activity, info.view_count));
+ videoCountView.setVisibility(info.view_count >= 0 ? View.VISIBLE : View.GONE);
+
+ if (info.dislike_count == -1 && info.like_count == -1) {
+ thumbsDownImageView.setVisibility(View.VISIBLE);
+ thumbsUpImageView.setVisibility(View.VISIBLE);
+ thumbsUpTextView.setVisibility(View.GONE);
+ thumbsDownTextView.setVisibility(View.GONE);
+
+ thumbsDisabledTextView.setVisibility(View.VISIBLE);
+ } else {
+ if (info.dislike_count >= 0) thumbsDownTextView.setText(Localization.shortCount(activity, info.dislike_count));
+ thumbsDownTextView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
+ thumbsDownImageView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
+
+ if (info.like_count >= 0) thumbsUpTextView.setText(Localization.shortCount(activity, info.like_count));
+ thumbsUpTextView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
+ thumbsUpImageView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
+
+ thumbsDisabledTextView.setVisibility(View.GONE);
+ }
+
+ videoTitleRoot.setClickable(true);
+ videoTitleToggleArrow.setVisibility(View.VISIBLE);
+ videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
+ videoDescriptionView.setVisibility(View.GONE);
+ videoDescriptionRootLayout.setVisibility(View.GONE);
+ if (!TextUtils.isEmpty(info.upload_date)) {
+ videoUploadDateView.setText(Localization.localizeDate(activity, info.upload_date));
+ }
+ prepareDescription(info.description);
+
+ animateView(spinnerToolbar, true, 500);
+ setupActionBarHandler(info);
+ initThumbnailViews(info);
+ initRelatedVideos(info);
+ if (wasRelatedStreamsExpanded) {
+ toggleExpandRelatedVideos(currentInfo);
+ wasRelatedStreamsExpanded = false;
+ }
+ setTitleToUrl(info.service_id, info.url, info.name);
+
+ if (!info.errors.isEmpty()) {
+ showSnackBarError(info.errors, UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(info.service_id), info.url, 0);
+ }
+
+ if (autoPlayEnabled) {
+ openVideoPlayer();
+ // Only auto play in the first open
+ autoPlayEnabled = false;
+ }
}
- @Override
- public void onReCaptchaException() {
- Toast.makeText(activity, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
- // Starting ReCaptcha Challenge Activity
- startActivityForResult(new Intent(activity, ReCaptchaActivity.class), ReCaptchaActivity.RECAPTCHA_REQUEST);
+ /*//////////////////////////////////////////////////////////////////////////
+ // Stream Results
+ //////////////////////////////////////////////////////////////////////////*/
- setErrorMessage(getString(R.string.recaptcha_request_toast), false);
+ @Override
+ protected boolean onError(Throwable exception) {
+ if (super.onError(exception)) return true;
+
+ if (exception instanceof YoutubeStreamExtractor.GemaException) {
+ onBlockedByGemaError();
+ } else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
+ showError(getString(R.string.live_streams_not_supported), false);
+ } else if (exception instanceof ContentNotAvailableException) {
+ showError(getString(R.string.content_not_available), false);
+ } else {
+ int errorId = exception instanceof YoutubeStreamExtractor.DecryptException ? R.string.youtube_signature_decryption_error :
+ exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
+ onUnrecoverableError(exception, UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(serviceId), url, errorId);
+ }
+
+ return true;
}
- @Override
public void onBlockedByGemaError() {
-
thumbnailBackgroundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -1181,109 +1096,6 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
}
});
- setErrorImage(R.drawable.gruese_die_gema);
- setErrorMessage(getString(R.string.blocked_by_gema), false);
+ showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema);
}
-
- @Override
- public void onContentErrorWithMessage(int messageId) {
- setErrorImage(R.drawable.not_available_monkey);
- setErrorMessage(getString(messageId), false);
- }
-
- @Override
- public void onContentError() {
- setErrorImage(R.drawable.not_available_monkey);
- setErrorMessage(getString(R.string.content_not_available), false);
- }
-
- @Override
- public void onUnrecoverableError(Exception exception) {
- activity.finish();
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Background handling
- //////////////////////////////////////////////////////////////////////////*/
-
- private static class BackgroundCallback implements Handler.Callback {
- private static final int MESSAGE_DESCRIPTION = 1;
- public static final int MESSAGE_UPLOADER_DATE = 2;
- private final Handler uiHandler;
- private final Context context;
-
- BackgroundCallback(Handler uiHandler, Context context) {
- this.uiHandler = uiHandler;
- this.context = context;
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_DESCRIPTION:
- handleDescription((String) msg.obj);
- return true;
- case MESSAGE_UPLOADER_DATE:
- handleUploadDate((String) msg.obj);
- return true;
- }
- return false;
- }
-
- private void handleUploadDate(String uploadDate) {
- String localizedDate = Localization.localizeDate(uploadDate, context);
- uiHandler.sendMessage(Message.obtain(uiHandler, MESSAGE_UPLOADER_DATE, localizedDate));
- }
-
- private void handleDescription(String description) {
- Spanned parsedDescription;
- if (TextUtils.isEmpty(description)) {
- return;
- }
- if (Build.VERSION.SDK_INT >= 24) {
- parsedDescription = Html.fromHtml(description, 0);
- } else {
- //noinspection deprecation
- parsedDescription = Html.fromHtml(description);
- }
- uiHandler.sendMessage(Message.obtain(uiHandler, MESSAGE_DESCRIPTION, parsedDescription));
- }
- }
-
- private class UICallback implements Handler.Callback {
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case BackgroundCallback.MESSAGE_DESCRIPTION:
- if (videoDescriptionView != null) {
- videoDescriptionView.setText((Spanned) msg.obj);
- videoDescriptionView.setVisibility(View.VISIBLE);
- }
- return true;
- case BackgroundCallback.MESSAGE_UPLOADER_DATE:
- if (videoUploadDateView != null) {
- videoUploadDateView.setText((String) msg.obj);
- videoUploadDateView.setVisibility(View.VISIBLE);
- }
- return true;
- }
- return false;
- }
- }
-
- public interface OnVideoPlayListener {
- /**
- * Called when a video is played
- * @param videoStream the video stream that is played
- * @param streamInfo the stream info
- */
- void onVideoPlayed(VideoStream videoStream, StreamInfo streamInfo);
-
- /**
- * Called when the audio is played in the background
- * @param streamInfo the stream info
- * @param audioStream the audio stream that is played
- */
- void onBackgroundPlayed(StreamInfo streamInfo, AudioStream audioStream);
- }
-}
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..4501ab859
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java
@@ -0,0 +1,239 @@
+package org.schabi.newpipe.fragments.list;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+
+import org.schabi.newpipe.R;
+import org.schabi.newpipe.extractor.InfoItem;
+import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
+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.info_list.InfoItemBuilder;
+import org.schabi.newpipe.info_list.InfoListAdapter;
+import org.schabi.newpipe.util.NavigationHelper;
+import org.schabi.newpipe.util.StateSaver;
+
+import java.util.List;
+import java.util.Queue;
+
+import static org.schabi.newpipe.util.AnimationUtils.animateView;
+
+public abstract class BaseListFragment extends BaseStateFragment implements ListViewContract, StateSaver.WriteRead {
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Views
+ //////////////////////////////////////////////////////////////////////////*/
+
+ protected InfoListAdapter infoListAdapter;
+ protected RecyclerView itemsList;
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // LifeCycle
+ //////////////////////////////////////////////////////////////////////////*/
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ infoListAdapter = new InfoListAdapter(activity);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ StateSaver.onDestroy(savedState);
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // State Saving
+ //////////////////////////////////////////////////////////////////////////*/
+
+ protected StateSaver.SavedState savedState;
+
+ @Override
+ public String generateSuffix() {
+ // Naive solution, but it's good for now (the items don't change)
+ return "." + infoListAdapter.getItemsList().size() + ".list";
+ }
+
+ @Override
+ public void writeTo(Queue