diff --git a/app/src/main/java/org/schabi/newpipe/detail/StreamInfoWorker.java b/app/src/main/java/org/schabi/newpipe/detail/StreamInfoWorker.java new file mode 100644 index 000000000..e5bb29618 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/detail/StreamInfoWorker.java @@ -0,0 +1,227 @@ +package org.schabi.newpipe.detail; + +import android.app.Activity; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.View; + +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.ErrorActivity; +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.ParsingException; +import org.schabi.newpipe.extractor.ServiceList; +import org.schabi.newpipe.extractor.StreamExtractor; +import org.schabi.newpipe.extractor.StreamInfo; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor; +import org.schabi.newpipe.search_fragment.StreamInfoListAdapter; + +import java.io.IOException; + +/** + * Created by the-scrabi on 02.08.16. + */ + +public class StreamInfoWorker { + + private static final String TAG = StreamInfoWorker.class.toString(); + + public interface OnStreamInfoReceivedListener { + void onReceive(StreamInfo info); + void onError(int messageId); + void onBlockedByGemaError(); + void onContentErrorWithMessage(int messageId); + void onContentError(); + } + + private class StreamResultReturnedRunnable implements Runnable { + private final StreamInfo streamInfo; + public StreamResultReturnedRunnable(StreamInfo streamInfo) { + this.streamInfo = streamInfo; + } + @Override + public void run() { + /* + if(a != null) { + boolean showAgeRestrictedContent = PreferenceManager.getDefaultSharedPreferences(a) + .getBoolean(activity.getString(R.string.show_age_restricted_content), false); + if (streamInfo.age_limit == 0 || showAgeRestrictedContent) { + updateInfo(streamInfo); + } else { + onNotSpecifiedContentErrorWithMessage(R.string.video_is_age_restricted); + } + } + */ + } + } + + private class StreamExtractorRunnable implements Runnable { + private final Handler h = new Handler(); + private StreamExtractor streamExtractor; + private final int serviceId; + private final String videoUrl; + private Activity a; + + public StreamExtractorRunnable(Activity a, String videoUrl, int serviceId) { + this.serviceId = serviceId; + this.videoUrl = videoUrl; + this.a = a; + } + + @Override + public void run() { + StreamInfo streamInfo = null; + StreamingService service = null; + try { + service = ServiceList.getService(serviceId); + } catch (Exception e) { + e.printStackTrace(); + ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null, + ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, + "", videoUrl, R.string.could_not_get_stream)); + return; + } + try { + streamExtractor = service.getExtractorInstance(videoUrl, new Downloader()); + streamInfo = StreamInfo.getVideoInfo(streamExtractor, new Downloader()); + + final StreamInfo info = streamInfo; + h.post(new Runnable() { + @Override + public void run() { + onStreamInfoReceivedListener.onReceive(info); + } + }); + + // look for errors during extraction + // this if statement only covers extra information. + // if these are not available or caused an error, they are just not available + // but don't render the stream information unusalbe. + if(streamInfo != null && + !streamInfo.errors.isEmpty()) { + Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:"); + for (Throwable e : streamInfo.errors) { + e.printStackTrace(); + Log.e(TAG, "------"); + } + + View rootView = a != null ? a.findViewById(R.id.videoitem_detail) : null; + ErrorActivity.reportError(h, a, + streamInfo.errors, null, rootView, + ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, + service.getServiceInfo().name, videoUrl, 0 /* no message for the user */)); + } + + // These errors render the stream information unusable. + } catch (IOException e) { + h.post(new Runnable() { + @Override + public void run() { + onStreamInfoReceivedListener.onError(R.string.network_error); + } + }); + e.printStackTrace(); + } + // custom service related exceptions + catch (YoutubeStreamExtractor.DecryptException de) { + h.post(new Runnable() { + @Override + public void run() { + onStreamInfoReceivedListener.onError(R.string.youtube_signature_decryption_error); + } + }); + de.printStackTrace(); + } catch (YoutubeStreamExtractor.GemaException ge) { + h.post(new Runnable() { + @Override + public void run() { + onStreamInfoReceivedListener.onBlockedByGemaError(); + } + }); + } catch(YoutubeStreamExtractor.LiveStreamException e) { + h.post(new Runnable() { + @Override + public void run() { + onStreamInfoReceivedListener + .onContentErrorWithMessage(R.string.live_streams_not_supported); + } + }); + } + // ---------------------------------------- + catch(StreamExtractor.ContentNotAvailableException e) { + h.post(new Runnable() { + @Override + public void run() { + onStreamInfoReceivedListener + .onContentError(); + } + }); + e.printStackTrace(); + } catch(StreamInfo.StreamExctractException e) { + if(!streamInfo.errors.isEmpty()) { + // !!! if this case ever kicks in someone gets kicked out !!! + ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null, + ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, + service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); + } else { + ErrorActivity.reportError(h, a, streamInfo.errors, VideoItemDetailFragment.class, null, + ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, + service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); + } + h.post(new Runnable() { + @Override + public void run() { + a.finish(); + } + }); + e.printStackTrace(); + } catch (ParsingException e) { + ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null, + ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, + service.getServiceInfo().name, videoUrl, R.string.parsing_error)); + h.post(new Runnable() { + @Override + public void run() { + a.finish(); + } + }); + e.printStackTrace(); + } catch(Exception e) { + ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null, + ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, + service.getServiceInfo().name, videoUrl, R.string.general_error)); + h.post(new Runnable() { + @Override + public void run() { + a.finish(); + } + }); + e.printStackTrace(); + } + } + } + + private static StreamInfoWorker streamInfoWorker = null; + private StreamExtractorRunnable runnable = null; + private OnStreamInfoReceivedListener onStreamInfoReceivedListener = null; + + private StreamInfoWorker() { + + } + + public static StreamInfoWorker getInstance() { + return streamInfoWorker == null ? (streamInfoWorker = new StreamInfoWorker()) : streamInfoWorker; + } + + public void search(int serviceId, String url, Activity a) { + runnable = new StreamExtractorRunnable(a, url, serviceId); + Thread thread = new Thread(runnable); + thread.start(); + } + + public void setOnStreamInfoReceivedListener( + OnStreamInfoReceivedListener onStreamInfoReceivedListener) { + this.onStreamInfoReceivedListener = onStreamInfoReceivedListener; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/detail/VideoItemDetailFragment.java b/app/src/main/java/org/schabi/newpipe/detail/VideoItemDetailFragment.java index 60e4610d3..65a5437cf 100644 --- a/app/src/main/java/org/schabi/newpipe/detail/VideoItemDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/detail/VideoItemDetailFragment.java @@ -130,144 +130,6 @@ public class VideoItemDetailFragment extends Fragment { private OnInvokeCreateOptionsMenuListener onInvokeCreateOptionsMenuListener; - private class VideoExtractorRunnable implements Runnable { - private final Handler h = new Handler(); - private StreamExtractor streamExtractor; - private final StreamingService service; - private final String videoUrl; - - public VideoExtractorRunnable(String videoUrl, StreamingService service) { - this.service = service; - this.videoUrl = videoUrl; - } - - @Override - public void run() { - StreamInfo streamInfo = null; - try { - streamExtractor = service.getExtractorInstance(videoUrl, new Downloader()); - streamInfo = StreamInfo.getVideoInfo(streamExtractor, new Downloader()); - - h.post(new VideoResultReturnedRunnable(streamInfo)); - - // look for errors during extraction - // this if statement only covers extra information. - // if these are not available or caused an error, they are just not available - // but don't render the stream information unusalbe. - if(streamInfo != null && - !streamInfo.errors.isEmpty()) { - Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:"); - for (Throwable e : streamInfo.errors) { - e.printStackTrace(); - Log.e(TAG, "------"); - } - - Activity a = getActivity(); - View rootView = a != null ? a.findViewById(R.id.videoitem_detail) : null; - ErrorActivity.reportError(h, getActivity(), - streamInfo.errors, null, rootView, - ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, - service.getServiceInfo().name, videoUrl, 0 /* no message for the user */)); - } - - // These errors render the stream information unusable. - } catch (IOException e) { - postNewErrorToast(h, R.string.network_error); - e.printStackTrace(); - } - // custom service related exceptions - catch (YoutubeStreamExtractor.DecryptException de) { - postNewErrorToast(h, R.string.youtube_signature_decryption_error); - de.printStackTrace(); - } catch (YoutubeStreamExtractor.GemaException ge) { - h.post(new Runnable() { - @Override - public void run() { - onErrorBlockedByGema(); - } - }); - } catch(YoutubeStreamExtractor.LiveStreamException e) { - h.post(new Runnable() { - @Override - public void run() { - onNotSpecifiedContentErrorWithMessage(R.string.live_streams_not_supported); - } - }); - } - // ---------------------------------------- - catch(StreamExtractor.ContentNotAvailableException e) { - h.post(new Runnable() { - @Override - public void run() { - onNotSpecifiedContentError(); - } - }); - e.printStackTrace(); - } catch(StreamInfo.StreamExctractException e) { - if(!streamInfo.errors.isEmpty()) { - // !!! if this case ever kicks in someone gets kicked out !!! - ErrorActivity.reportError(h, getActivity(), e, VideoItemDetailFragment.class, null, - ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, - service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); - } else { - ErrorActivity.reportError(h, getActivity(), streamInfo.errors, VideoItemDetailFragment.class, null, - ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, - service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); - } - h.post(new Runnable() { - @Override - public void run() { - getActivity().finish(); - } - }); - e.printStackTrace(); - } catch (ParsingException e) { - ErrorActivity.reportError(h, getActivity(), e, VideoItemDetailFragment.class, null, - ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, - service.getServiceInfo().name, videoUrl, R.string.parsing_error)); - h.post(new Runnable() { - @Override - public void run() { - getActivity().finish(); - } - }); - e.printStackTrace(); - } catch(Exception e) { - ErrorActivity.reportError(h, getActivity(), e, VideoItemDetailFragment.class, null, - ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, - service.getServiceInfo().name, videoUrl, R.string.general_error)); - h.post(new Runnable() { - @Override - public void run() { - getActivity().finish(); - } - }); - e.printStackTrace(); - } - } - } - - private class VideoResultReturnedRunnable implements Runnable { - private final StreamInfo streamInfo; - public VideoResultReturnedRunnable(StreamInfo streamInfo) { - this.streamInfo = streamInfo; - } - @Override - public void run() { - Activity a = getActivity(); - if(a != null) { - boolean showAgeRestrictedContent = PreferenceManager.getDefaultSharedPreferences(a) - .getBoolean(activity.getString(R.string.show_age_restricted_content), false); - if (streamInfo.age_limit == 0 || showAgeRestrictedContent) { - updateInfo(streamInfo); - } else { - onNotSpecifiedContentErrorWithMessage(R.string.video_is_age_restricted); - } - } - } - } - - private void updateInfo(final StreamInfo info) { try { Context c = getContext(); @@ -768,6 +630,34 @@ public class VideoItemDetailFragment extends Fragment { showNextVideoItem = PreferenceManager.getDefaultSharedPreferences(getActivity()) .getBoolean(activity.getString(R.string.show_next_video_key), true); + + StreamInfoWorker siw = StreamInfoWorker.getInstance(); + siw.setOnStreamInfoReceivedListener(new StreamInfoWorker.OnStreamInfoReceivedListener() { + @Override + public void onReceive(StreamInfo info) { + updateInfo(info); + } + + @Override + public void onError(int messageId) { + postNewErrorToast(messageId); + } + + @Override + public void onBlockedByGemaError() { + onErrorBlockedByGema(); + } + + @Override + public void onContentErrorWithMessage(int messageId) { + onNotSpecifiedContentErrorWithMessage(messageId); + } + + @Override + public void onContentError() { + onNotSpecifiedContentError(); + } + }); } @Override @@ -800,17 +690,12 @@ public class VideoItemDetailFragment extends Fragment { // then we must not try to access objects of this fragment. // Otherwise the applications would crash. if(backgroundButton != null) { - try { - streamingServiceId = getArguments().getInt(STREAMING_SERVICE); - StreamingService streamingService = ServiceList.getService(streamingServiceId); - Thread videoExtractorThread = new Thread(new VideoExtractorRunnable( - getArguments().getString(VIDEO_URL), streamingService)); + streamingServiceId = getArguments().getInt(STREAMING_SERVICE); + String videoUrl = getArguments().getString(VIDEO_URL); + StreamInfoWorker siw = StreamInfoWorker.getInstance(); + siw.search(streamingServiceId, videoUrl, getActivity()); - autoPlayEnabled = getArguments().getBoolean(AUTO_PLAY); - videoExtractorThread.start(); - } catch (Exception e) { - e.printStackTrace(); - } + autoPlayEnabled = getArguments().getBoolean(AUTO_PLAY); if(Build.VERSION.SDK_INT >= 18) { ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView); @@ -932,23 +817,13 @@ public class VideoItemDetailFragment extends Fragment { this.onInvokeCreateOptionsMenuListener = listener; } - private void postNewErrorToast(Handler h, final int stringResource) { - h.post(new Runnable() { - @Override - public void run() { - Toast.makeText(VideoItemDetailFragment.this.getActivity(), - stringResource, Toast.LENGTH_LONG).show(); - } - }); + private void postNewErrorToast(final int stringResource) { + Toast.makeText(VideoItemDetailFragment.this.getActivity(), + stringResource, Toast.LENGTH_LONG).show(); } - private void postNewErrorToast(Handler h, final String message) { - h.post(new Runnable() { - @Override - public void run() { - Toast.makeText(VideoItemDetailFragment.this.getActivity(), - message, Toast.LENGTH_LONG).show(); - } - }); + private void postNewErrorToast(final String message) { + Toast.makeText(VideoItemDetailFragment.this.getActivity(), + message, Toast.LENGTH_LONG).show(); } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_searchinfoitem.xml b/app/src/main/res/layout/fragment_searchinfoitem.xml index b948e0205..5aef046c0 100644 --- a/app/src/main/res/layout/fragment_searchinfoitem.xml +++ b/app/src/main/res/layout/fragment_searchinfoitem.xml @@ -10,4 +10,5 @@ android:layout_marginRight="16dp" app:layoutManager="LinearLayoutManager" tools:context=".search_fragment.SearchInfoItemFragment" - tools:listitem="@layout/video_item" /> + tools:listitem="@layout/video_item" + android:scrollbars="vertical"/> diff --git a/app/src/main/res/layout/main_bg.xml b/app/src/main/res/layout/main_bg.xml index b1f3e0d4a..00ab5916a 100644 --- a/app/src/main/res/layout/main_bg.xml +++ b/app/src/main/res/layout/main_bg.xml @@ -4,8 +4,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/mainBG" - tools:context=".detail.VideoItemDetailActivity" - tools:showIn="@layout/activity_videoitem_list"> + tools:context=".detail.VideoItemDetailActivity">