mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
		| @@ -41,7 +41,7 @@ android { | |||||||
| } | } | ||||||
|  |  | ||||||
| ext { | ext { | ||||||
|     supportLibVersion = '27.1.0' |     supportLibVersion = '27.1.1' | ||||||
|     exoPlayerLibVersion = '2.7.3' |     exoPlayerLibVersion = '2.7.3' | ||||||
|     roomDbLibVersion = '1.0.0' |     roomDbLibVersion = '1.0.0' | ||||||
|     leakCanaryLibVersion = '1.5.4' |     leakCanaryLibVersion = '1.5.4' | ||||||
| @@ -56,7 +56,6 @@ dependencies { | |||||||
|  |  | ||||||
|     implementation 'com.github.TeamNewPipe:NewPipeExtractor:77a74b8' |     implementation 'com.github.TeamNewPipe:NewPipeExtractor:77a74b8' | ||||||
|  |  | ||||||
|  |  | ||||||
|     testImplementation 'junit:junit:4.12' |     testImplementation 'junit:junit:4.12' | ||||||
|     testImplementation 'org.mockito:mockito-core:1.10.19' |     testImplementation 'org.mockito:mockito-core:1.10.19' | ||||||
|  |  | ||||||
| @@ -66,7 +65,6 @@ dependencies { | |||||||
|     implementation "com.android.support:recyclerview-v7:$supportLibVersion" |     implementation "com.android.support:recyclerview-v7:$supportLibVersion" | ||||||
|     implementation "com.android.support:preference-v14:$supportLibVersion" |     implementation "com.android.support:preference-v14:$supportLibVersion" | ||||||
|  |  | ||||||
|     implementation 'com.google.code.gson:gson:2.8.2' |  | ||||||
|     implementation 'ch.acra:acra:4.9.2' |     implementation 'ch.acra:acra:4.9.2' | ||||||
|  |  | ||||||
|     implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' |     implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' | ||||||
|   | |||||||
| @@ -80,8 +80,8 @@ | |||||||
|             android:name=".history.HistoryActivity" |             android:name=".history.HistoryActivity" | ||||||
|             android:label="@string/title_activity_history"/> |             android:label="@string/title_activity_history"/> | ||||||
|  |  | ||||||
|         <service android:name=".subscription.services.SubscriptionsImportService"/> |         <service android:name=".local.subscription.services.SubscriptionsImportService"/> | ||||||
|         <service android:name=".subscription.services.SubscriptionsExportService"/> |         <service android:name=".local.subscription.services.SubscriptionsExportService"/> | ||||||
|  |  | ||||||
|         <activity |         <activity | ||||||
|             android:name=".PanicResponderActivity" |             android:name=".PanicResponderActivity" | ||||||
|   | |||||||
| @@ -151,7 +151,8 @@ public class MainActivity extends AppCompatActivity { | |||||||
|  |  | ||||||
|         settings.setOnClickListener(view -> NavigationHelper.openSettings(this)); |         settings.setOnClickListener(view -> NavigationHelper.openSettings(this)); | ||||||
|         downloads.setOnClickListener(view ->NavigationHelper.openDownloads(this)); |         downloads.setOnClickListener(view ->NavigationHelper.openDownloads(this)); | ||||||
|         history.setOnClickListener(view -> NavigationHelper.openHistory(this)); |         history.setOnClickListener(view -> | ||||||
|  |                 NavigationHelper.openStatisticFragment(getSupportFragmentManager())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void setupDrawerHeader() { |     private void setupDrawerHeader() { | ||||||
| @@ -327,16 +328,16 @@ public class MainActivity extends AppCompatActivity { | |||||||
|             case android.R.id.home: |             case android.R.id.home: | ||||||
|                 onHomeButtonPressed(); |                 onHomeButtonPressed(); | ||||||
|                 return true; |                 return true; | ||||||
|             case R.id.action_settings: |  | ||||||
|                 NavigationHelper.openSettings(this); |  | ||||||
|                 return true; |  | ||||||
|             case R.id.action_show_downloads: |             case R.id.action_show_downloads: | ||||||
|                 return NavigationHelper.openDownloads(this); |                 return NavigationHelper.openDownloads(this); | ||||||
|  |             case R.id.action_history: | ||||||
|  |                 NavigationHelper.openStatisticFragment(getSupportFragmentManager()); | ||||||
|  |                 return true; | ||||||
|             case R.id.action_about: |             case R.id.action_about: | ||||||
|                 NavigationHelper.openAbout(this); |                 NavigationHelper.openAbout(this); | ||||||
|                 return true; |                 return true; | ||||||
|             case R.id.action_history: |             case R.id.action_settings: | ||||||
|                 NavigationHelper.openHistory(this); |                 NavigationHelper.openSettings(this); | ||||||
|                 return true; |                 return true; | ||||||
|             default: |             default: | ||||||
|                 return super.onOptionsItemSelected(item); |                 return super.onOptionsItemSelected(item); | ||||||
|   | |||||||
| @@ -32,10 +32,10 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; | |||||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | import org.schabi.newpipe.player.helper.PlayerHelper; | ||||||
| import org.schabi.newpipe.playlist.ChannelPlayQueue; | import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.playlist.PlaylistPlayQueue; | import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; | ||||||
| import org.schabi.newpipe.playlist.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.ExtractorHelper; | import org.schabi.newpipe.util.ExtractorHelper; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
|   | |||||||
| @@ -32,7 +32,6 @@ public class AboutActivity extends AppCompatActivity { | |||||||
|             new SoftwareComponent("Giga Get", "2014", "Peter Cai", "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2), |             new SoftwareComponent("Giga Get", "2014", "Peter Cai", "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2), | ||||||
|             new SoftwareComponent("NewPipe Extractor", "2017", "Christian Schabesberger", "https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3), |             new SoftwareComponent("NewPipe Extractor", "2017", "Christian Schabesberger", "https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3), | ||||||
|             new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley", "https://github.com/jhy/jsoup", StandardLicenses.MIT), |             new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley", "https://github.com/jhy/jsoup", StandardLicenses.MIT), | ||||||
|             new SoftwareComponent("Google Gson", "2008", "Google Inc", "https://github.com/google/gson", StandardLicenses.APACHE2), |  | ||||||
|             new SoftwareComponent("Rhino", "2015", "Mozilla", "https://www.mozilla.org/rhino/", StandardLicenses.MPL2), |             new SoftwareComponent("Rhino", "2015", "Mozilla", "https://www.mozilla.org/rhino/", StandardLicenses.MPL2), | ||||||
|             new SoftwareComponent("ACRA", "2013", "Kevin Gaudin", "http://www.acra.ch", StandardLicenses.APACHE2), |             new SoftwareComponent("ACRA", "2013", "Kevin Gaudin", "http://www.acra.ch", StandardLicenses.APACHE2), | ||||||
|             new SoftwareComponent("Universal Image Loader", "2011 - 2015", "Sergey Tarasevich", "https://github.com/nostra13/Android-Universal-Image-Loader", StandardLicenses.APACHE2), |             new SoftwareComponent("Universal Image Loader", "2011 - 2015", "Sergey Tarasevich", "https://github.com/nostra13/Android-Universal-Image-Loader", StandardLicenses.APACHE2), | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import android.arch.persistence.room.PrimaryKey; | |||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamType; | import org.schabi.newpipe.extractor.stream.StreamType; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.util.Constants; | import org.schabi.newpipe.util.Constants; | ||||||
|  |  | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
|   | |||||||
| @@ -239,7 +239,8 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC | |||||||
|         if (rootView == null && getView() != null) rootView = getView(); |         if (rootView == null && getView() != null) rootView = getView(); | ||||||
|         if (rootView == null) return; |         if (rootView == null) return; | ||||||
|  |  | ||||||
|         ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView, ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId)); |         ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView, | ||||||
|  |                 ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|   | |||||||
| @@ -27,10 +27,10 @@ import org.schabi.newpipe.extractor.ServiceList; | |||||||
| import org.schabi.newpipe.extractor.StreamingService; | import org.schabi.newpipe.extractor.StreamingService; | ||||||
| import org.schabi.newpipe.extractor.kiosk.KioskList; | import org.schabi.newpipe.extractor.kiosk.KioskList; | ||||||
| import org.schabi.newpipe.fragments.list.channel.ChannelFragment; | import org.schabi.newpipe.fragments.list.channel.ChannelFragment; | ||||||
| import org.schabi.newpipe.fragments.list.feed.FeedFragment; | import org.schabi.newpipe.local.feed.FeedFragment; | ||||||
| import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; | import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; | ||||||
| import org.schabi.newpipe.fragments.local.bookmark.BookmarkFragment; | import org.schabi.newpipe.local.bookmark.BookmarkFragment; | ||||||
| import org.schabi.newpipe.fragments.subscription.SubscriptionFragment; | import org.schabi.newpipe.local.subscription.SubscriptionFragment; | ||||||
| import org.schabi.newpipe.report.ErrorActivity; | import org.schabi.newpipe.report.ErrorActivity; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.KioskTranslator; | import org.schabi.newpipe.util.KioskTranslator; | ||||||
|   | |||||||
| @@ -64,15 +64,15 @@ import org.schabi.newpipe.fragments.BackPressable; | |||||||
| import org.schabi.newpipe.fragments.BaseStateFragment; | import org.schabi.newpipe.fragments.BaseStateFragment; | ||||||
| import org.schabi.newpipe.util.StreamItemAdapter; | import org.schabi.newpipe.util.StreamItemAdapter; | ||||||
| import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; | import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; | ||||||
| import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog; | import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; | ||||||
| import org.schabi.newpipe.info_list.InfoItemBuilder; | import org.schabi.newpipe.info_list.InfoItemBuilder; | ||||||
| import org.schabi.newpipe.info_list.InfoItemDialog; | import org.schabi.newpipe.info_list.InfoItemDialog; | ||||||
| import org.schabi.newpipe.player.MainVideoPlayer; | import org.schabi.newpipe.player.MainVideoPlayer; | ||||||
| import org.schabi.newpipe.player.PopupVideoPlayer; | import org.schabi.newpipe.player.PopupVideoPlayer; | ||||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | import org.schabi.newpipe.player.helper.PlayerHelper; | ||||||
| import org.schabi.newpipe.player.old.PlayVideoActivity; | import org.schabi.newpipe.player.old.PlayVideoActivity; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.playlist.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.Constants; | import org.schabi.newpipe.util.Constants; | ||||||
| import org.schabi.newpipe.util.ExtractorHelper; | import org.schabi.newpipe.util.ExtractorHelper; | ||||||
|   | |||||||
| @@ -20,10 +20,10 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; | |||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| import org.schabi.newpipe.fragments.BaseStateFragment; | import org.schabi.newpipe.fragments.BaseStateFragment; | ||||||
| import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | ||||||
| import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog; | import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; | ||||||
| import org.schabi.newpipe.info_list.InfoItemDialog; | import org.schabi.newpipe.info_list.InfoItemDialog; | ||||||
| import org.schabi.newpipe.info_list.InfoListAdapter; | import org.schabi.newpipe.info_list.InfoListAdapter; | ||||||
| import org.schabi.newpipe.playlist.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| import org.schabi.newpipe.util.OnClickGesture; | import org.schabi.newpipe.util.OnClickGesture; | ||||||
| import org.schabi.newpipe.util.StateSaver; | import org.schabi.newpipe.util.StateSaver; | ||||||
| @@ -140,9 +140,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem | |||||||
|         infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<StreamInfoItem>() { |         infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<StreamInfoItem>() { | ||||||
|             @Override |             @Override | ||||||
|             public void selected(StreamInfoItem selectedItem) { |             public void selected(StreamInfoItem selectedItem) { | ||||||
|                 onItemSelected(selectedItem); |                 onStreamSelected(selectedItem); | ||||||
|                 NavigationHelper.openVideoDetailFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(), |  | ||||||
|                         selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             @Override |             @Override | ||||||
| @@ -178,6 +176,12 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private void onStreamSelected(StreamInfoItem selectedItem) { | ||||||
|  |         onItemSelected(selectedItem); | ||||||
|  |         NavigationHelper.openVideoDetailFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(), | ||||||
|  |                 selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     protected void onScrollToBottom() { |     protected void onScrollToBottom() { | ||||||
|         if (hasMoreItems() && !isLoading.get()) { |         if (hasMoreItems() && !isLoading.get()) { | ||||||
|             loadMoreItems(); |             loadMoreItems(); | ||||||
| @@ -216,6 +220,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem | |||||||
|  |  | ||||||
|         new InfoItemDialog(getActivity(), item, commands, actions).show(); |         new InfoItemDialog(getActivity(), item, commands, actions).show(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|     // Menu |     // Menu | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|   | |||||||
| @@ -32,17 +32,15 @@ import org.schabi.newpipe.extractor.ListExtractor; | |||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
| import org.schabi.newpipe.extractor.channel.ChannelInfo; | import org.schabi.newpipe.extractor.channel.ChannelInfo; | ||||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||||
| import org.schabi.newpipe.extractor.stream.Stream; |  | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; |  | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| import org.schabi.newpipe.fragments.list.BaseListInfoFragment; | import org.schabi.newpipe.fragments.list.BaseListInfoFragment; | ||||||
| import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog; |  | ||||||
| import org.schabi.newpipe.info_list.InfoItemDialog; | import org.schabi.newpipe.info_list.InfoItemDialog; | ||||||
| import org.schabi.newpipe.playlist.ChannelPlayQueue; | import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; | ||||||
| import org.schabi.newpipe.playlist.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
|  | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.subscription.SubscriptionService; | import org.schabi.newpipe.local.subscription.SubscriptionService; | ||||||
| import org.schabi.newpipe.util.AnimationUtils; | import org.schabi.newpipe.util.AnimationUtils; | ||||||
| import org.schabi.newpipe.util.ExtractorHelper; | import org.schabi.newpipe.util.ExtractorHelper; | ||||||
| import org.schabi.newpipe.util.ImageDisplayConstants; | import org.schabi.newpipe.util.ImageDisplayConstants; | ||||||
|   | |||||||
| @@ -27,14 +27,13 @@ import org.schabi.newpipe.extractor.ListExtractor; | |||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; |  | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| import org.schabi.newpipe.fragments.list.BaseListInfoFragment; | import org.schabi.newpipe.fragments.list.BaseListInfoFragment; | ||||||
| import org.schabi.newpipe.fragments.local.RemotePlaylistManager; | import org.schabi.newpipe.local.playlist.RemotePlaylistManager; | ||||||
| import org.schabi.newpipe.info_list.InfoItemDialog; | import org.schabi.newpipe.info_list.InfoItemDialog; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.playlist.PlaylistPlayQueue; | import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; | ||||||
| import org.schabi.newpipe.playlist.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.ExtractorHelper; | import org.schabi.newpipe.util.ExtractorHelper; | ||||||
| import org.schabi.newpipe.util.ImageDisplayConstants; | import org.schabi.newpipe.util.ImageDisplayConstants; | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ import org.schabi.newpipe.extractor.search.SearchEngine; | |||||||
| import org.schabi.newpipe.extractor.search.SearchResult; | import org.schabi.newpipe.extractor.search.SearchResult; | ||||||
| import org.schabi.newpipe.fragments.BackPressable; | import org.schabi.newpipe.fragments.BackPressable; | ||||||
| import org.schabi.newpipe.fragments.list.BaseListFragment; | import org.schabi.newpipe.fragments.list.BaseListFragment; | ||||||
| import org.schabi.newpipe.history.HistoryRecordManager; | import org.schabi.newpipe.local.history.HistoryRecordManager; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.Constants; | import org.schabi.newpipe.util.Constants; | ||||||
| import org.schabi.newpipe.util.AnimationUtils; | import org.schabi.newpipe.util.AnimationUtils; | ||||||
| @@ -544,7 +544,7 @@ public class SearchFragment | |||||||
|                                     howManyDeleted -> suggestionPublisher |                                     howManyDeleted -> suggestionPublisher | ||||||
|                                             .onNext(searchEditText.getText().toString()), |                                             .onNext(searchEditText.getText().toString()), | ||||||
|                                     throwable -> showSnackBarError(throwable, |                                     throwable -> showSnackBarError(throwable, | ||||||
|                                             UserAction.SOMETHING_ELSE, "none", |                                             UserAction.DELETE_FROM_HISTORY, "none", | ||||||
|                                             "Deleting item failed", R.string.general_error) |                                             "Deleting item failed", R.string.general_error) | ||||||
|                             ); |                             ); | ||||||
|                     disposables.add(onDelete); |                     disposables.add(onDelete); | ||||||
|   | |||||||
| @@ -63,24 +63,15 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd | |||||||
|     public void onBindViewHolder(SuggestionItemHolder holder, int position) { |     public void onBindViewHolder(SuggestionItemHolder holder, int position) { | ||||||
|         final SuggestionItem currentItem = getItem(position); |         final SuggestionItem currentItem = getItem(position); | ||||||
|         holder.updateFrom(currentItem); |         holder.updateFrom(currentItem); | ||||||
|         holder.queryView.setOnClickListener(new View.OnClickListener() { |         holder.queryView.setOnClickListener(v -> { | ||||||
|             @Override |  | ||||||
|             public void onClick(View v) { |  | ||||||
|             if (listener != null) listener.onSuggestionItemSelected(currentItem); |             if (listener != null) listener.onSuggestionItemSelected(currentItem); | ||||||
|             } |  | ||||||
|         }); |         }); | ||||||
|         holder.queryView.setOnLongClickListener(new View.OnLongClickListener() { |         holder.queryView.setOnLongClickListener(v -> { | ||||||
|             @Override |  | ||||||
|             public boolean onLongClick(View v) { |  | ||||||
|                 if (listener != null) listener.onSuggestionItemLongClick(currentItem); |                 if (listener != null) listener.onSuggestionItemLongClick(currentItem); | ||||||
|                 return true; |                 return true; | ||||||
|             } |  | ||||||
|         }); |         }); | ||||||
|         holder.insertView.setOnClickListener(new View.OnClickListener() { |         holder.insertView.setOnClickListener(v -> { | ||||||
|             @Override |  | ||||||
|             public void onClick(View v) { |  | ||||||
|             if (listener != null) listener.onSuggestionItemInserted(currentItem); |             if (listener != null) listener.onSuggestionItemInserted(currentItem); | ||||||
|             } |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,21 +0,0 @@ | |||||||
| package org.schabi.newpipe.fragments.local.bookmark; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; |  | ||||||
| import org.schabi.newpipe.database.stream.StreamStatisticsEntry; |  | ||||||
|  |  | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.List; |  | ||||||
|  |  | ||||||
| public final class LastPlayedFragment extends StatisticsPlaylistFragment { |  | ||||||
|     @Override |  | ||||||
|     protected String getName() { |  | ||||||
|         return getString(R.string.title_last_played); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected List<StreamStatisticsEntry> processResult(List<StreamStatisticsEntry> results)  { |  | ||||||
|         Collections.sort(results, (left, right) -> |  | ||||||
|                 right.latestAccessDate.compareTo(left.latestAccessDate)); |  | ||||||
|         return results; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| package org.schabi.newpipe.fragments.local.bookmark; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; |  | ||||||
| import org.schabi.newpipe.database.stream.StreamStatisticsEntry; |  | ||||||
|  |  | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.List; |  | ||||||
|  |  | ||||||
| public final class MostPlayedFragment extends StatisticsPlaylistFragment { |  | ||||||
|     @Override |  | ||||||
|     protected String getName() { |  | ||||||
|         return getString(R.string.title_most_played); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected List<StreamStatisticsEntry> processResult(List<StreamStatisticsEntry> results)  { |  | ||||||
|         Collections.sort(results, (left, right) -> |  | ||||||
|                 ((Long) right.watchCount).compareTo(left.watchCount)); |  | ||||||
|         return results; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,141 +0,0 @@ | |||||||
| package org.schabi.newpipe.history; |  | ||||||
|  |  | ||||||
| import android.content.Intent; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.support.design.widget.FloatingActionButton; |  | ||||||
| import android.support.design.widget.TabLayout; |  | ||||||
| import android.support.v4.app.Fragment; |  | ||||||
| import android.support.v4.app.FragmentManager; |  | ||||||
| import android.support.v4.app.FragmentPagerAdapter; |  | ||||||
| import android.support.v4.app.FragmentStatePagerAdapter; |  | ||||||
| import android.support.v4.view.ViewPager; |  | ||||||
| import android.support.v7.app.AppCompatActivity; |  | ||||||
| import android.support.v7.widget.Toolbar; |  | ||||||
| import android.view.Menu; |  | ||||||
| import android.view.MenuItem; |  | ||||||
|  |  | ||||||
| import com.jakewharton.rxbinding2.view.RxView; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; |  | ||||||
| import org.schabi.newpipe.settings.SettingsActivity; |  | ||||||
| import org.schabi.newpipe.util.ThemeHelper; |  | ||||||
|  |  | ||||||
| import io.reactivex.android.schedulers.AndroidSchedulers; |  | ||||||
|  |  | ||||||
| public class HistoryActivity extends AppCompatActivity { |  | ||||||
|  |  | ||||||
|     private static final String TAG = "HistoryActivity"; |  | ||||||
|     /** |  | ||||||
|      * The {@link android.support.v4.view.PagerAdapter} that will provide |  | ||||||
|      * fragments for each of the sections. We use a |  | ||||||
|      * {@link FragmentPagerAdapter} derivative, which will keep every |  | ||||||
|      * loaded fragment in memory. If this becomes too memory intensive, it |  | ||||||
|      * may be best to switch to a |  | ||||||
|      * {@link android.support.v4.app.FragmentStatePagerAdapter}. |  | ||||||
|      */ |  | ||||||
|     private SectionsPagerAdapter mSectionsPagerAdapter; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * The {@link ViewPager} that will host the section contents. |  | ||||||
|      */ |  | ||||||
|     private ViewPager mViewPager; |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
|         ThemeHelper.setTheme(this); |  | ||||||
|         setContentView(R.layout.activity_history); |  | ||||||
|  |  | ||||||
|         Toolbar toolbar = findViewById(R.id.toolbar); |  | ||||||
|         setSupportActionBar(toolbar); |  | ||||||
|         if (getSupportActionBar() != null) { |  | ||||||
|             getSupportActionBar().setDisplayHomeAsUpEnabled(true); |  | ||||||
|             getSupportActionBar().setTitle(R.string.title_activity_history); |  | ||||||
|         } |  | ||||||
|         // Create the adapter that will return a fragment for each of the three |  | ||||||
|         // primary sections of the activity. |  | ||||||
|         mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); |  | ||||||
|  |  | ||||||
|         // Set up the ViewPager with the sections adapter. |  | ||||||
|         mViewPager = findViewById(R.id.container); |  | ||||||
|         mViewPager.setAdapter(mSectionsPagerAdapter); |  | ||||||
|  |  | ||||||
|         TabLayout tabLayout = findViewById(R.id.tabs); |  | ||||||
|         tabLayout.setupWithViewPager(mViewPager); |  | ||||||
|  |  | ||||||
|         final FloatingActionButton fab = findViewById(R.id.fab); |  | ||||||
|         RxView.clicks(fab) |  | ||||||
|                 .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                 .subscribe(ignored -> { |  | ||||||
|                     int currentItem = mViewPager.getCurrentItem(); |  | ||||||
|                     HistoryFragment fragment = (HistoryFragment) mSectionsPagerAdapter |  | ||||||
|                             .instantiateItem(mViewPager, currentItem); |  | ||||||
|                     fragment.onHistoryCleared(); |  | ||||||
|                 }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean onCreateOptionsMenu(Menu menu) { |  | ||||||
|         // Inflate the menu; this adds items to the action bar if it is present. |  | ||||||
|         getMenuInflater().inflate(R.menu.menu_history, menu); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean onOptionsItemSelected(MenuItem item) { |  | ||||||
|         switch (item.getItemId()) { |  | ||||||
|             case android.R.id.home: |  | ||||||
|                 finish(); |  | ||||||
|                 return true; |  | ||||||
|             case R.id.action_settings: |  | ||||||
|                 Intent intent = new Intent(this, SettingsActivity.class); |  | ||||||
|                 startActivity(intent); |  | ||||||
|                 return true; |  | ||||||
|         } |  | ||||||
|         return super.onOptionsItemSelected(item); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * A {@link FragmentPagerAdapter} that returns a fragment corresponding to |  | ||||||
|      * one of the sections/tabs/pages. |  | ||||||
|      */ |  | ||||||
|     public class SectionsPagerAdapter extends FragmentStatePagerAdapter { |  | ||||||
|  |  | ||||||
|         public SectionsPagerAdapter(FragmentManager fm) { |  | ||||||
|             super(fm); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public Fragment getItem(int position) { |  | ||||||
|             Fragment fragment; |  | ||||||
|             switch (position) { |  | ||||||
|                 case 0: |  | ||||||
|                     fragment = SearchHistoryFragment.newInstance(); |  | ||||||
|                     break; |  | ||||||
|                 case 1: |  | ||||||
|                     fragment = WatchHistoryFragment.newInstance(); |  | ||||||
|                     break; |  | ||||||
|                 default: |  | ||||||
|                     throw new IllegalArgumentException("position: " + position); |  | ||||||
|             } |  | ||||||
|             return fragment; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public CharSequence getPageTitle(int position) { |  | ||||||
|             switch (position) { |  | ||||||
|                 case 0: |  | ||||||
|                     return getString(R.string.title_history_search); |  | ||||||
|                 case 1: |  | ||||||
|                     return getString(R.string.title_history_view); |  | ||||||
|             } |  | ||||||
|             throw new IllegalArgumentException("position: " + position); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public int getCount() { |  | ||||||
|             // Show 3 total pages. |  | ||||||
|             return 2; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,286 +0,0 @@ | |||||||
| package org.schabi.newpipe.history; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.os.Parcelable; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
| import android.support.annotation.CallSuper; |  | ||||||
| import android.support.annotation.MainThread; |  | ||||||
| import android.support.annotation.NonNull; |  | ||||||
| import android.support.annotation.Nullable; |  | ||||||
| import android.support.annotation.StringRes; |  | ||||||
| import android.support.design.widget.Snackbar; |  | ||||||
| import android.support.v7.app.AlertDialog; |  | ||||||
| import android.support.v7.widget.LinearLayoutManager; |  | ||||||
| import android.support.v7.widget.RecyclerView; |  | ||||||
| import android.util.Log; |  | ||||||
| import android.view.LayoutInflater; |  | ||||||
| import android.view.View; |  | ||||||
| import android.view.ViewGroup; |  | ||||||
|  |  | ||||||
| import org.reactivestreams.Subscriber; |  | ||||||
| import org.reactivestreams.Subscription; |  | ||||||
| import org.schabi.newpipe.BaseFragment; |  | ||||||
| import org.schabi.newpipe.R; |  | ||||||
|  |  | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.Collection; |  | ||||||
| import java.util.List; |  | ||||||
|  |  | ||||||
| import icepick.State; |  | ||||||
| import io.reactivex.Flowable; |  | ||||||
| import io.reactivex.Single; |  | ||||||
| import io.reactivex.android.schedulers.AndroidSchedulers; |  | ||||||
| import io.reactivex.disposables.CompositeDisposable; |  | ||||||
| import io.reactivex.disposables.Disposable; |  | ||||||
|  |  | ||||||
| import static org.schabi.newpipe.util.AnimationUtils.animateView; |  | ||||||
|  |  | ||||||
| public abstract class HistoryFragment<E> extends BaseFragment |  | ||||||
|         implements HistoryEntryAdapter.OnHistoryItemClickListener<E> { |  | ||||||
|  |  | ||||||
|     private SharedPreferences mSharedPreferences; |  | ||||||
|     private String mHistoryIsEnabledKey; |  | ||||||
|     private boolean mHistoryIsEnabled; |  | ||||||
|     private HistoryIsEnabledChangeListener mHistoryIsEnabledChangeListener; |  | ||||||
|  |  | ||||||
|     private View mDisabledView; |  | ||||||
|     private View mEmptyHistoryView; |  | ||||||
|  |  | ||||||
|     @State |  | ||||||
|     Parcelable mRecyclerViewState; |  | ||||||
|     private RecyclerView mRecyclerView; |  | ||||||
|     private HistoryEntryAdapter<E, ? extends RecyclerView.ViewHolder> mHistoryAdapter; |  | ||||||
|  |  | ||||||
|     private Subscription historySubscription; |  | ||||||
|  |  | ||||||
|     protected HistoryRecordManager historyRecordManager; |  | ||||||
|     protected CompositeDisposable disposables; |  | ||||||
|  |  | ||||||
|     @StringRes |  | ||||||
|     abstract int getEnabledConfigKey(); |  | ||||||
|  |  | ||||||
|     @CallSuper |  | ||||||
|     @Override |  | ||||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
|  |  | ||||||
|         mHistoryIsEnabledKey = getString(getEnabledConfigKey()); |  | ||||||
|  |  | ||||||
|         mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext()); |  | ||||||
|         // Read history enabled from preferences |  | ||||||
|         mHistoryIsEnabled = isHistoryEnabled(); |  | ||||||
|         // Register history enabled listener |  | ||||||
|         mSharedPreferences.registerOnSharedPreferenceChangeListener(mHistoryIsEnabledChangeListener); |  | ||||||
|  |  | ||||||
|         historyRecordManager = new HistoryRecordManager(getContext()); |  | ||||||
|         disposables = new CompositeDisposable(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     protected abstract HistoryEntryAdapter<E, ? extends RecyclerView.ViewHolder> createAdapter(); |  | ||||||
|  |  | ||||||
|     protected abstract Single<List<Long>> insert(final Collection<E> entries); |  | ||||||
|  |  | ||||||
|     protected abstract Single<Integer> delete(final Collection<E> entries); |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     protected abstract Flowable<List<E>> getAll(); |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onResume() { |  | ||||||
|         super.onResume(); |  | ||||||
|  |  | ||||||
|         getAll().observeOn(AndroidSchedulers.mainThread()).subscribe(getHistorySubscriber()); |  | ||||||
|  |  | ||||||
|         final boolean newEnabled = isHistoryEnabled(); |  | ||||||
|         if (newEnabled != mHistoryIsEnabled) { |  | ||||||
|             onHistoryIsEnabledChanged(newEnabled); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     private Subscriber<List<E>> getHistorySubscriber() { |  | ||||||
|         return new Subscriber<List<E>>() { |  | ||||||
|             @Override |  | ||||||
|             public void onSubscribe(Subscription s) { |  | ||||||
|                 if (historySubscription != null) historySubscription.cancel(); |  | ||||||
|  |  | ||||||
|                 historySubscription = s; |  | ||||||
|                 historySubscription.request(1); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             @Override |  | ||||||
|             public void onNext(List<E> entries) { |  | ||||||
|                 if (!entries.isEmpty()) { |  | ||||||
|                     mHistoryAdapter.setEntries(entries); |  | ||||||
|                     animateView(mEmptyHistoryView, false, 200); |  | ||||||
|  |  | ||||||
|                     if (mRecyclerViewState != null) { |  | ||||||
|                         mRecyclerView.getLayoutManager().onRestoreInstanceState(mRecyclerViewState); |  | ||||||
|                         mRecyclerViewState = null; |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     mHistoryAdapter.clear(); |  | ||||||
|                     showEmptyHistory(); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (historySubscription != null) historySubscription.request(1); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             @Override |  | ||||||
|             public void onError(Throwable t) { |  | ||||||
|  |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             @Override |  | ||||||
|             public void onComplete() { |  | ||||||
|  |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private boolean isHistoryEnabled() { |  | ||||||
|         return mSharedPreferences.getBoolean(mHistoryIsEnabledKey, false); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Called when the history is cleared to update the views |  | ||||||
|      */ |  | ||||||
|     @MainThread |  | ||||||
|     public void onHistoryCleared() { |  | ||||||
|         if (getContext() == null) return; |  | ||||||
|  |  | ||||||
|         new AlertDialog.Builder(getContext()) |  | ||||||
|                 .setTitle(R.string.delete_all) |  | ||||||
|                 .setMessage(R.string.delete_all_history_prompt) |  | ||||||
|                 .setCancelable(true) |  | ||||||
|                 .setNegativeButton(R.string.cancel, null) |  | ||||||
|                 .setPositiveButton(R.string.delete_all, (dialog, i) -> clearHistory()) |  | ||||||
|                 .show(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     protected void makeSnackbar(@StringRes final int text) { |  | ||||||
|         if (getActivity() == null) return; |  | ||||||
|  |  | ||||||
|         View view = getActivity().findViewById(R.id.main_content); |  | ||||||
|         if (view == null) view = mRecyclerView.getRootView(); |  | ||||||
|         Snackbar.make(view, text, Snackbar.LENGTH_LONG).show(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void clearHistory() { |  | ||||||
|         final Collection<E> itemsToDelete = new ArrayList<>(mHistoryAdapter.getItems()); |  | ||||||
|  |  | ||||||
|         final Disposable deletion = delete(itemsToDelete) |  | ||||||
|                 .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                 .subscribe( |  | ||||||
|                         ignored -> Log.d(TAG, "Clear history deleted [" + |  | ||||||
|                         itemsToDelete.size() + "] items."), |  | ||||||
|                         error -> Log.e(TAG, "Clear history delete step failed", error) |  | ||||||
|                 ); |  | ||||||
|  |  | ||||||
|         final Disposable cleanUp = historyRecordManager.removeOrphanedRecords() |  | ||||||
|                 .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                 .subscribe( |  | ||||||
|                         ignored -> Log.d(TAG, "Clear history deleted orphaned stream records"), |  | ||||||
|                         error -> Log.e(TAG, "Clear history remove orphaned records failed", error) |  | ||||||
|                 ); |  | ||||||
|  |  | ||||||
|         disposables.addAll(deletion, cleanUp); |  | ||||||
|  |  | ||||||
|         makeSnackbar(R.string.history_cleared); |  | ||||||
|         mHistoryAdapter.clear(); |  | ||||||
|         showEmptyHistory(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void showEmptyHistory() { |  | ||||||
|         if (mHistoryIsEnabled) { |  | ||||||
|             animateView(mEmptyHistoryView, true, 200); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Nullable |  | ||||||
|     @CallSuper |  | ||||||
|     @Override |  | ||||||
|     public View onCreateView(@NonNull LayoutInflater inflater, |  | ||||||
|                              @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { |  | ||||||
|         View rootView = inflater.inflate(R.layout.fragment_history, container, false); |  | ||||||
|         mRecyclerView = rootView.findViewById(R.id.history_view); |  | ||||||
|  |  | ||||||
|         RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext(), |  | ||||||
|                 LinearLayoutManager.VERTICAL, false); |  | ||||||
|         mRecyclerView.setLayoutManager(layoutManager); |  | ||||||
|  |  | ||||||
|         mHistoryAdapter = createAdapter(); |  | ||||||
|         mHistoryAdapter.setOnHistoryItemClickListener(this); |  | ||||||
|         mRecyclerView.setAdapter(mHistoryAdapter); |  | ||||||
|         mDisabledView = rootView.findViewById(R.id.history_disabled_view); |  | ||||||
|         mEmptyHistoryView = rootView.findViewById(R.id.history_empty); |  | ||||||
|  |  | ||||||
|         if (mHistoryIsEnabled) { |  | ||||||
|             mRecyclerView.setVisibility(View.VISIBLE); |  | ||||||
|         } else { |  | ||||||
|             mRecyclerView.setVisibility(View.GONE); |  | ||||||
|             mDisabledView.setVisibility(View.VISIBLE); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return rootView; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @CallSuper |  | ||||||
|     @Override |  | ||||||
|     public void onDestroy() { |  | ||||||
|         super.onDestroy(); |  | ||||||
|  |  | ||||||
|         if (disposables != null) disposables.dispose(); |  | ||||||
|         if (historySubscription != null) historySubscription.cancel(); |  | ||||||
|  |  | ||||||
|         mSharedPreferences.unregisterOnSharedPreferenceChangeListener(mHistoryIsEnabledChangeListener); |  | ||||||
|         mSharedPreferences = null; |  | ||||||
|         mHistoryIsEnabledChangeListener = null; |  | ||||||
|         mHistoryIsEnabledKey = null; |  | ||||||
|         historySubscription = null; |  | ||||||
|         disposables = null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onPause() { |  | ||||||
|         super.onPause(); |  | ||||||
|         mRecyclerViewState = mRecyclerView.getLayoutManager().onSaveInstanceState(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Called when history enabled flag is changed. |  | ||||||
|      * |  | ||||||
|      * @param historyIsEnabled the new value |  | ||||||
|      */ |  | ||||||
|     @CallSuper |  | ||||||
|     public void onHistoryIsEnabledChanged(boolean historyIsEnabled) { |  | ||||||
|         mHistoryIsEnabled = historyIsEnabled; |  | ||||||
|         if (historyIsEnabled) { |  | ||||||
|             animateView(mRecyclerView, true, 300); |  | ||||||
|             animateView(mDisabledView, false, 300); |  | ||||||
|             if (mHistoryAdapter.isEmpty()) { |  | ||||||
|                 animateView(mEmptyHistoryView, true, 300); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             animateView(mRecyclerView, false, 300); |  | ||||||
|             animateView(mDisabledView, true, 300); |  | ||||||
|             animateView(mEmptyHistoryView, false, 300); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private class HistoryIsEnabledChangeListener |  | ||||||
|             implements SharedPreferences.OnSharedPreferenceChangeListener { |  | ||||||
|         @Override |  | ||||||
|         public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { |  | ||||||
|             if (key.equals(mHistoryIsEnabledKey)) { |  | ||||||
|                 boolean enabled = sharedPreferences.getBoolean(key, false); |  | ||||||
|                 if (mHistoryIsEnabled != enabled) { |  | ||||||
|                     onHistoryIsEnabledChanged(enabled); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,145 +0,0 @@ | |||||||
| package org.schabi.newpipe.history; |  | ||||||
|  |  | ||||||
| import android.content.Context; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.support.annotation.NonNull; |  | ||||||
| import android.support.annotation.Nullable; |  | ||||||
| import android.support.annotation.StringRes; |  | ||||||
| import android.support.v7.app.AlertDialog; |  | ||||||
| import android.support.v7.widget.RecyclerView; |  | ||||||
| import android.util.Log; |  | ||||||
| import android.view.LayoutInflater; |  | ||||||
| import android.view.View; |  | ||||||
| import android.view.ViewGroup; |  | ||||||
| import android.widget.TextView; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; |  | ||||||
| import org.schabi.newpipe.database.history.model.SearchHistoryEntry; |  | ||||||
| import org.schabi.newpipe.extractor.NewPipe; |  | ||||||
| import org.schabi.newpipe.util.Localization; |  | ||||||
| import org.schabi.newpipe.util.NavigationHelper; |  | ||||||
|  |  | ||||||
| import java.util.Collection; |  | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.List; |  | ||||||
|  |  | ||||||
| import io.reactivex.Flowable; |  | ||||||
| import io.reactivex.Single; |  | ||||||
| import io.reactivex.android.schedulers.AndroidSchedulers; |  | ||||||
| import io.reactivex.disposables.Disposable; |  | ||||||
|  |  | ||||||
| public class SearchHistoryFragment extends HistoryFragment<SearchHistoryEntry> { |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     public static SearchHistoryFragment newInstance() { |  | ||||||
|         return new SearchHistoryFragment(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     @Override |  | ||||||
|     protected SearchHistoryAdapter createAdapter() { |  | ||||||
|         return new SearchHistoryAdapter(getContext()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected Single<List<Long>> insert(Collection<SearchHistoryEntry> entries) { |  | ||||||
|         return historyRecordManager.insertSearches(entries); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected Single<Integer> delete(Collection<SearchHistoryEntry> entries) { |  | ||||||
|         return historyRecordManager.deleteSearches(entries); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     @Override |  | ||||||
|     protected Flowable<List<SearchHistoryEntry>> getAll() { |  | ||||||
|         return historyRecordManager.getSearchHistory(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @StringRes |  | ||||||
|     @Override |  | ||||||
|     int getEnabledConfigKey() { |  | ||||||
|         return R.string.enable_search_history_key; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onHistoryItemClick(final SearchHistoryEntry historyItem) { |  | ||||||
|         NavigationHelper.openSearch(getContext(), historyItem.getServiceId(), |  | ||||||
|                 historyItem.getSearch()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onHistoryItemLongClick(final SearchHistoryEntry item) { |  | ||||||
|         if (activity == null) return; |  | ||||||
|  |  | ||||||
|         new AlertDialog.Builder(activity) |  | ||||||
|                 .setTitle(item.getSearch()) |  | ||||||
|                 .setMessage(R.string.delete_item_search_history) |  | ||||||
|                 .setCancelable(true) |  | ||||||
|                 .setNeutralButton(R.string.cancel, null) |  | ||||||
|                 .setPositiveButton(R.string.delete_one, (dialog, i) -> { |  | ||||||
|                     final Disposable onDelete = historyRecordManager |  | ||||||
|                             .deleteSearches(Collections.singleton(item)) |  | ||||||
|                             .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                             .subscribe( |  | ||||||
|                                     ignored -> {/*successful*/}, |  | ||||||
|                                     error -> Log.e(TAG, "Search history Delete One failed:", error) |  | ||||||
|                             ); |  | ||||||
|                     disposables.add(onDelete); |  | ||||||
|                     makeSnackbar(R.string.item_deleted); |  | ||||||
|                 }) |  | ||||||
|                 .setNegativeButton(R.string.delete_all, (dialog, i) -> { |  | ||||||
|                     final Disposable onDeleteAll = historyRecordManager |  | ||||||
|                             .deleteSearchHistory(item.getSearch()) |  | ||||||
|                             .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                             .subscribe( |  | ||||||
|                                     ignored -> {/*successful*/}, |  | ||||||
|                                     error -> Log.e(TAG, "Search history Delete All failed:", error) |  | ||||||
|                             ); |  | ||||||
|                     disposables.add(onDeleteAll); |  | ||||||
|                     makeSnackbar(R.string.item_deleted); |  | ||||||
|                 }) |  | ||||||
|                 .show(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private static class ViewHolder extends RecyclerView.ViewHolder { |  | ||||||
|         private final TextView search; |  | ||||||
|         private final TextView info; |  | ||||||
|  |  | ||||||
|         public ViewHolder(View itemView) { |  | ||||||
|             super(itemView); |  | ||||||
|             search = itemView.findViewById(R.id.search); |  | ||||||
|             info = itemView.findViewById(R.id.info); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     protected class SearchHistoryAdapter extends HistoryEntryAdapter<SearchHistoryEntry, ViewHolder> { |  | ||||||
|  |  | ||||||
|         SearchHistoryAdapter(Context context) { |  | ||||||
|             super(context); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |  | ||||||
|             LayoutInflater inflater = LayoutInflater.from(parent.getContext()); |  | ||||||
|             View rootView = inflater.inflate(R.layout.item_search_history, parent, false); |  | ||||||
|             return new ViewHolder(rootView); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         void onBindViewHolder(ViewHolder holder, SearchHistoryEntry entry, int position) { |  | ||||||
|             holder.search.setText(entry.getSearch()); |  | ||||||
|  |  | ||||||
|             final String info = Localization.concatenateStrings( |  | ||||||
|                     getFormattedDate(entry.getCreationDate()), |  | ||||||
|                     NewPipe.getNameOfService(entry.getServiceId())); |  | ||||||
|             holder.info.setText(info); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,171 +0,0 @@ | |||||||
| package org.schabi.newpipe.history; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| import android.content.Context; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.support.annotation.NonNull; |  | ||||||
| import android.support.annotation.Nullable; |  | ||||||
| import android.support.annotation.StringRes; |  | ||||||
| import android.support.v7.app.AlertDialog; |  | ||||||
| import android.support.v7.widget.RecyclerView; |  | ||||||
| import android.util.Log; |  | ||||||
| import android.view.LayoutInflater; |  | ||||||
| import android.view.View; |  | ||||||
| import android.view.ViewGroup; |  | ||||||
| import android.widget.ImageView; |  | ||||||
| import android.widget.TextView; |  | ||||||
|  |  | ||||||
| import com.nostra13.universalimageloader.core.ImageLoader; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; |  | ||||||
| import org.schabi.newpipe.database.history.model.StreamHistoryEntry; |  | ||||||
| import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder; |  | ||||||
| import org.schabi.newpipe.util.ImageDisplayConstants; |  | ||||||
| import org.schabi.newpipe.util.Localization; |  | ||||||
| import org.schabi.newpipe.util.NavigationHelper; |  | ||||||
|  |  | ||||||
| import java.util.Collection; |  | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.List; |  | ||||||
|  |  | ||||||
| import io.reactivex.Flowable; |  | ||||||
| import io.reactivex.Single; |  | ||||||
| import io.reactivex.android.schedulers.AndroidSchedulers; |  | ||||||
| import io.reactivex.disposables.Disposable; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| public class WatchHistoryFragment extends HistoryFragment<StreamHistoryEntry> { |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     public static WatchHistoryFragment newInstance() { |  | ||||||
|         return new WatchHistoryFragment(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @StringRes |  | ||||||
|     @Override |  | ||||||
|     int getEnabledConfigKey() { |  | ||||||
|         return R.string.enable_watch_history_key; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     @Override |  | ||||||
|     protected StreamHistoryAdapter createAdapter() { |  | ||||||
|         return new StreamHistoryAdapter(getContext()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected Single<List<Long>> insert(Collection<StreamHistoryEntry> entries) { |  | ||||||
|         return historyRecordManager.insertStreamHistory(entries); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected Single<Integer> delete(Collection<StreamHistoryEntry> entries) { |  | ||||||
|         return historyRecordManager.deleteStreamHistory(entries); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     @Override |  | ||||||
|     protected Flowable<List<StreamHistoryEntry>> getAll() { |  | ||||||
|         return historyRecordManager.getStreamHistory(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onHistoryItemClick(StreamHistoryEntry historyItem) { |  | ||||||
|         NavigationHelper.openVideoDetail(getContext(), historyItem.serviceId, historyItem.url, |  | ||||||
|                 historyItem.title); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onHistoryItemLongClick(StreamHistoryEntry item) { |  | ||||||
|         new AlertDialog.Builder(activity) |  | ||||||
|                 .setTitle(item.title) |  | ||||||
|                 .setMessage(R.string.delete_stream_history_prompt) |  | ||||||
|                 .setCancelable(true) |  | ||||||
|                 .setNeutralButton(R.string.cancel, null) |  | ||||||
|                 .setPositiveButton(R.string.delete_one, (dialog, i) -> { |  | ||||||
|                     final Disposable onDelete = historyRecordManager |  | ||||||
|                             .deleteStreamHistory(Collections.singleton(item)) |  | ||||||
|                             .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                             .subscribe( |  | ||||||
|                                     ignored -> {/*successful*/}, |  | ||||||
|                                     error -> Log.e(TAG, "Watch history Delete One failed:", error) |  | ||||||
|                             ); |  | ||||||
|                     disposables.add(onDelete); |  | ||||||
|                     makeSnackbar(R.string.item_deleted); |  | ||||||
|                 }) |  | ||||||
|                 .setNegativeButton(R.string.delete_all, (dialog, i) -> { |  | ||||||
|                     final Disposable onDeleteAll = historyRecordManager |  | ||||||
|                             .deleteStreamHistory(item.streamId) |  | ||||||
|                             .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                             .subscribe( |  | ||||||
|                                     ignored -> {/*successful*/}, |  | ||||||
|                                     error -> Log.e(TAG, "Watch history Delete All failed:", error) |  | ||||||
|                             ); |  | ||||||
|                     disposables.add(onDeleteAll); |  | ||||||
|                     makeSnackbar(R.string.item_deleted); |  | ||||||
|                 }) |  | ||||||
|                 .show(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private static class StreamHistoryAdapter extends HistoryEntryAdapter<StreamHistoryEntry, ViewHolder> { |  | ||||||
|  |  | ||||||
|         StreamHistoryAdapter(Context context) { |  | ||||||
|             super(context); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |  | ||||||
|             LayoutInflater inflater = LayoutInflater.from(parent.getContext()); |  | ||||||
|             View itemView = inflater.inflate(R.layout.list_stream_item, parent, false); |  | ||||||
|             return new ViewHolder(itemView); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public void onViewRecycled(ViewHolder holder) { |  | ||||||
|             holder.itemView.setOnClickListener(null); |  | ||||||
|             ImageLoader.getInstance() |  | ||||||
|                     .cancelDisplayTask(holder.thumbnailView); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         void onBindViewHolder(ViewHolder holder, StreamHistoryEntry entry, int position) { |  | ||||||
|             final String formattedDate = getFormattedDate(entry.accessDate); |  | ||||||
|             final String info; |  | ||||||
|             if (entry.repeatCount > 1) { |  | ||||||
|                 info = Localization.concatenateStrings(formattedDate, |  | ||||||
|                         getFormattedViewString(entry.repeatCount)); |  | ||||||
|             } else { |  | ||||||
|                 info = formattedDate; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             holder.info.setText(info); |  | ||||||
|             holder.streamTitle.setText(entry.title); |  | ||||||
|             holder.uploader.setText(entry.uploader); |  | ||||||
|             holder.duration.setText(Localization.getDurationString(entry.duration)); |  | ||||||
|             ImageLoader.getInstance().displayImage(entry.thumbnailUrl, holder.thumbnailView, |  | ||||||
|                     ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private static class ViewHolder extends RecyclerView.ViewHolder { |  | ||||||
|         private final TextView info; |  | ||||||
|         private final TextView streamTitle; |  | ||||||
|         private final ImageView thumbnailView; |  | ||||||
|         private final TextView uploader; |  | ||||||
|         private final TextView duration; |  | ||||||
|  |  | ||||||
|         public ViewHolder(View itemView) { |  | ||||||
|             super(itemView); |  | ||||||
|             thumbnailView = itemView.findViewById(R.id.itemThumbnailView); |  | ||||||
|             info = itemView.findViewById(R.id.itemAdditionalDetails); |  | ||||||
|             streamTitle = itemView.findViewById(R.id.itemVideoTitleView); |  | ||||||
|             uploader = itemView.findViewById(R.id.itemUploaderView); |  | ||||||
|             duration = itemView.findViewById(R.id.itemDurationView); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.bookmark; | package org.schabi.newpipe.local; | ||||||
| 
 | 
 | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.support.v4.app.Fragment; | import android.support.v4.app.Fragment; | ||||||
| @@ -13,7 +13,6 @@ import android.view.View; | |||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.fragments.BaseStateFragment; | import org.schabi.newpipe.fragments.BaseStateFragment; | ||||||
| import org.schabi.newpipe.fragments.list.ListViewContract; | import org.schabi.newpipe.fragments.list.ListViewContract; | ||||||
| import org.schabi.newpipe.fragments.local.LocalItemListAdapter; |  | ||||||
| 
 | 
 | ||||||
| import static org.schabi.newpipe.util.AnimationUtils.animateView; | import static org.schabi.newpipe.util.AnimationUtils.animateView; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local; | package org.schabi.newpipe.local; | ||||||
| 
 | 
 | ||||||
| import android.support.v7.widget.RecyclerView; | import android.support.v7.widget.RecyclerView; | ||||||
| import android.view.View; | import android.view.View; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local; | package org.schabi.newpipe.local; | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.widget.ImageView; | import android.widget.ImageView; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local; | package org.schabi.newpipe.local; | ||||||
| 
 | 
 | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.support.v7.widget.RecyclerView; | import android.support.v7.widget.RecyclerView; | ||||||
| @@ -7,11 +7,13 @@ import android.view.View; | |||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.fragments.local.holder.LocalItemHolder; | import org.schabi.newpipe.local.HeaderFooterHolder; | ||||||
| import org.schabi.newpipe.fragments.local.holder.LocalPlaylistItemHolder; | import org.schabi.newpipe.local.LocalItemBuilder; | ||||||
| import org.schabi.newpipe.fragments.local.holder.LocalPlaylistStreamItemHolder; | import org.schabi.newpipe.local.holder.LocalItemHolder; | ||||||
| import org.schabi.newpipe.fragments.local.holder.LocalStatisticStreamItemHolder; | import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder; | ||||||
| import org.schabi.newpipe.fragments.local.holder.RemotePlaylistItemHolder; | import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder; | ||||||
|  | import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder; | ||||||
|  | import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder; | ||||||
| import org.schabi.newpipe.util.Localization; | import org.schabi.newpipe.util.Localization; | ||||||
| import org.schabi.newpipe.util.OnClickGesture; | import org.schabi.newpipe.util.OnClickGesture; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.bookmark; | package org.schabi.newpipe.local.bookmark; | ||||||
| 
 | 
 | ||||||
| import android.app.AlertDialog; | import android.app.AlertDialog; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| @@ -19,8 +19,9 @@ import org.schabi.newpipe.database.LocalItem; | |||||||
| import org.schabi.newpipe.database.playlist.PlaylistLocalItem; | import org.schabi.newpipe.database.playlist.PlaylistLocalItem; | ||||||
| import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | ||||||
| import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; | import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; | ||||||
| import org.schabi.newpipe.fragments.local.LocalPlaylistManager; | import org.schabi.newpipe.local.BaseLocalListFragment; | ||||||
| import org.schabi.newpipe.fragments.local.RemotePlaylistManager; | import org.schabi.newpipe.local.playlist.LocalPlaylistManager; | ||||||
|  | import org.schabi.newpipe.local.playlist.RemotePlaylistManager; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| import org.schabi.newpipe.util.OnClickGesture; | import org.schabi.newpipe.util.OnClickGesture; | ||||||
| @@ -38,9 +39,6 @@ import io.reactivex.disposables.CompositeDisposable; | |||||||
| public final class BookmarkFragment | public final class BookmarkFragment | ||||||
|         extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> { |         extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> { | ||||||
| 
 | 
 | ||||||
|     private View lastPlayedButton; |  | ||||||
|     private View mostPlayedButton; |  | ||||||
| 
 |  | ||||||
|     @State |     @State | ||||||
|     protected Parcelable itemsListState; |     protected Parcelable itemsListState; | ||||||
| 
 | 
 | ||||||
| @@ -94,15 +92,6 @@ public final class BookmarkFragment | |||||||
|         super.initViews(rootView, savedInstanceState); |         super.initViews(rootView, savedInstanceState); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |  | ||||||
|     protected View getListHeader() { |  | ||||||
|         final View headerRootLayout = activity.getLayoutInflater() |  | ||||||
|                 .inflate(R.layout.bookmark_header, itemsList, false); |  | ||||||
|         lastPlayedButton = headerRootLayout.findViewById(R.id.lastPlayed); |  | ||||||
|         mostPlayedButton = headerRootLayout.findViewById(R.id.mostPlayed); |  | ||||||
|         return headerRootLayout; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |     @Override | ||||||
|     protected void initListeners() { |     protected void initListeners() { | ||||||
|         super.initListeners(); |         super.initListeners(); | ||||||
| @@ -136,18 +125,6 @@ public final class BookmarkFragment | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| 
 |  | ||||||
|         lastPlayedButton.setOnClickListener(view -> { |  | ||||||
|             if (getParentFragment() != null) { |  | ||||||
|                 NavigationHelper.openLastPlayedFragment(getParentFragment().getFragmentManager()); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         mostPlayedButton.setOnClickListener(view -> { |  | ||||||
|             if (getParentFragment() != null) { |  | ||||||
|                 NavigationHelper.openMostPlayedFragment(getParentFragment().getFragmentManager()); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /////////////////////////////////////////////////////////////////////////// |     /////////////////////////////////////////////////////////////////////////// | ||||||
| @@ -180,8 +157,6 @@ public final class BookmarkFragment | |||||||
|     @Override |     @Override | ||||||
|     public void onDestroyView() { |     public void onDestroyView() { | ||||||
|         super.onDestroyView(); |         super.onDestroyView(); | ||||||
|         if (mostPlayedButton != null) mostPlayedButton.setOnClickListener(null); |  | ||||||
|         if (lastPlayedButton != null) lastPlayedButton.setOnClickListener(null); |  | ||||||
| 
 | 
 | ||||||
|         if (disposables != null) disposables.clear(); |         if (disposables != null) disposables.clear(); | ||||||
|         if (databaseSubscription != null) databaseSubscription.cancel(); |         if (databaseSubscription != null) databaseSubscription.cancel(); | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.dialog; | package org.schabi.newpipe.local.dialog; | ||||||
| 
 | 
 | ||||||
| import android.annotation.SuppressLint; | import android.annotation.SuppressLint; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| @@ -18,9 +18,9 @@ import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | |||||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| import org.schabi.newpipe.fragments.local.LocalItemListAdapter; | import org.schabi.newpipe.local.LocalItemListAdapter; | ||||||
| import org.schabi.newpipe.fragments.local.LocalPlaylistManager; | import org.schabi.newpipe.local.playlist.LocalPlaylistManager; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.util.OnClickGesture; | import org.schabi.newpipe.util.OnClickGesture; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.dialog; | package org.schabi.newpipe.local.dialog; | ||||||
| 
 | 
 | ||||||
| import android.app.AlertDialog; | import android.app.AlertDialog; | ||||||
| import android.app.Dialog; | import android.app.Dialog; | ||||||
| @@ -12,7 +12,7 @@ import android.widget.Toast; | |||||||
| import org.schabi.newpipe.NewPipeDatabase; | import org.schabi.newpipe.NewPipeDatabase; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.database.stream.model.StreamEntity; | import org.schabi.newpipe.database.stream.model.StreamEntity; | ||||||
| import org.schabi.newpipe.fragments.local.LocalPlaylistManager; | import org.schabi.newpipe.local.playlist.LocalPlaylistManager; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.dialog; | package org.schabi.newpipe.local.dialog; | ||||||
| 
 | 
 | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.list.feed; | package org.schabi.newpipe.local.feed; | ||||||
| 
 | 
 | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.os.Handler; | import android.os.Handler; | ||||||
| @@ -22,7 +22,7 @@ import org.schabi.newpipe.extractor.channel.ChannelInfo; | |||||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||||
| import org.schabi.newpipe.fragments.list.BaseListFragment; | import org.schabi.newpipe.fragments.list.BaseListFragment; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.subscription.SubscriptionService; | import org.schabi.newpipe.local.subscription.SubscriptionService; | ||||||
| 
 | 
 | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.history; | package org.schabi.newpipe.local.history; | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.res.Resources; | import android.content.res.Resources; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.history; | package org.schabi.newpipe.local.history; | ||||||
| 
 | 
 | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,22 @@ | |||||||
| package org.schabi.newpipe.history; | package org.schabi.newpipe.local.history; | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * Copyright (C) Mauricio Colli 2018 | ||||||
|  |  * HistoryRecordManager.java is part of NewPipe. | ||||||
|  |  * | ||||||
|  |  * NewPipe is free software: you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * NewPipe is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| @@ -27,6 +45,7 @@ import java.util.List; | |||||||
| 
 | 
 | ||||||
| import io.reactivex.Flowable; | import io.reactivex.Flowable; | ||||||
| import io.reactivex.Maybe; | import io.reactivex.Maybe; | ||||||
|  | import io.reactivex.Scheduler; | ||||||
| import io.reactivex.Single; | import io.reactivex.Single; | ||||||
| import io.reactivex.schedulers.Schedulers; | import io.reactivex.schedulers.Schedulers; | ||||||
| 
 | 
 | ||||||
| @@ -80,6 +99,11 @@ public class HistoryRecordManager { | |||||||
|                 .subscribeOn(Schedulers.io()); |                 .subscribeOn(Schedulers.io()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public Single<Integer> deleteWholeStreamHistory() { | ||||||
|  |         return Single.fromCallable(() -> streamHistoryTable.deleteAll()) | ||||||
|  |                 .subscribeOn(Schedulers.io()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public Flowable<List<StreamHistoryEntry>> getStreamHistory() { |     public Flowable<List<StreamHistoryEntry>> getStreamHistory() { | ||||||
|         return streamHistoryTable.getHistory().subscribeOn(Schedulers.io()); |         return streamHistoryTable.getHistory().subscribeOn(Schedulers.io()); | ||||||
|     } |     } | ||||||
| @@ -114,20 +138,6 @@ public class HistoryRecordManager { | |||||||
|     // Search History |     // Search History | ||||||
|     /////////////////////////////////////////////////////// |     /////////////////////////////////////////////////////// | ||||||
| 
 | 
 | ||||||
|     public Single<List<Long>> insertSearches(final Collection<SearchHistoryEntry> entries) { |  | ||||||
|         return Single.fromCallable(() -> searchHistoryTable.insertAll(entries)) |  | ||||||
|                 .subscribeOn(Schedulers.io()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public Single<Integer> deleteSearches(final Collection<SearchHistoryEntry> entries) { |  | ||||||
|         return Single.fromCallable(() -> searchHistoryTable.delete(entries)) |  | ||||||
|                 .subscribeOn(Schedulers.io()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public Flowable<List<SearchHistoryEntry>> getSearchHistory() { |  | ||||||
|         return searchHistoryTable.getAll(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public Maybe<Long> onSearched(final int serviceId, final String search) { |     public Maybe<Long> onSearched(final int serviceId, final String search) { | ||||||
|         if (!isSearchHistoryEnabled()) return Maybe.empty(); |         if (!isSearchHistoryEnabled()) return Maybe.empty(); | ||||||
| 
 | 
 | ||||||
| @@ -150,6 +160,11 @@ public class HistoryRecordManager { | |||||||
|                 .subscribeOn(Schedulers.io()); |                 .subscribeOn(Schedulers.io()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public Single<Integer> deleteWholeSearchHistory() { | ||||||
|  |         return Single.fromCallable(() -> searchHistoryTable.deleteAll()) | ||||||
|  |                 .subscribeOn(Schedulers.io()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public Flowable<List<SearchHistoryEntry>> getRelatedSearches(final String query, |     public Flowable<List<SearchHistoryEntry>> getRelatedSearches(final String query, | ||||||
|                                                                  final int similarQueryLimit, |                                                                  final int similarQueryLimit, | ||||||
|                                                                  final int uniqueQueryLimit) { |                                                                  final int uniqueQueryLimit) { | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.bookmark; | package org.schabi.newpipe.local.history; | ||||||
| 
 | 
 | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| @@ -7,9 +7,13 @@ import android.os.Bundle; | |||||||
| import android.os.Parcelable; | import android.os.Parcelable; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
|  | import android.support.design.widget.Snackbar; | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
|  | import android.widget.ImageView; | ||||||
|  | import android.widget.TextView; | ||||||
|  | import android.widget.Toast; | ||||||
| 
 | 
 | ||||||
| import org.reactivestreams.Subscriber; | import org.reactivestreams.Subscriber; | ||||||
| import org.reactivestreams.Subscription; | import org.reactivestreams.Subscription; | ||||||
| @@ -17,13 +21,14 @@ import org.schabi.newpipe.R; | |||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.database.stream.StreamStatisticsEntry; | import org.schabi.newpipe.database.stream.StreamStatisticsEntry; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| import org.schabi.newpipe.history.HistoryRecordManager; | import org.schabi.newpipe.local.BaseLocalListFragment; | ||||||
| import org.schabi.newpipe.info_list.InfoItemDialog; | import org.schabi.newpipe.info_list.InfoItemDialog; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.playlist.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| import org.schabi.newpipe.util.OnClickGesture; | import org.schabi.newpipe.util.OnClickGesture; | ||||||
|  | import org.schabi.newpipe.util.ThemeHelper; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| @@ -31,13 +36,19 @@ import java.util.List; | |||||||
| 
 | 
 | ||||||
| import icepick.State; | import icepick.State; | ||||||
| import io.reactivex.android.schedulers.AndroidSchedulers; | import io.reactivex.android.schedulers.AndroidSchedulers; | ||||||
|  | import io.reactivex.disposables.CompositeDisposable; | ||||||
|  | import io.reactivex.disposables.Disposable; | ||||||
| 
 | 
 | ||||||
| public abstract class StatisticsPlaylistFragment | public class StatisticsPlaylistFragment | ||||||
|         extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void> { |         extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void> { | ||||||
| 
 | 
 | ||||||
|     private View headerPlayAllButton; |     private View headerPlayAllButton; | ||||||
|     private View headerPopupButton; |     private View headerPopupButton; | ||||||
|     private View headerBackgroundButton; |     private View headerBackgroundButton; | ||||||
|  |     private View playlistCtrl; | ||||||
|  |     private View sortButton; | ||||||
|  |     private ImageView sortButtonIcon; | ||||||
|  |     private TextView sortButtonText; | ||||||
| 
 | 
 | ||||||
|     @State |     @State | ||||||
|     protected Parcelable itemsListState; |     protected Parcelable itemsListState; | ||||||
| @@ -45,14 +56,28 @@ public abstract class StatisticsPlaylistFragment | |||||||
|     /* Used for independent events */ |     /* Used for independent events */ | ||||||
|     private Subscription databaseSubscription; |     private Subscription databaseSubscription; | ||||||
|     private HistoryRecordManager recordManager; |     private HistoryRecordManager recordManager; | ||||||
|  |     private CompositeDisposable disposables = new CompositeDisposable(); | ||||||
| 
 | 
 | ||||||
|     /////////////////////////////////////////////////////////////////////////// |     private enum StatisticSortMode { | ||||||
|     // Abstracts |         LAST_PLAYED, | ||||||
|     /////////////////////////////////////////////////////////////////////////// |         MOST_PLAYED, | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     protected abstract String getName(); |     StatisticSortMode sortMode = StatisticSortMode.LAST_PLAYED; | ||||||
| 
 | 
 | ||||||
|     protected abstract List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results); |     protected List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results) { | ||||||
|  |         switch (sortMode) { | ||||||
|  |             case LAST_PLAYED: | ||||||
|  |                 Collections.sort(results, (left, right) -> | ||||||
|  |                     right.latestAccessDate.compareTo(left.latestAccessDate)); | ||||||
|  |                 return results; | ||||||
|  |             case MOST_PLAYED: | ||||||
|  |                 Collections.sort(results, (left, right) -> | ||||||
|  |                         ((Long) right.watchCount).compareTo(left.watchCount)); | ||||||
|  |                 return results; | ||||||
|  |             default: return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /////////////////////////////////////////////////////////////////////////// |     /////////////////////////////////////////////////////////////////////////// | ||||||
|     // Fragment LifeCycle - Creation |     // Fragment LifeCycle - Creation | ||||||
| @@ -78,16 +103,20 @@ public abstract class StatisticsPlaylistFragment | |||||||
|     @Override |     @Override | ||||||
|     protected void initViews(View rootView, Bundle savedInstanceState) { |     protected void initViews(View rootView, Bundle savedInstanceState) { | ||||||
|         super.initViews(rootView, savedInstanceState); |         super.initViews(rootView, savedInstanceState); | ||||||
|         setTitle(getName()); |         setTitle(getString(R.string.title_last_played)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     protected View getListHeader() { |     protected View getListHeader() { | ||||||
|         final View headerRootLayout = activity.getLayoutInflater().inflate(R.layout.playlist_control, |         final View headerRootLayout = activity.getLayoutInflater().inflate(R.layout.statistic_playlist_control, | ||||||
|                 itemsList, false); |                 itemsList, false); | ||||||
|  |         playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control); | ||||||
|         headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button); |         headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button); | ||||||
|         headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button); |         headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button); | ||||||
|         headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button); |         headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button); | ||||||
|  |         sortButton = headerRootLayout.findViewById(R.id.sortButton); | ||||||
|  |         sortButtonIcon = headerRootLayout.findViewById(R.id.sortButtonIcon); | ||||||
|  |         sortButtonText = headerRootLayout.findViewById(R.id.sortButtonText); | ||||||
|         return headerRootLayout; |         return headerRootLayout; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -193,6 +222,8 @@ public abstract class StatisticsPlaylistFragment | |||||||
|         super.handleResult(result); |         super.handleResult(result); | ||||||
|         if (itemListAdapter == null) return; |         if (itemListAdapter == null) return; | ||||||
| 
 | 
 | ||||||
|  |         playlistCtrl.setVisibility(View.VISIBLE); | ||||||
|  | 
 | ||||||
|         itemListAdapter.clearStreamItemList(); |         itemListAdapter.clearStreamItemList(); | ||||||
| 
 | 
 | ||||||
|         if (result.isEmpty()) { |         if (result.isEmpty()) { | ||||||
| @@ -212,6 +243,7 @@ public abstract class StatisticsPlaylistFragment | |||||||
|                 NavigationHelper.playOnPopupPlayer(activity, getPlayQueue())); |                 NavigationHelper.playOnPopupPlayer(activity, getPlayQueue())); | ||||||
|         headerBackgroundButton.setOnClickListener(view -> |         headerBackgroundButton.setOnClickListener(view -> | ||||||
|                 NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue())); |                 NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue())); | ||||||
|  |         sortButton.setOnClickListener(view -> toggleSortMode()); | ||||||
| 
 | 
 | ||||||
|         hideLoading(); |         hideLoading(); | ||||||
|     } |     } | ||||||
| @@ -238,6 +270,21 @@ public abstract class StatisticsPlaylistFragment | |||||||
|     // Utils |     // Utils | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
| 
 | 
 | ||||||
|  |     private void toggleSortMode() { | ||||||
|  |         if(sortMode == StatisticSortMode.LAST_PLAYED) { | ||||||
|  |             sortMode = StatisticSortMode.MOST_PLAYED; | ||||||
|  |             setTitle(getString(R.string.title_most_played)); | ||||||
|  |             sortButtonIcon.setImageResource(ThemeHelper.getIconByAttr(R.attr.history, getContext())); | ||||||
|  |             sortButtonText.setText(R.string.title_last_played); | ||||||
|  |         } else { | ||||||
|  |             sortMode = StatisticSortMode.LAST_PLAYED; | ||||||
|  |             setTitle(getString(R.string.title_last_played)); | ||||||
|  |             sortButtonIcon.setImageResource(ThemeHelper.getIconByAttr(R.attr.filter, getContext())); | ||||||
|  |             sortButtonText.setText(R.string.title_most_played); | ||||||
|  |         } | ||||||
|  |         startLoading(true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private void showStreamDialog(final StreamStatisticsEntry item) { |     private void showStreamDialog(final StreamStatisticsEntry item) { | ||||||
|         final Context context = getContext(); |         final Context context = getContext(); | ||||||
|         final Activity activity = getActivity(); |         final Activity activity = getActivity(); | ||||||
| @@ -250,6 +297,7 @@ public abstract class StatisticsPlaylistFragment | |||||||
|                 context.getResources().getString(R.string.start_here_on_main), |                 context.getResources().getString(R.string.start_here_on_main), | ||||||
|                 context.getResources().getString(R.string.start_here_on_background), |                 context.getResources().getString(R.string.start_here_on_background), | ||||||
|                 context.getResources().getString(R.string.start_here_on_popup), |                 context.getResources().getString(R.string.start_here_on_popup), | ||||||
|  |                 context.getResources().getString(R.string.delete), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         final DialogInterface.OnClickListener actions = (dialogInterface, i) -> { |         final DialogInterface.OnClickListener actions = (dialogInterface, i) -> { | ||||||
| @@ -270,6 +318,9 @@ public abstract class StatisticsPlaylistFragment | |||||||
|                 case 4: |                 case 4: | ||||||
|                     NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index)); |                     NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index)); | ||||||
|                     break; |                     break; | ||||||
|  |                 case 5: | ||||||
|  |                     deleteEntry(index); | ||||||
|  |                     break; | ||||||
|                 default: |                 default: | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
| @@ -278,6 +329,32 @@ public abstract class StatisticsPlaylistFragment | |||||||
|         new InfoItemDialog(getActivity(), infoItem, commands, actions).show(); |         new InfoItemDialog(getActivity(), infoItem, commands, actions).show(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private void deleteEntry(final int index) { | ||||||
|  |         final LocalItem infoItem = itemListAdapter.getItemsList() | ||||||
|  |                 .get(index); | ||||||
|  |         if(infoItem instanceof StreamStatisticsEntry) { | ||||||
|  |             final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem; | ||||||
|  |             final Disposable onDelete = recordManager.deleteStreamHistory(entry.streamId) | ||||||
|  |                     .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                     .subscribe( | ||||||
|  |                             howManyDelted -> { | ||||||
|  |                                 if(getView() != null) { | ||||||
|  |                                     Snackbar.make(getView(), R.string.one_item_deleted, | ||||||
|  |                                             Snackbar.LENGTH_SHORT).show(); | ||||||
|  |                                 } else { | ||||||
|  |                                     Toast.makeText(getContext(), | ||||||
|  |                                             R.string.one_item_deleted, | ||||||
|  |                                             Toast.LENGTH_SHORT).show(); | ||||||
|  |                                 } | ||||||
|  |                             }, | ||||||
|  |                             throwable -> showSnackBarError(throwable, | ||||||
|  |                                     UserAction.DELETE_FROM_HISTORY, "none", | ||||||
|  |                                     "Deleting item failed", R.string.general_error)); | ||||||
|  | 
 | ||||||
|  |             disposables.add(onDelete); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private PlayQueue getPlayQueue() { |     private PlayQueue getPlayQueue() { | ||||||
|         return getPlayQueue(0); |         return getPlayQueue(0); | ||||||
|     } |     } | ||||||
| @@ -1,11 +1,11 @@ | |||||||
| package org.schabi.newpipe.fragments.local.holder; | package org.schabi.newpipe.local.holder; | ||||||
| 
 | 
 | ||||||
| import android.support.v7.widget.RecyclerView; | import android.support.v7.widget.RecyclerView; | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.fragments.local.LocalItemBuilder; | import org.schabi.newpipe.local.LocalItemBuilder; | ||||||
| 
 | 
 | ||||||
| import java.text.DateFormat; | import java.text.DateFormat; | ||||||
| 
 | 
 | ||||||
| @@ -1,11 +1,11 @@ | |||||||
| package org.schabi.newpipe.fragments.local.holder; | package org.schabi.newpipe.local.holder; | ||||||
| 
 | 
 | ||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; | ||||||
| import org.schabi.newpipe.fragments.local.LocalItemBuilder; | import org.schabi.newpipe.local.LocalItemBuilder; | ||||||
| import org.schabi.newpipe.util.ImageDisplayConstants; | import org.schabi.newpipe.util.ImageDisplayConstants; | ||||||
| 
 | 
 | ||||||
| import java.text.DateFormat; | import java.text.DateFormat; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.holder; | package org.schabi.newpipe.local.holder; | ||||||
| 
 | 
 | ||||||
| import android.support.v4.content.ContextCompat; | import android.support.v4.content.ContextCompat; | ||||||
| import android.view.MotionEvent; | import android.view.MotionEvent; | ||||||
| @@ -11,7 +11,7 @@ import org.schabi.newpipe.R; | |||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; | import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; | ||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
| import org.schabi.newpipe.fragments.local.LocalItemBuilder; | import org.schabi.newpipe.local.LocalItemBuilder; | ||||||
| import org.schabi.newpipe.util.ImageDisplayConstants; | import org.schabi.newpipe.util.ImageDisplayConstants; | ||||||
| import org.schabi.newpipe.util.Localization; | import org.schabi.newpipe.util.Localization; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.holder; | package org.schabi.newpipe.local.holder; | ||||||
| 
 | 
 | ||||||
| import android.support.v4.content.ContextCompat; | import android.support.v4.content.ContextCompat; | ||||||
| import android.view.View; | import android.view.View; | ||||||
| @@ -10,7 +10,7 @@ import org.schabi.newpipe.R; | |||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.database.stream.StreamStatisticsEntry; | import org.schabi.newpipe.database.stream.StreamStatisticsEntry; | ||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
| import org.schabi.newpipe.fragments.local.LocalItemBuilder; | import org.schabi.newpipe.local.LocalItemBuilder; | ||||||
| import org.schabi.newpipe.util.ImageDisplayConstants; | import org.schabi.newpipe.util.ImageDisplayConstants; | ||||||
| import org.schabi.newpipe.util.Localization; | import org.schabi.newpipe.util.Localization; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.holder; | package org.schabi.newpipe.local.holder; | ||||||
| 
 | 
 | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.ImageView; | import android.widget.ImageView; | ||||||
| @@ -6,7 +6,7 @@ import android.widget.TextView; | |||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.fragments.local.LocalItemBuilder; | import org.schabi.newpipe.local.LocalItemBuilder; | ||||||
| 
 | 
 | ||||||
| import java.text.DateFormat; | import java.text.DateFormat; | ||||||
| 
 | 
 | ||||||
| @@ -1,11 +1,11 @@ | |||||||
| package org.schabi.newpipe.fragments.local.holder; | package org.schabi.newpipe.local.holder; | ||||||
| 
 | 
 | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; | import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; | ||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
| import org.schabi.newpipe.fragments.local.LocalItemBuilder; | import org.schabi.newpipe.local.LocalItemBuilder; | ||||||
| import org.schabi.newpipe.util.ImageDisplayConstants; | import org.schabi.newpipe.util.ImageDisplayConstants; | ||||||
| import org.schabi.newpipe.util.Localization; | import org.schabi.newpipe.util.Localization; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local.bookmark; | package org.schabi.newpipe.local.playlist; | ||||||
| 
 | 
 | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| @@ -26,10 +26,10 @@ import org.schabi.newpipe.R; | |||||||
| import org.schabi.newpipe.database.LocalItem; | import org.schabi.newpipe.database.LocalItem; | ||||||
| import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; | import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| import org.schabi.newpipe.fragments.local.LocalPlaylistManager; | import org.schabi.newpipe.local.BaseLocalListFragment; | ||||||
| import org.schabi.newpipe.info_list.InfoItemDialog; | import org.schabi.newpipe.info_list.InfoItemDialog; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.playlist.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.Localization; | import org.schabi.newpipe.util.Localization; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| @@ -173,7 +173,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | |||||||
|             @Override |             @Override | ||||||
|             public void held(LocalItem selectedItem) { |             public void held(LocalItem selectedItem) { | ||||||
|                 if (selectedItem instanceof PlaylistStreamEntry) { |                 if (selectedItem instanceof PlaylistStreamEntry) { | ||||||
|                     showStreamDialog((PlaylistStreamEntry) selectedItem); |                     showStreamItemDialog((PlaylistStreamEntry) selectedItem); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @@ -506,7 +506,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | |||||||
|     // Utils |     // Utils | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
| 
 | 
 | ||||||
|     protected void showStreamDialog(final PlaylistStreamEntry item) { |     protected void showStreamItemDialog(final PlaylistStreamEntry item) { | ||||||
|         final Context context = getContext(); |         final Context context = getContext(); | ||||||
|         final Activity activity = getActivity(); |         final Activity activity = getActivity(); | ||||||
|         if (context == null || context.getResources() == null || getActivity() == null) return; |         if (context == null || context.getResources() == null || getActivity() == null) return; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local; | package org.schabi.newpipe.local.playlist; | ||||||
| 
 | 
 | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.local; | package org.schabi.newpipe.local.playlist; | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.database.AppDatabase; | import org.schabi.newpipe.database.AppDatabase; | ||||||
| import org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO; | import org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO; | ||||||
| @@ -13,11 +13,9 @@ import io.reactivex.schedulers.Schedulers; | |||||||
| 
 | 
 | ||||||
| public class RemotePlaylistManager { | public class RemotePlaylistManager { | ||||||
| 
 | 
 | ||||||
|     private final AppDatabase database; |  | ||||||
|     private final PlaylistRemoteDAO playlistRemoteTable; |     private final PlaylistRemoteDAO playlistRemoteTable; | ||||||
| 
 | 
 | ||||||
|     public RemotePlaylistManager(final AppDatabase db) { |     public RemotePlaylistManager(final AppDatabase db) { | ||||||
|         database = db; |  | ||||||
|         playlistRemoteTable = db.playlistRemoteDAO(); |         playlistRemoteTable = db.playlistRemoteDAO(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.subscription; | package org.schabi.newpipe.local.subscription; | ||||||
| 
 | 
 | ||||||
| import android.app.AlertDialog; | import android.app.AlertDialog; | ||||||
| import android.app.Dialog; | import android.app.Dialog; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.subscription; | package org.schabi.newpipe.local.subscription; | ||||||
| 
 | 
 | ||||||
| public interface ImportExportEventListener { | public interface ImportExportEventListener { | ||||||
|     /** |     /** | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|  * along with this program. If not, see <http://www.gnu.org/licenses/>. |  * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package org.schabi.newpipe.subscription; | package org.schabi.newpipe.local.subscription; | ||||||
| 
 | 
 | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.subscription; | package org.schabi.newpipe.local.subscription; | ||||||
| 
 | 
 | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.content.BroadcastReceiver; | import android.content.BroadcastReceiver; | ||||||
| @@ -39,9 +39,8 @@ import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; | |||||||
| import org.schabi.newpipe.fragments.BaseStateFragment; | import org.schabi.newpipe.fragments.BaseStateFragment; | ||||||
| import org.schabi.newpipe.info_list.InfoListAdapter; | import org.schabi.newpipe.info_list.InfoListAdapter; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.subscription.SubscriptionService; | import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService; | ||||||
| import org.schabi.newpipe.subscription.services.SubscriptionsExportService; | import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService; | ||||||
| import org.schabi.newpipe.subscription.services.SubscriptionsImportService; |  | ||||||
| import org.schabi.newpipe.util.FilePickerActivityHelper; | import org.schabi.newpipe.util.FilePickerActivityHelper; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| import org.schabi.newpipe.util.OnClickGesture; | import org.schabi.newpipe.util.OnClickGesture; | ||||||
| @@ -53,7 +52,6 @@ import java.io.File; | |||||||
| import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.Comparator; |  | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Locale; | import java.util.Locale; | ||||||
| @@ -65,9 +63,9 @@ import io.reactivex.disposables.CompositeDisposable; | |||||||
| import io.reactivex.disposables.Disposable; | import io.reactivex.disposables.Disposable; | ||||||
| import io.reactivex.schedulers.Schedulers; | import io.reactivex.schedulers.Schedulers; | ||||||
| 
 | 
 | ||||||
| import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_MODE; | import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE; | ||||||
| import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_VALUE; | import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE; | ||||||
| import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE; | import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE; | ||||||
| import static org.schabi.newpipe.util.AnimationUtils.animateRotation; | import static org.schabi.newpipe.util.AnimationUtils.animateRotation; | ||||||
| import static org.schabi.newpipe.util.AnimationUtils.animateView; | import static org.schabi.newpipe.util.AnimationUtils.animateView; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.subscription; | package org.schabi.newpipe.local.subscription; | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.fragments.subscription; | package org.schabi.newpipe.local.subscription; | ||||||
| 
 | 
 | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| @@ -26,7 +26,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; | |||||||
| import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; | import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; | ||||||
| import org.schabi.newpipe.report.ErrorActivity; | import org.schabi.newpipe.report.ErrorActivity; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.subscription.services.SubscriptionsImportService; | import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService; | ||||||
| import org.schabi.newpipe.util.Constants; | import org.schabi.newpipe.util.Constants; | ||||||
| import org.schabi.newpipe.util.FilePickerActivityHelper; | import org.schabi.newpipe.util.FilePickerActivityHelper; | ||||||
| import org.schabi.newpipe.util.ServiceHelper; | import org.schabi.newpipe.util.ServiceHelper; | ||||||
| @@ -37,10 +37,10 @@ import java.util.List; | |||||||
| import icepick.State; | import icepick.State; | ||||||
| 
 | 
 | ||||||
| import static org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.ContentSource.CHANNEL_URL; | import static org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.ContentSource.CHANNEL_URL; | ||||||
| import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE; | import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE; | ||||||
| import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE; | import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE; | ||||||
| import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_MODE; | import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE; | ||||||
| import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_VALUE; | import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE; | ||||||
| 
 | 
 | ||||||
| public class SubscriptionsImportFragment extends BaseFragment { | public class SubscriptionsImportFragment extends BaseFragment { | ||||||
|     private static final int REQUEST_IMPORT_FILE_CODE = 666; |     private static final int REQUEST_IMPORT_FILE_CODE = 666; | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|  * along with this program. If not, see <http://www.gnu.org/licenses/>. |  * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package org.schabi.newpipe.subscription.services; | package org.schabi.newpipe.local.subscription.services; | ||||||
| 
 | 
 | ||||||
| import android.app.Service; | import android.app.Service; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| @@ -36,8 +36,8 @@ import org.schabi.newpipe.R; | |||||||
| import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; | import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; | ||||||
| import org.schabi.newpipe.report.ErrorActivity; | import org.schabi.newpipe.report.ErrorActivity; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.subscription.ImportExportEventListener; | import org.schabi.newpipe.local.subscription.ImportExportEventListener; | ||||||
| import org.schabi.newpipe.subscription.SubscriptionService; | import org.schabi.newpipe.local.subscription.SubscriptionService; | ||||||
| 
 | 
 | ||||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|  * along with this program. If not, see <http://www.gnu.org/licenses/>. |  * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package org.schabi.newpipe.subscription.services; | package org.schabi.newpipe.local.subscription.services; | ||||||
| 
 | 
 | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.support.v4.content.LocalBroadcastManager; | import android.support.v4.content.LocalBroadcastManager; | ||||||
| @@ -29,7 +29,7 @@ import org.reactivestreams.Subscription; | |||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.database.subscription.SubscriptionEntity; | import org.schabi.newpipe.database.subscription.SubscriptionEntity; | ||||||
| import org.schabi.newpipe.extractor.subscription.SubscriptionItem; | import org.schabi.newpipe.extractor.subscription.SubscriptionItem; | ||||||
| import org.schabi.newpipe.subscription.ImportExportJsonHelper; | import org.schabi.newpipe.local.subscription.ImportExportJsonHelper; | ||||||
| 
 | 
 | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||||
| @@ -49,7 +49,7 @@ public class SubscriptionsExportService extends BaseImportExportService { | |||||||
|     /** |     /** | ||||||
|      * A {@link LocalBroadcastManager local broadcast} will be made with this action when the export is successfully completed. |      * A {@link LocalBroadcastManager local broadcast} will be made with this action when the export is successfully completed. | ||||||
|      */ |      */ | ||||||
|     public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipe.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE"; |     public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE"; | ||||||
| 
 | 
 | ||||||
|     private Subscription subscription; |     private Subscription subscription; | ||||||
|     private File outFile; |     private File outFile; | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|  * along with this program. If not, see <http://www.gnu.org/licenses/>. |  * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package org.schabi.newpipe.subscription.services; | package org.schabi.newpipe.local.subscription.services; | ||||||
| 
 | 
 | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| @@ -33,7 +33,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity; | |||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
| import org.schabi.newpipe.extractor.channel.ChannelInfo; | import org.schabi.newpipe.extractor.channel.ChannelInfo; | ||||||
| import org.schabi.newpipe.extractor.subscription.SubscriptionItem; | import org.schabi.newpipe.extractor.subscription.SubscriptionItem; | ||||||
| import org.schabi.newpipe.subscription.ImportExportJsonHelper; | import org.schabi.newpipe.local.subscription.ImportExportJsonHelper; | ||||||
| import org.schabi.newpipe.util.Constants; | import org.schabi.newpipe.util.Constants; | ||||||
| import org.schabi.newpipe.util.ExtractorHelper; | import org.schabi.newpipe.util.ExtractorHelper; | ||||||
| 
 | 
 | ||||||
| @@ -64,7 +64,7 @@ public class SubscriptionsImportService extends BaseImportExportService { | |||||||
|     /** |     /** | ||||||
|      * A {@link LocalBroadcastManager local broadcast} will be made with this action when the import is successfully completed. |      * A {@link LocalBroadcastManager local broadcast} will be made with this action when the import is successfully completed. | ||||||
|      */ |      */ | ||||||
|     public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.subscription.services.SubscriptionsImportService.IMPORT_COMPLETE"; |     public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.IMPORT_COMPLETE"; | ||||||
| 
 | 
 | ||||||
|     private Subscription subscription; |     private Subscription subscription; | ||||||
|     private int currentMode; |     private int currentMode; | ||||||
| @@ -48,7 +48,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; | |||||||
| import org.schabi.newpipe.player.event.PlayerEventListener; | import org.schabi.newpipe.player.event.PlayerEventListener; | ||||||
| import org.schabi.newpipe.player.helper.LockManager; | import org.schabi.newpipe.player.helper.LockManager; | ||||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | import org.schabi.newpipe.player.helper.PlayerHelper; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.util.ListHelper; | import org.schabi.newpipe.util.ListHelper; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| import org.schabi.newpipe.util.ThemeHelper; | import org.schabi.newpipe.util.ThemeHelper; | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ import org.schabi.newpipe.Downloader; | |||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamType; | import org.schabi.newpipe.extractor.stream.StreamType; | ||||||
| import org.schabi.newpipe.history.HistoryRecordManager; | import org.schabi.newpipe.local.history.HistoryRecordManager; | ||||||
| import org.schabi.newpipe.player.helper.AudioReactor; | import org.schabi.newpipe.player.helper.AudioReactor; | ||||||
| import org.schabi.newpipe.player.helper.LoadController; | import org.schabi.newpipe.player.helper.LoadController; | ||||||
| import org.schabi.newpipe.player.helper.MediaSessionManager; | import org.schabi.newpipe.player.helper.MediaSessionManager; | ||||||
| @@ -69,9 +69,9 @@ import org.schabi.newpipe.player.playback.BasePlayerMediaSession; | |||||||
| import org.schabi.newpipe.player.playback.CustomTrackSelector; | import org.schabi.newpipe.player.playback.CustomTrackSelector; | ||||||
| import org.schabi.newpipe.player.playback.MediaSourceManager; | import org.schabi.newpipe.player.playback.MediaSourceManager; | ||||||
| import org.schabi.newpipe.player.playback.PlaybackListener; | import org.schabi.newpipe.player.playback.PlaybackListener; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueAdapter; | import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.util.SerializedCache; | import org.schabi.newpipe.util.SerializedCache; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|   | |||||||
| @@ -62,10 +62,10 @@ import org.schabi.newpipe.extractor.stream.VideoStream; | |||||||
| import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | ||||||
| import org.schabi.newpipe.player.helper.PlaybackParameterDialog; | import org.schabi.newpipe.player.helper.PlaybackParameterDialog; | ||||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | import org.schabi.newpipe.player.helper.PlayerHelper; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItemBuilder; | import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItemHolder; | import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItemTouchCallback; | import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; | ||||||
| import org.schabi.newpipe.util.AnimationUtils; | import org.schabi.newpipe.util.AnimationUtils; | ||||||
| import org.schabi.newpipe.util.ListHelper; | import org.schabi.newpipe.util.ListHelper; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| @@ -847,10 +847,12 @@ public final class MainVideoPlayer extends AppCompatActivity | |||||||
|             if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); |             if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); | ||||||
|             if (!playerImpl.isPlaying()) return false; |             if (!playerImpl.isPlaying()) return false; | ||||||
|  |  | ||||||
|             if (e.getX() > playerImpl.getRootView().getWidth() / 2) { |             if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) { | ||||||
|                 playerImpl.onFastForward(); |                 playerImpl.onFastForward(); | ||||||
|             } else { |             } else if (e.getX() < playerImpl.getRootView().getWidth() / 3) { | ||||||
|                 playerImpl.onFastRewind(); |                 playerImpl.onFastRewind(); | ||||||
|  |             } else { | ||||||
|  |                 playerImpl.getPlayPauseButton().performClick(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return true; |             return true; | ||||||
|   | |||||||
| @@ -2,17 +2,12 @@ package org.schabi.newpipe.player; | |||||||
|  |  | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| import android.util.Log; |  | ||||||
|  |  | ||||||
| import com.google.gson.Gson; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import com.google.gson.JsonSyntaxException; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; |  | ||||||
|  |  | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
|  |  | ||||||
| public class PlayerState implements Serializable { | public class PlayerState implements Serializable { | ||||||
|     private final static String TAG = "PlayerState"; |  | ||||||
|  |  | ||||||
|     @NonNull private final PlayQueue playQueue; |     @NonNull private final PlayQueue playQueue; | ||||||
|     private final int repeatMode; |     private final int repeatMode; | ||||||
| @@ -41,21 +36,6 @@ public class PlayerState implements Serializable { | |||||||
|     // Serdes |     // Serdes | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     @Nullable |  | ||||||
|     public static PlayerState fromJson(@NonNull final String json) { |  | ||||||
|         try { |  | ||||||
|             return new Gson().fromJson(json, PlayerState.class); |  | ||||||
|         } catch (JsonSyntaxException error) { |  | ||||||
|             Log.e(TAG, "Failed to deserialize PlayerState from json=[" + json + "]", error); |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     public String toJson() { |  | ||||||
|         return new Gson().toJson(this); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|     // Getters |     // Getters | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|   | |||||||
| @@ -64,7 +64,7 @@ import org.schabi.newpipe.player.event.PlayerEventListener; | |||||||
| import org.schabi.newpipe.player.helper.LockManager; | import org.schabi.newpipe.player.helper.LockManager; | ||||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | import org.schabi.newpipe.player.helper.PlayerHelper; | ||||||
| import org.schabi.newpipe.player.old.PlayVideoActivity; | import org.schabi.newpipe.player.old.PlayVideoActivity; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.util.ListHelper; | import org.schabi.newpipe.util.ListHelper; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| import org.schabi.newpipe.util.ThemeHelper; | import org.schabi.newpipe.util.ThemeHelper; | ||||||
|   | |||||||
| @@ -29,14 +29,14 @@ import com.google.android.exoplayer2.Player; | |||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | ||||||
| import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog; | import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; | ||||||
| import org.schabi.newpipe.player.event.PlayerEventListener; | import org.schabi.newpipe.player.event.PlayerEventListener; | ||||||
| import org.schabi.newpipe.player.helper.PlaybackParameterDialog; | import org.schabi.newpipe.player.helper.PlaybackParameterDialog; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueAdapter; | import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItemBuilder; | import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItemHolder; | import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItemTouchCallback; | import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; | ||||||
| import org.schabi.newpipe.util.Localization; | import org.schabi.newpipe.util.Localization; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
| import org.schabi.newpipe.util.ThemeHelper; | import org.schabi.newpipe.util.ThemeHelper; | ||||||
|   | |||||||
| @@ -68,7 +68,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; | |||||||
| import org.schabi.newpipe.extractor.stream.StreamType; | import org.schabi.newpipe.extractor.stream.StreamType; | ||||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | import org.schabi.newpipe.extractor.stream.VideoStream; | ||||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | import org.schabi.newpipe.player.helper.PlayerHelper; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.util.AnimationUtils; | import org.schabi.newpipe.util.AnimationUtils; | ||||||
| import org.schabi.newpipe.util.ListHelper; | import org.schabi.newpipe.util.ListHelper; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,9 +24,9 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; | |||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| import org.schabi.newpipe.extractor.stream.SubtitlesFormat; | import org.schabi.newpipe.extractor.stream.SubtitlesFormat; | ||||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | import org.schabi.newpipe.extractor.stream.VideoStream; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.playlist.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
|  |  | ||||||
| import java.text.DecimalFormat; | import java.text.DecimalFormat; | ||||||
| import java.text.NumberFormat; | import java.text.NumberFormat; | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import com.google.android.exoplayer2.ExoPlayer; | |||||||
| import com.google.android.exoplayer2.source.MediaPeriod; | import com.google.android.exoplayer2.source.MediaPeriod; | ||||||
| import com.google.android.exoplayer2.upstream.Allocator; | import com.google.android.exoplayer2.upstream.Allocator; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import com.google.android.exoplayer2.source.MediaPeriod; | |||||||
| import com.google.android.exoplayer2.source.MediaSource; | import com.google.android.exoplayer2.source.MediaSource; | ||||||
| import com.google.android.exoplayer2.upstream.Allocator; | import com.google.android.exoplayer2.upstream.Allocator; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ import android.support.annotation.NonNull; | |||||||
|  |  | ||||||
| import com.google.android.exoplayer2.source.MediaSource; | import com.google.android.exoplayer2.source.MediaSource; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
|  |  | ||||||
| public interface ManagedMediaSource extends MediaSource { | public interface ManagedMediaSource extends MediaSource { | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ import com.google.android.exoplayer2.ExoPlayer; | |||||||
| import com.google.android.exoplayer2.source.MediaPeriod; | import com.google.android.exoplayer2.source.MediaPeriod; | ||||||
| import com.google.android.exoplayer2.upstream.Allocator; | import com.google.android.exoplayer2.upstream.Allocator; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ import android.support.v4.media.MediaDescriptionCompat; | |||||||
|  |  | ||||||
| import org.schabi.newpipe.player.BasePlayer; | import org.schabi.newpipe.player.BasePlayer; | ||||||
| import org.schabi.newpipe.player.mediasession.MediaSessionCallback; | import org.schabi.newpipe.player.mediasession.MediaSessionCallback; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
|  |  | ||||||
| public class BasePlayerMediaSession implements MediaSessionCallback { | public class BasePlayerMediaSession implements MediaSessionCallback { | ||||||
|     private BasePlayer player; |     private BasePlayer player; | ||||||
|   | |||||||
| @@ -16,12 +16,12 @@ import org.schabi.newpipe.player.mediasource.LoadedMediaSource; | |||||||
| import org.schabi.newpipe.player.mediasource.ManagedMediaSource; | import org.schabi.newpipe.player.mediasource.ManagedMediaSource; | ||||||
| import org.schabi.newpipe.player.mediasource.ManagedMediaSourcePlaylist; | import org.schabi.newpipe.player.mediasource.ManagedMediaSourcePlaylist; | ||||||
| import org.schabi.newpipe.player.mediasource.PlaceholderMediaSource; | import org.schabi.newpipe.player.mediasource.PlaceholderMediaSource; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.playlist.events.MoveEvent; | import org.schabi.newpipe.player.playqueue.events.MoveEvent; | ||||||
| import org.schabi.newpipe.playlist.events.PlayQueueEvent; | import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent; | ||||||
| import org.schabi.newpipe.playlist.events.RemoveEvent; | import org.schabi.newpipe.player.playqueue.events.RemoveEvent; | ||||||
| import org.schabi.newpipe.playlist.events.ReorderEvent; | import org.schabi.newpipe.player.playqueue.events.ReorderEvent; | ||||||
| import org.schabi.newpipe.util.ServiceHelper; | import org.schabi.newpipe.util.ServiceHelper; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| @@ -45,7 +45,7 @@ import io.reactivex.subjects.PublishSubject; | |||||||
|  |  | ||||||
| import static org.schabi.newpipe.player.mediasource.FailedMediaSource.MediaSourceResolutionException; | import static org.schabi.newpipe.player.mediasource.FailedMediaSource.MediaSourceResolutionException; | ||||||
| import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfoLoadException; | import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfoLoadException; | ||||||
| import static org.schabi.newpipe.playlist.PlayQueue.DEBUG; | import static org.schabi.newpipe.player.playqueue.PlayQueue.DEBUG; | ||||||
|  |  | ||||||
| public class MediaSourceManager { | public class MediaSourceManager { | ||||||
|     @NonNull private final String TAG = "MediaSourceManager@" + hashCode(); |     @NonNull private final String TAG = "MediaSourceManager@" + hashCode(); | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ import android.support.annotation.Nullable; | |||||||
| import com.google.android.exoplayer2.source.MediaSource; | import com.google.android.exoplayer2.source.MediaSource; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
| 
 | 
 | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.extractor.InfoItem; |  | ||||||
| import org.schabi.newpipe.extractor.channel.ChannelInfo; | import org.schabi.newpipe.extractor.channel.ChannelInfo; | ||||||
| import org.schabi.newpipe.extractor.channel.ChannelInfoItem; | import org.schabi.newpipe.extractor.channel.ChannelInfoItem; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| @@ -6,15 +6,15 @@ import android.util.Log; | |||||||
| 
 | 
 | ||||||
| import org.reactivestreams.Subscriber; | import org.reactivestreams.Subscriber; | ||||||
| import org.reactivestreams.Subscription; | import org.reactivestreams.Subscription; | ||||||
| import org.schabi.newpipe.playlist.events.AppendEvent; | import org.schabi.newpipe.player.playqueue.events.AppendEvent; | ||||||
| import org.schabi.newpipe.playlist.events.ErrorEvent; | import org.schabi.newpipe.player.playqueue.events.ErrorEvent; | ||||||
| import org.schabi.newpipe.playlist.events.InitEvent; | import org.schabi.newpipe.player.playqueue.events.InitEvent; | ||||||
| import org.schabi.newpipe.playlist.events.MoveEvent; | import org.schabi.newpipe.player.playqueue.events.MoveEvent; | ||||||
| import org.schabi.newpipe.playlist.events.PlayQueueEvent; | import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent; | ||||||
| import org.schabi.newpipe.playlist.events.RecoveryEvent; | import org.schabi.newpipe.player.playqueue.events.RecoveryEvent; | ||||||
| import org.schabi.newpipe.playlist.events.RemoveEvent; | import org.schabi.newpipe.player.playqueue.events.RemoveEvent; | ||||||
| import org.schabi.newpipe.playlist.events.ReorderEvent; | import org.schabi.newpipe.player.playqueue.events.ReorderEvent; | ||||||
| import org.schabi.newpipe.playlist.events.SelectEvent; | import org.schabi.newpipe.player.playqueue.events.SelectEvent; | ||||||
| 
 | 
 | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.support.v7.widget.RecyclerView; | import android.support.v7.widget.RecyclerView; | ||||||
| @@ -8,12 +8,12 @@ import android.view.View; | |||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.playlist.events.AppendEvent; | import org.schabi.newpipe.player.playqueue.events.AppendEvent; | ||||||
| import org.schabi.newpipe.playlist.events.ErrorEvent; | import org.schabi.newpipe.player.playqueue.events.ErrorEvent; | ||||||
| import org.schabi.newpipe.playlist.events.MoveEvent; | import org.schabi.newpipe.player.playqueue.events.MoveEvent; | ||||||
| import org.schabi.newpipe.playlist.events.PlayQueueEvent; | import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent; | ||||||
| import org.schabi.newpipe.playlist.events.RemoveEvent; | import org.schabi.newpipe.player.playqueue.events.RemoveEvent; | ||||||
| import org.schabi.newpipe.playlist.events.SelectEvent; | import org.schabi.newpipe.player.playqueue.events.SelectEvent; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.text.TextUtils; | import android.text.TextUtils; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import android.support.v7.widget.RecyclerView; | import android.support.v7.widget.RecyclerView; | ||||||
| import android.view.View; | import android.view.View; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import android.support.v7.widget.RecyclerView; | import android.support.v7.widget.RecyclerView; | ||||||
| import android.support.v7.widget.helper.ItemTouchHelper; | import android.support.v7.widget.helper.ItemTouchHelper; | ||||||
| @@ -1,6 +1,5 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.extractor.InfoItem; |  | ||||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | ||||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; | import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist; | package org.schabi.newpipe.player.playqueue; | ||||||
| 
 | 
 | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| public class AppendEvent implements PlayQueueEvent { | public class AppendEvent implements PlayQueueEvent { | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| public class ErrorEvent implements PlayQueueEvent { | public class ErrorEvent implements PlayQueueEvent { | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| public class InitEvent implements PlayQueueEvent { | public class InitEvent implements PlayQueueEvent { | ||||||
|     @Override |     @Override | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| public class MoveEvent implements PlayQueueEvent { | public class MoveEvent implements PlayQueueEvent { | ||||||
|     final private int fromIndex; |     final private int fromIndex; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| public enum PlayQueueEventType { | public enum PlayQueueEventType { | ||||||
|     INIT, |     INIT, | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| public class RecoveryEvent implements PlayQueueEvent { | public class RecoveryEvent implements PlayQueueEvent { | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| public class RemoveEvent implements PlayQueueEvent { | public class RemoveEvent implements PlayQueueEvent { | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| public class ReorderEvent implements PlayQueueEvent { | public class ReorderEvent implements PlayQueueEvent { | ||||||
|     private final int fromSelectedIndex; |     private final int fromSelectedIndex; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package org.schabi.newpipe.playlist.events; | package org.schabi.newpipe.player.playqueue.events; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| public class SelectEvent implements PlayQueueEvent { | public class SelectEvent implements PlayQueueEvent { | ||||||
| @@ -14,7 +14,8 @@ public enum UserAction { | |||||||
|     REQUESTED_STREAM("requested stream"), |     REQUESTED_STREAM("requested stream"), | ||||||
|     REQUESTED_CHANNEL("requested channel"), |     REQUESTED_CHANNEL("requested channel"), | ||||||
|     REQUESTED_PLAYLIST("requested playlist"), |     REQUESTED_PLAYLIST("requested playlist"), | ||||||
|     REQUESTED_KIOSK("requested kiosk"); |     REQUESTED_KIOSK("requested kiosk"), | ||||||
|  |     DELETE_FROM_HISTORY("delete from history"); | ||||||
|  |  | ||||||
|  |  | ||||||
|     private final String message; |     private final String message; | ||||||
|   | |||||||
| @@ -1,20 +1,45 @@ | |||||||
| package org.schabi.newpipe.settings; | package org.schabi.newpipe.settings; | ||||||
|  |  | ||||||
|  | import android.content.DialogInterface; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
|  | import android.support.design.widget.Snackbar; | ||||||
|  | import android.support.v7.app.AlertDialog; | ||||||
| import android.support.v7.preference.Preference; | import android.support.v7.preference.Preference; | ||||||
|  | import android.util.Log; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.MainActivity; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.local.history.HistoryRecordManager; | ||||||
|  | import org.schabi.newpipe.report.ErrorActivity; | ||||||
|  | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.util.InfoCache; | import org.schabi.newpipe.util.InfoCache; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Collection; | ||||||
|  |  | ||||||
|  | import io.reactivex.Single; | ||||||
|  | import io.reactivex.android.schedulers.AndroidSchedulers; | ||||||
|  | import io.reactivex.disposables.CompositeDisposable; | ||||||
|  | import io.reactivex.disposables.Disposable; | ||||||
|  | import io.reactivex.disposables.Disposables; | ||||||
|  |  | ||||||
| public class HistorySettingsFragment extends BasePreferenceFragment { | public class HistorySettingsFragment extends BasePreferenceFragment { | ||||||
|     private String cacheWipeKey; |     private String cacheWipeKey; | ||||||
|  |     private String viewsHistroyClearKey; | ||||||
|  |     private String searchHistoryClearKey; | ||||||
|  |     private HistoryRecordManager recordManager; | ||||||
|  |     private CompositeDisposable disposables; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { |     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
|         cacheWipeKey = getString(R.string.metadata_cache_wipe_key); |         cacheWipeKey = getString(R.string.metadata_cache_wipe_key); | ||||||
|  |         viewsHistroyClearKey = getString(R.string.clear_views_history_key); | ||||||
|  |         searchHistoryClearKey = getString(R.string.clear_search_history_key); | ||||||
|  |         recordManager = new HistoryRecordManager(getActivity()); | ||||||
|  |         disposables = new CompositeDisposable(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -30,6 +55,70 @@ public class HistorySettingsFragment extends BasePreferenceFragment { | |||||||
|                     Toast.LENGTH_SHORT).show(); |                     Toast.LENGTH_SHORT).show(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (preference.getKey().equals(viewsHistroyClearKey)) { | ||||||
|  |             new AlertDialog.Builder(getActivity()) | ||||||
|  |                     .setTitle(R.string.delete_view_history_alert) | ||||||
|  |                     .setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss())) | ||||||
|  |                     .setPositiveButton(R.string.delete, ((dialog, which) -> { | ||||||
|  |                         final Disposable onDelete = recordManager.deleteWholeStreamHistory() | ||||||
|  |                                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                                 .subscribe( | ||||||
|  |                                         howManyDeleted -> Toast.makeText(getActivity(), | ||||||
|  |                                                 R.string.view_history_deleted, | ||||||
|  |                                                 Toast.LENGTH_SHORT).show(), | ||||||
|  |                                         throwable -> ErrorActivity.reportError(getContext(), | ||||||
|  |                                                 throwable, | ||||||
|  |                                                 SettingsActivity.class, null, | ||||||
|  |                                                 ErrorActivity.ErrorInfo.make( | ||||||
|  |                                                         UserAction.DELETE_FROM_HISTORY, | ||||||
|  |                                                         "none", | ||||||
|  |                                                         "Delete view history", | ||||||
|  |                                                         R.string.general_error))); | ||||||
|  |  | ||||||
|  |                         final Disposable onClearOrphans = recordManager.removeOrphanedRecords() | ||||||
|  |                                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                                 .subscribe( | ||||||
|  |                                         howManyDeleted -> {}, | ||||||
|  |                                         throwable -> ErrorActivity.reportError(getContext(), | ||||||
|  |                                                 throwable, | ||||||
|  |                                                 SettingsActivity.class, null, | ||||||
|  |                                                 ErrorActivity.ErrorInfo.make( | ||||||
|  |                                                         UserAction.DELETE_FROM_HISTORY, | ||||||
|  |                                                         "none", | ||||||
|  |                                                         "Delete search history", | ||||||
|  |                                                         R.string.general_error))); | ||||||
|  |                         disposables.add(onClearOrphans); | ||||||
|  |                         disposables.add(onDelete); | ||||||
|  |                     })) | ||||||
|  |                     .create() | ||||||
|  |                     .show(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (preference.getKey().equals(searchHistoryClearKey)) { | ||||||
|  |             new AlertDialog.Builder(getActivity()) | ||||||
|  |                     .setTitle(R.string.delete_search_history_alert) | ||||||
|  |                     .setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss())) | ||||||
|  |                     .setPositiveButton(R.string.delete, ((dialog, which) -> { | ||||||
|  |                         final Disposable onDelete = recordManager.deleteWholeSearchHistory() | ||||||
|  |                                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                                 .subscribe( | ||||||
|  |                                         howManyDeleted -> Toast.makeText(getActivity(), | ||||||
|  |                                                 R.string.search_history_deleted, | ||||||
|  |                                                 Toast.LENGTH_SHORT).show(), | ||||||
|  |                                         throwable -> ErrorActivity.reportError(getContext(), | ||||||
|  |                                                 throwable, | ||||||
|  |                                                 SettingsActivity.class, null, | ||||||
|  |                                                 ErrorActivity.ErrorInfo.make( | ||||||
|  |                                                         UserAction.DELETE_FROM_HISTORY, | ||||||
|  |                                                         "none", | ||||||
|  |                                                         "Delete search history", | ||||||
|  |                                                         R.string.general_error))); | ||||||
|  |                         disposables.add(onDelete); | ||||||
|  |                     })) | ||||||
|  |                     .create() | ||||||
|  |                     .show(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return super.onPreferenceTreeClick(preference); |         return super.onPreferenceTreeClick(preference); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ import org.schabi.newpipe.R; | |||||||
| import org.schabi.newpipe.database.subscription.SubscriptionEntity; | import org.schabi.newpipe.database.subscription.SubscriptionEntity; | ||||||
| import org.schabi.newpipe.report.ErrorActivity; | import org.schabi.newpipe.report.ErrorActivity; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
| import org.schabi.newpipe.subscription.SubscriptionService; | import org.schabi.newpipe.local.subscription.SubscriptionService; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Vector; | import java.util.Vector; | ||||||
|   | |||||||
| @@ -33,15 +33,13 @@ import org.schabi.newpipe.extractor.stream.VideoStream; | |||||||
| import org.schabi.newpipe.fragments.MainFragment; | import org.schabi.newpipe.fragments.MainFragment; | ||||||
| import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | ||||||
| import org.schabi.newpipe.fragments.list.channel.ChannelFragment; | import org.schabi.newpipe.fragments.list.channel.ChannelFragment; | ||||||
| import org.schabi.newpipe.fragments.list.feed.FeedFragment; | import org.schabi.newpipe.local.feed.FeedFragment; | ||||||
| import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; | import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; | ||||||
| import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; | import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; | ||||||
| import org.schabi.newpipe.fragments.list.search.SearchFragment; | import org.schabi.newpipe.fragments.list.search.SearchFragment; | ||||||
| import org.schabi.newpipe.fragments.local.bookmark.LastPlayedFragment; | import org.schabi.newpipe.local.history.StatisticsPlaylistFragment; | ||||||
| import org.schabi.newpipe.fragments.local.bookmark.LocalPlaylistFragment; | import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; | ||||||
| import org.schabi.newpipe.fragments.local.bookmark.MostPlayedFragment; | import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment; | ||||||
| import org.schabi.newpipe.fragments.subscription.SubscriptionsImportFragment; |  | ||||||
| import org.schabi.newpipe.history.HistoryActivity; |  | ||||||
| import org.schabi.newpipe.player.BackgroundPlayer; | import org.schabi.newpipe.player.BackgroundPlayer; | ||||||
| import org.schabi.newpipe.player.BackgroundPlayerActivity; | import org.schabi.newpipe.player.BackgroundPlayerActivity; | ||||||
| import org.schabi.newpipe.player.BasePlayer; | import org.schabi.newpipe.player.BasePlayer; | ||||||
| @@ -50,7 +48,7 @@ import org.schabi.newpipe.player.PopupVideoPlayer; | |||||||
| import org.schabi.newpipe.player.PopupVideoPlayerActivity; | import org.schabi.newpipe.player.PopupVideoPlayerActivity; | ||||||
| import org.schabi.newpipe.player.VideoPlayer; | import org.schabi.newpipe.player.VideoPlayer; | ||||||
| import org.schabi.newpipe.player.old.PlayVideoActivity; | import org.schabi.newpipe.player.old.PlayVideoActivity; | ||||||
| import org.schabi.newpipe.playlist.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.settings.SettingsActivity; | import org.schabi.newpipe.settings.SettingsActivity; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| @@ -352,16 +350,9 @@ public class NavigationHelper { | |||||||
|                 .commit(); |                 .commit(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void openLastPlayedFragment(FragmentManager fragmentManager) { |     public static void openStatisticFragment(FragmentManager fragmentManager) { | ||||||
|         defaultTransaction(fragmentManager) |         defaultTransaction(fragmentManager) | ||||||
|                 .replace(R.id.fragment_holder, new LastPlayedFragment()) |                 .replace(R.id.fragment_holder, new StatisticsPlaylistFragment()) | ||||||
|                 .addToBackStack(null) |  | ||||||
|                 .commit(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static void openMostPlayedFragment(FragmentManager fragmentManager) { |  | ||||||
|         defaultTransaction(fragmentManager) |  | ||||||
|                 .replace(R.id.fragment_holder, new MostPlayedFragment()) |  | ||||||
|                 .addToBackStack(null) |                 .addToBackStack(null) | ||||||
|                 .commit(); |                 .commit(); | ||||||
|     } |     } | ||||||
| @@ -417,11 +408,6 @@ public class NavigationHelper { | |||||||
|         context.startActivity(intent); |         context.startActivity(intent); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void openHistory(Context context) { |  | ||||||
|         Intent intent = new Intent(context, HistoryActivity.class); |  | ||||||
|         context.startActivity(intent); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static void openSettings(Context context) { |     public static void openSettings(Context context) { | ||||||
|         Intent intent = new Intent(context, SettingsActivity.class); |         Intent intent = new Intent(context, SettingsActivity.class); | ||||||
|         context.startActivity(intent); |         context.startActivity(intent); | ||||||
|   | |||||||
| @@ -190,4 +190,16 @@ public class ThemeHelper { | |||||||
|         String defaultTheme = context.getResources().getString(R.string.default_theme_value); |         String defaultTheme = context.getResources().getString(R.string.default_theme_value); | ||||||
|         return PreferenceManager.getDefaultSharedPreferences(context).getString(themeKey, defaultTheme); |         return PreferenceManager.getDefaultSharedPreferences(context).getString(themeKey, defaultTheme); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * This will get the R.drawable.* resource to which attr is currently pointing to. | ||||||
|  |      * | ||||||
|  |      * @param attr a R.attribute.* resource value | ||||||
|  |      * @param context the context to use | ||||||
|  |      * @return a R.drawable.* resource value | ||||||
|  |      */ | ||||||
|  |     public static int getIconByAttr(final int attr, final Context context) { | ||||||
|  |         return context.obtainStyledAttributes(new int[] {attr}) | ||||||
|  |                 .getResourceId(0, -1); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,8 +3,6 @@ package us.shandian.giga.get; | |||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
|  |  | ||||||
| import com.google.gson.Gson; |  | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.FilenameFilter; | import java.io.FilenameFilter; | ||||||
| import java.io.RandomAccessFile; | import java.io.RandomAccessFile; | ||||||
| @@ -156,16 +154,8 @@ public class DownloadManagerImpl implements DownloadManager { | |||||||
|  |  | ||||||
|             for (File sub : subs) { |             for (File sub : subs) { | ||||||
|                 if (sub.isFile() && sub.getName().endsWith(".giga")) { |                 if (sub.isFile() && sub.getName().endsWith(".giga")) { | ||||||
|                     String str = Utility.readFromFile(sub.getAbsolutePath()); |                     DownloadMission mis = Utility.readFromFile(sub.getAbsolutePath()); | ||||||
|                     if (str != null && !str.trim().equals("")) { |                     if (mis != null) { | ||||||
|  |  | ||||||
|                         if (DEBUG) { |  | ||||||
|                             Log.d(TAG, "loading mission " + sub.getName()); |  | ||||||
|                             Log.d(TAG, str); |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         DownloadMission mis = new Gson().fromJson(str, DownloadMission.class); |  | ||||||
|  |  | ||||||
|                         if (mis.finished) { |                         if (mis.finished) { | ||||||
|                             if (!sub.delete()) { |                             if (!sub.delete()) { | ||||||
|                                 Log.w(TAG, "Unable to delete .giga file: " + sub.getPath()); |                                 Log.w(TAG, "Unable to delete .giga file: " + sub.getPath()); | ||||||
|   | |||||||
| @@ -4,9 +4,8 @@ import android.os.Handler; | |||||||
| import android.os.Looper; | import android.os.Looper; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
|  |  | ||||||
| import com.google.gson.Gson; |  | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
|  | import java.io.Serializable; | ||||||
| import java.lang.ref.WeakReference; | import java.lang.ref.WeakReference; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| @@ -18,7 +17,9 @@ import us.shandian.giga.util.Utility; | |||||||
|  |  | ||||||
| import static org.schabi.newpipe.BuildConfig.DEBUG; | import static org.schabi.newpipe.BuildConfig.DEBUG; | ||||||
|  |  | ||||||
| public class DownloadMission { | public class DownloadMission implements Serializable { | ||||||
|  |     private static final long serialVersionUID = 0L; | ||||||
|  |  | ||||||
|     private static final String TAG = DownloadMission.class.getSimpleName(); |     private static final String TAG = DownloadMission.class.getSimpleName(); | ||||||
|  |  | ||||||
|     public interface MissionListener { |     public interface MissionListener { | ||||||
| @@ -79,7 +80,6 @@ public class DownloadMission { | |||||||
|     private transient boolean mWritingToFile; |     private transient boolean mWritingToFile; | ||||||
|  |  | ||||||
|     private static final int NO_IDENTIFIER = -1; |     private static final int NO_IDENTIFIER = -1; | ||||||
|     private long db_identifier = NO_IDENTIFIER; |  | ||||||
|  |  | ||||||
|     public DownloadMission() { |     public DownloadMission() { | ||||||
|     } |     } | ||||||
| @@ -308,7 +308,7 @@ public class DownloadMission { | |||||||
|      */ |      */ | ||||||
|     private void doWriteThisToFile() { |     private void doWriteThisToFile() { | ||||||
|         synchronized (blockState) { |         synchronized (blockState) { | ||||||
|             Utility.writeToFile(getMetaFilename(), new Gson().toJson(this)); |             Utility.writeToFile(getMetaFilename(), this); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import android.content.ClipboardManager; | |||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.support.annotation.ColorRes; | import android.support.annotation.ColorRes; | ||||||
| import android.support.annotation.DrawableRes; | import android.support.annotation.DrawableRes; | ||||||
|  | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
|  |  | ||||||
| @@ -16,6 +17,9 @@ import java.io.FileInputStream; | |||||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||||
| import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.io.ObjectInputStream; | ||||||
|  | import java.io.ObjectOutputStream; | ||||||
|  | import java.io.Serializable; | ||||||
| import java.security.MessageDigest; | import java.security.MessageDigest; | ||||||
| import java.security.NoSuchAlgorithmException; | import java.security.NoSuchAlgorithmException; | ||||||
|  |  | ||||||
| @@ -51,58 +55,48 @@ public class Utility { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void writeToFile(String fileName, String content) { |     public static void writeToFile(@NonNull String fileName, @NonNull Serializable serializable) { | ||||||
|         try { |         ObjectOutputStream objectOutputStream = null; | ||||||
|             writeToFile(fileName, content.getBytes("UTF-8")); |  | ||||||
|         } catch (Exception e) { |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static void writeToFile(String fileName, byte[] content) { |  | ||||||
|         File f = new File(fileName); |  | ||||||
|  |  | ||||||
|         if (!f.exists()) { |  | ||||||
|             try { |  | ||||||
|                 f.createNewFile(); |  | ||||||
|             } catch (Exception e) { |  | ||||||
|  |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             FileOutputStream opt = new FileOutputStream(f, false); |             objectOutputStream = new ObjectOutputStream(new FileOutputStream(fileName)); | ||||||
|             opt.write(content, 0, content.length); |             objectOutputStream.writeObject(serializable); | ||||||
|             opt.close(); |  | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|  |             //nothing to do | ||||||
|         } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     public static String readFromFile(String file) { |         if(objectOutputStream != null) { | ||||||
|             try { |             try { | ||||||
|             File f = new File(file); |                 objectOutputStream.close(); | ||||||
|  |  | ||||||
|             if (!f.exists() || !f.canRead()) { |  | ||||||
|                 return null; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             BufferedInputStream ipt = new BufferedInputStream(new FileInputStream(f)); |  | ||||||
|  |  | ||||||
|             byte[] buf = new byte[512]; |  | ||||||
|             StringBuilder sb = new StringBuilder(); |  | ||||||
|  |  | ||||||
|             while (ipt.available() > 0) { |  | ||||||
|                 int len = ipt.read(buf, 0, 512); |  | ||||||
|                 sb.append(new String(buf, 0, len, "UTF-8")); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             ipt.close(); |  | ||||||
|             return sb.toString(); |  | ||||||
|             } catch (Exception e) { |             } catch (Exception e) { | ||||||
|             return null; |                 //nothing to do | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nullable | ||||||
|  |     @SuppressWarnings("unchecked") | ||||||
|  |     public static <T> T readFromFile(String file) { | ||||||
|  |         T object = null; | ||||||
|  |         ObjectInputStream objectInputStream = null; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             objectInputStream = new ObjectInputStream(new FileInputStream(file)); | ||||||
|  |             object = (T) objectInputStream.readObject(); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             //nothing to do | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(objectInputStream != null){ | ||||||
|  |             try { | ||||||
|  |                 objectInputStream .close(); | ||||||
|  |             } catch (Exception e) { | ||||||
|  |                 //nothing to do | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return object; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Nullable |     @Nullable | ||||||
|     public static String getFileExt(String url) { |     public static String getFileExt(String url) { | ||||||
|   | |||||||
| @@ -1,81 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <RelativeLayout |  | ||||||
|     xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:layout_marginBottom="12dp" |  | ||||||
|     android:background="?attr/selectableItemBackground"> |  | ||||||
|  |  | ||||||
|     <RelativeLayout |  | ||||||
|         android:id="@+id/lastPlayed" |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:background="?attr/selectableItemBackground" |  | ||||||
|         android:clickable="true" |  | ||||||
|         android:focusable="true"> |  | ||||||
|         <ImageView |  | ||||||
|             android:id="@+id/lastPlayedIcon" |  | ||||||
|             android:layout_width="48dp" |  | ||||||
|             android:layout_height="28dp" |  | ||||||
|             android:layout_alignParentLeft="true" |  | ||||||
|             android:layout_centerVertical="true" |  | ||||||
|             android:layout_marginLeft="12dp" |  | ||||||
|             android:layout_marginRight="12dp" |  | ||||||
|             android:src="?attr/history" |  | ||||||
|             tools:ignore="ContentDescription,RtlHardcoded"/> |  | ||||||
|  |  | ||||||
|         <TextView |  | ||||||
|             android:id="@+id/lastPlayedText" |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="50dp" |  | ||||||
|             android:layout_toRightOf="@+id/lastPlayedIcon" |  | ||||||
|             android:gravity="left|center" |  | ||||||
|             android:text="@string/title_last_played" |  | ||||||
|             android:textAppearance="?android:attr/textAppearanceLarge" |  | ||||||
|             android:textSize="15sp" |  | ||||||
|             android:textStyle="bold" |  | ||||||
|             tools:ignore="RtlHardcoded"/> |  | ||||||
|     </RelativeLayout> |  | ||||||
|  |  | ||||||
|     <RelativeLayout |  | ||||||
|         android:id="@+id/mostPlayed" |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_below="@id/lastPlayed" |  | ||||||
|         android:background="?attr/selectableItemBackground" |  | ||||||
|         android:clickable="true" |  | ||||||
|         android:focusable="true"> |  | ||||||
|         <ImageView |  | ||||||
|             android:id="@+id/mostPlayedIcon" |  | ||||||
|             android:layout_width="48dp" |  | ||||||
|             android:layout_height="28dp" |  | ||||||
|             android:layout_alignParentLeft="true" |  | ||||||
|             android:layout_centerVertical="true" |  | ||||||
|             android:layout_marginLeft="12dp" |  | ||||||
|             android:layout_marginRight="12dp" |  | ||||||
|             android:src="?attr/filter" |  | ||||||
|             tools:ignore="ContentDescription,RtlHardcoded"/> |  | ||||||
|  |  | ||||||
|         <TextView |  | ||||||
|             android:id="@+id/mostPlayedText" |  | ||||||
|             android:layout_width="match_parent" |  | ||||||
|             android:layout_height="50dp" |  | ||||||
|             android:layout_toRightOf="@+id/mostPlayedIcon" |  | ||||||
|             android:gravity="left|center" |  | ||||||
|             android:text="@string/title_most_played" |  | ||||||
|             android:textAppearance="?android:attr/textAppearanceLarge" |  | ||||||
|             android:textSize="15sp" |  | ||||||
|             android:textStyle="bold" |  | ||||||
|             tools:ignore="RtlHardcoded"/> |  | ||||||
|     </RelativeLayout> |  | ||||||
|  |  | ||||||
|     <View |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="1dp" |  | ||||||
|         android:layout_below="@+id/mostPlayed" |  | ||||||
|         android:layout_marginLeft="8dp" |  | ||||||
|         android:layout_marginRight="8dp" |  | ||||||
|         android:background="?attr/separator_color"/> |  | ||||||
|  |  | ||||||
| </RelativeLayout> |  | ||||||
							
								
								
									
										42
									
								
								app/src/main/res/layout/statistic_playlist_control.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/src/main/res/layout/statistic_playlist_control.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <LinearLayout | ||||||
|  |     xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:layout_height="wrap_content" | ||||||
|  |     android:background="?attr/selectableItemBackground" | ||||||
|  |     android:orientation="vertical"> | ||||||
|  |  | ||||||
|  |     <RelativeLayout | ||||||
|  |         android:id="@+id/sortButton" | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:clickable="true" | ||||||
|  |         android:focusable="true"> | ||||||
|  |         <ImageView | ||||||
|  |             android:id="@+id/sortButtonIcon" | ||||||
|  |             android:layout_width="48dp" | ||||||
|  |             android:layout_height="28dp" | ||||||
|  |             android:layout_alignParentLeft="true" | ||||||
|  |             android:layout_centerVertical="true" | ||||||
|  |             android:layout_marginLeft="12dp" | ||||||
|  |             android:layout_marginRight="12dp" | ||||||
|  |             android:src="?attr/filter" | ||||||
|  |             tools:ignore="ContentDescription,RtlHardcoded"/> | ||||||
|  |  | ||||||
|  |         <TextView | ||||||
|  |             android:id="@+id/sortButtonText" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="50dp" | ||||||
|  |             android:layout_toRightOf="@id/sortButtonIcon" | ||||||
|  |             android:gravity="left|center" | ||||||
|  |             android:text="@string/title_most_played" | ||||||
|  |             android:textAppearance="?android:attr/textAppearanceLarge" | ||||||
|  |             android:textSize="15sp" | ||||||
|  |             android:textStyle="bold" | ||||||
|  |             tools:ignore="RtlHardcoded"/> | ||||||
|  |     </RelativeLayout> | ||||||
|  |  | ||||||
|  |     <include layout="@layout/playlist_control" /> | ||||||
|  |  | ||||||
|  | </LinearLayout> | ||||||
| @@ -147,6 +147,8 @@ | |||||||
|     <string name="download_thumbnail_key" translatable="false">download_thumbnail_key</string> |     <string name="download_thumbnail_key" translatable="false">download_thumbnail_key</string> | ||||||
|  |  | ||||||
|     <string name="metadata_cache_wipe_key" translatable="false">cache_wipe_key</string> |     <string name="metadata_cache_wipe_key" translatable="false">cache_wipe_key</string> | ||||||
|  |     <string name="clear_views_history_key" translatable="false">clear_play_history</string> | ||||||
|  |     <string name="clear_search_history_key" translatable="false">clear_search_history</string> | ||||||
|  |  | ||||||
|     <!-- FileName Downloads  --> |     <!-- FileName Downloads  --> | ||||||
|     <string name="settings_file_charset_key" translatable="false">file_rename</string> |     <string name="settings_file_charset_key" translatable="false">file_rename</string> | ||||||
|   | |||||||
| @@ -155,6 +155,14 @@ | |||||||
|     <string name="export_data_title">Export database</string> |     <string name="export_data_title">Export database</string> | ||||||
|     <string name="import_data_summary">Will override your current history and subscriptions</string> |     <string name="import_data_summary">Will override your current history and subscriptions</string> | ||||||
|     <string name="export_data_summary">Export history, subscriptions and playlists.</string> |     <string name="export_data_summary">Export history, subscriptions and playlists.</string> | ||||||
|  |     <string name="clear_views_history_title">Clear watch history</string> | ||||||
|  |     <string name="clear_views_history_summary">Deletes the history of played streams.</string> | ||||||
|  |     <string name="delete_view_history_alert">Delete whole watch history.</string> | ||||||
|  |     <string name="view_history_deleted">Watch history deleted.</string> | ||||||
|  |     <string name="clear_search_history_title">Clear search history</string> | ||||||
|  |     <string name="clear_search_history_summary">Deletes history of search keywords.</string> | ||||||
|  |     <string name="delete_search_history_alert">Delete whole search history.</string> | ||||||
|  |     <string name="search_history_deleted">Search history deleted.</string> | ||||||
|     <!-- error strings --> |     <!-- error strings --> | ||||||
|     <string name="general_error">Error</string> |     <string name="general_error">Error</string> | ||||||
|     <string name="network_error">Network error</string> |     <string name="network_error">Network error</string> | ||||||
| @@ -273,6 +281,7 @@ | |||||||
|     <string name="msg_copied">Copied to clipboard</string> |     <string name="msg_copied">Copied to clipboard</string> | ||||||
|     <string name="no_available_dir">Please select an available download folder</string> |     <string name="no_available_dir">Please select an available download folder</string> | ||||||
|     <string name="msg_popup_permission">This permission is needed to\nopen in popup mode</string> |     <string name="msg_popup_permission">This permission is needed to\nopen in popup mode</string> | ||||||
|  |     <string name="one_item_deleted">1 item deleted.</string> | ||||||
|  |  | ||||||
|     <!-- Checksum types --> |     <!-- Checksum types --> | ||||||
|     <string name="md5" translatable="false">MD5</string> |     <string name="md5" translatable="false">MD5</string> | ||||||
|   | |||||||
| @@ -21,4 +21,14 @@ | |||||||
|         android:summary="@string/metadata_cache_wipe_summary" |         android:summary="@string/metadata_cache_wipe_summary" | ||||||
|         android:title="@string/metadata_cache_wipe_title"/> |         android:title="@string/metadata_cache_wipe_title"/> | ||||||
|  |  | ||||||
|  |     <Preference | ||||||
|  |         android:key="@string/clear_views_history_key" | ||||||
|  |         android:title="@string/clear_views_history_title" | ||||||
|  |         android:summary="@string/clear_views_history_summary"/> | ||||||
|  |  | ||||||
|  |     <Preference | ||||||
|  |         android:key="@string/clear_search_history_key" | ||||||
|  |         android:title="@string/clear_search_history_title" | ||||||
|  |         android:summary="@string/clear_search_history_summary"/> | ||||||
|  |  | ||||||
| </PreferenceScreen> | </PreferenceScreen> | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| package org.schabi.newpipe.subscription.services; | package org.schabi.newpipe.local.subscription.services; | ||||||
| 
 | 
 | ||||||
| import org.junit.Test; | import org.junit.Test; | ||||||
| import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; | import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; | ||||||
| import org.schabi.newpipe.extractor.subscription.SubscriptionItem; | import org.schabi.newpipe.extractor.subscription.SubscriptionItem; | ||||||
| import org.schabi.newpipe.subscription.ImportExportJsonHelper; | import org.schabi.newpipe.local.subscription.ImportExportJsonHelper; | ||||||
| 
 | 
 | ||||||
| import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||||
| import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||||
		Reference in New Issue
	
	Block a user
	 Weblate
					Weblate