diff --git a/app/build.gradle b/app/build.gradle index c41535163..fbaab1214 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,7 +54,7 @@ dependencies { exclude module: 'support-annotations' } - implementation 'com.github.TeamNewPipe:NewPipeExtractor:bf1c771' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:1eff8c5708' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.8.9' diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 61b430c27..191332e19 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -61,6 +61,8 @@ import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.ThemeHelper; +import static org.schabi.newpipe.extractor.InfoItem.InfoType.PLAYLIST; + public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); @@ -392,31 +394,45 @@ public class MainActivity extends AppCompatActivity { } private void handleIntent(Intent intent) { - if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]"); + try { + if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]"); - if (intent.hasExtra(Constants.KEY_LINK_TYPE)) { - String url = intent.getStringExtra(Constants.KEY_URL); - int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); - String title = intent.getStringExtra(Constants.KEY_TITLE); - switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) { - case STREAM: - boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false); - NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay); - break; - case CHANNEL: - NavigationHelper.openChannelFragment(getSupportFragmentManager(), serviceId, url, title); - break; - case PLAYLIST: - NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), serviceId, url, title); - break; + if (intent.hasExtra(Constants.KEY_LINK_TYPE)) { + String url = intent.getStringExtra(Constants.KEY_URL); + int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); + String title = intent.getStringExtra(Constants.KEY_TITLE); + switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) { + case STREAM: + boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false); + NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay); + break; + case CHANNEL: + NavigationHelper.openChannelFragment(getSupportFragmentManager(), + serviceId, + url, + title); + break; + case PLAYLIST: + NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), + serviceId, + url, + title); + break; + } + } else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) { + String searchString = intent.getStringExtra(Constants.KEY_SEARCH_STRING); + if (searchString == null) searchString = ""; + int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); + NavigationHelper.openSearchFragment( + getSupportFragmentManager(), + serviceId, + searchString); + + } else { + NavigationHelper.gotoMainFragment(getSupportFragmentManager()); } - } else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) { - String searchQuery = intent.getStringExtra(Constants.KEY_QUERY); - if (searchQuery == null) searchQuery = ""; - int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); - NavigationHelper.openSearchFragment(getSupportFragmentManager(), serviceId, searchQuery); - } else { - NavigationHelper.gotoMainFragment(getSupportFragmentManager()); + } catch (Exception e) { + ErrorActivity.reportUiError(this, e); } } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 31092d3e6..0e3312403 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -10,6 +10,7 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; import android.support.v7.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; @@ -127,7 +128,14 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_search: - NavigationHelper.openSearchFragment(getFragmentManager(), ServiceHelper.getSelectedServiceId(activity), ""); + try { + NavigationHelper.openSearchFragment( + getFragmentManager(), + ServiceHelper.getSelectedServiceId(activity), + ""); + } catch (Exception e) { + ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e); + } return true; } return super.onOptionsItemSelected(item); @@ -226,7 +234,9 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte FALLBACK_CHANNEL_URL); String name = preferences.getString(getString(R.string.main_page_selected_channel_name), FALLBACK_CHANNEL_NAME); - ChannelFragment fragment = ChannelFragment.getInstance(serviceId, url, name); + ChannelFragment fragment = ChannelFragment.getInstance(serviceId, + url, + name); fragment.useAsFrontPage(true); return fragment; } else { @@ -255,20 +265,13 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte for (final String ks : kl.getAvailableKiosks()) { menu.add(0, KIOSK_MENU_OFFSET + i, Menu.NONE, KioskTranslator.getTranslatedKioskName(ks, getContext())) - .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem menuItem) { + .setOnMenuItemClickListener(menuItem -> { try { NavigationHelper.openKioskFragment(getFragmentManager(), currentServiceId, ks); } catch (Exception e) { - ErrorActivity.reportError(activity, e, - activity.getClass(), - null, - ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, - "none", "", R.string.app_ui_crash)); + ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e); } return true; - } }); i++; } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 5aef21f39..d91502cdd 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -17,6 +17,7 @@ import android.support.v4.content.ContextCompat; import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; import android.text.Html; import android.text.Spanned; import android.text.TextUtils; @@ -54,7 +55,7 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ParsingException; -import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor; +import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; @@ -64,6 +65,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.local.history.HistoryRecordManager; +import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.util.StreamItemAdapter; import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; @@ -365,11 +367,15 @@ public class VideoDetailFragment if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) { Log.w(TAG, "Can't open channel because we got no channel URL"); } else { - NavigationHelper.openChannelFragment( - getFragmentManager(), - currentInfo.getServiceId(), - currentInfo.getUploaderUrl(), - currentInfo.getUploaderName()); + try { + NavigationHelper.openChannelFragment( + getFragmentManager(), + currentInfo.getServiceId(), + currentInfo.getUploaderUrl(), + currentInfo.getUploaderName()); + } catch (Exception e) { + ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e); + } } break; case R.id.detail_thumbnail_root_layout: diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index c4b341aae..1db12bba9 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -6,6 +6,7 @@ import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; @@ -24,6 +25,7 @@ import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; +import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.StateSaver; @@ -152,18 +154,35 @@ public abstract class BaseListFragment extends BaseStateFragment implem infoListAdapter.setOnChannelSelectedListener(new OnClickGesture() { @Override public void selected(ChannelInfoItem selectedItem) { - onItemSelected(selectedItem); - NavigationHelper.openChannelFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(), - selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); + try { + onItemSelected(selectedItem); + NavigationHelper.openChannelFragment(useAsFrontPage ? + getParentFragment().getFragmentManager() + : getFragmentManager(), + selectedItem.getServiceId(), + selectedItem.getUrl(), + selectedItem.getName()); + } catch (Exception e) { + ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e); + } } }); infoListAdapter.setOnPlaylistSelectedListener(new OnClickGesture() { @Override public void selected(PlaylistInfoItem selectedItem) { - onItemSelected(selectedItem); - NavigationHelper.openPlaylistFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(), - selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); + try { + onItemSelected(selectedItem); + NavigationHelper.openPlaylistFragment( + useAsFrontPage + ? getParentFragment().getFragmentManager() + : getFragmentManager(), + selectedItem.getServiceId(), + selectedItem.getUrl(), + selectedItem.getName()); + } catch (Exception e) { + ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e); + } } }); @@ -178,7 +197,9 @@ public abstract class BaseListFragment extends BaseStateFragment implem private void onStreamSelected(StreamInfoItem selectedItem) { onItemSelected(selectedItem); - NavigationHelper.openVideoDetailFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(), + NavigationHelper.openVideoDetailFragment(useAsFrontPage + ? getParentFragment().getFragmentManager() + : getFragmentManager(), selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java index a132213bf..e702c602f 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java @@ -8,6 +8,9 @@ import android.view.View; import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListInfo; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; +import org.schabi.newpipe.extractor.linkhandler.LinkHandler; import org.schabi.newpipe.util.Constants; import java.util.Queue; @@ -166,7 +169,6 @@ public abstract class BaseListInfoFragment public void handleResult(@NonNull I result) { super.handleResult(result); - url = result.getUrl(); name = result.getName(); setTitle(name); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index b56692877..42ba8e0ac 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -33,6 +33,7 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; @@ -501,7 +502,11 @@ public class ChannelFragment extends BaseListInfoFragment { if (super.onError(exception)) return true; int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error; - onUnrecoverableError(exception, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId), url, errorId); + onUnrecoverableError(exception, + UserAction.REQUESTED_CHANNEL, + NewPipe.getNameOfService(serviceId), + url, + errorId); return true; } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index 5dfdcd655..114e92e43 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -11,22 +11,20 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; -import org.schabi.newpipe.extractor.UrlIdHandler; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.kiosk.KioskInfo; -import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; +import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; -import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.KioskTranslator; -import org.schabi.newpipe.util.NavigationHelper; import icepick.State; import io.reactivex.Single; @@ -74,10 +72,10 @@ public class KioskFragment extends BaseListInfoFragment { throws ExtractionException { KioskFragment instance = new KioskFragment(); StreamingService service = NewPipe.getService(serviceId); - UrlIdHandler kioskTypeUrlIdHandler = service.getKioskList() - .getUrlIdHandlerByType(kioskId); + ListLinkHandlerFactory kioskLinkHandlerFactory = service.getKioskList() + .getListLinkHandlerFactoryByType(kioskId); instance.setInitialData(serviceId, - kioskTypeUrlIdHandler.getUrl(kioskId), kioskId); + kioskLinkHandlerFactory.fromId(kioskId).getUrl(), kioskId); instance.kioskId = kioskId; return instance; } @@ -136,7 +134,10 @@ public class KioskFragment extends BaseListInfoFragment { .getDefaultSharedPreferences(activity) .getString(getString(R.string.content_country_key), getString(R.string.default_country_value)); - return ExtractorHelper.getKioskInfo(serviceId, url, contentCountry, forceReload); + return ExtractorHelper.getKioskInfo(serviceId, + url, + contentCountry, + forceReload); } @Override @@ -145,7 +146,10 @@ public class KioskFragment extends BaseListInfoFragment { .getDefaultSharedPreferences(activity) .getString(getString(R.string.content_country_key), getString(R.string.default_country_value)); - return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextPageUrl, contentCountry); + return ExtractorHelper.getMoreKioskItems(serviceId, + url, + currentNextPageUrl, + contentCountry); } /*////////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index ca732aa2c..b7a42791c 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -6,6 +6,7 @@ import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; @@ -19,6 +20,7 @@ import android.widget.TextView; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import org.schabi.newpipe.App; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; @@ -28,12 +30,14 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; +import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ImageDisplayConstants; @@ -267,11 +271,16 @@ public class PlaylistFragment extends BaseListInfoFragment { if (!TextUtils.isEmpty(result.getUploaderName())) { headerUploaderName.setText(result.getUploaderName()); if (!TextUtils.isEmpty(result.getUploaderUrl())) { - headerUploaderLayout.setOnClickListener(v -> + headerUploaderLayout.setOnClickListener(v -> { + try { NavigationHelper.openChannelFragment(getFragmentManager(), - result.getServiceId(), result.getUploaderUrl(), - result.getUploaderName()) - ); + result.getServiceId(), + result.getUploaderUrl(), + result.getUploaderName()); + } catch (Exception e) { + ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e); + } + }); } } @@ -339,7 +348,11 @@ public class PlaylistFragment extends BaseListInfoFragment { if (super.onError(exception)) return true; int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error; - onUnrecoverableError(exception, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId), url, errorId); + onUnrecoverableError(exception, + UserAction.REQUESTED_PLAYLIST, + NewPipe.getNameOfService(serviceId), + url, + errorId); return true; } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index d07ff6448..5e511c03b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -29,6 +29,8 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; +import com.facebook.stetho.common.ListUtil; + import org.schabi.newpipe.R; import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.database.history.model.SearchHistoryEntry; @@ -37,26 +39,28 @@ import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ParsingException; -import org.schabi.newpipe.extractor.search.SearchEngine; -import org.schabi.newpipe.extractor.search.SearchResult; +import org.schabi.newpipe.extractor.search.SearchExtractor; +import org.schabi.newpipe.extractor.search.SearchInfo; import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.list.BaseListFragment; import org.schabi.newpipe.local.history.HistoryRecordManager; +import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.LayoutManagerSmoothScroller; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.ServiceHelper; import java.io.IOException; import java.io.InterruptedIOException; import java.net.SocketException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Queue; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import icepick.State; @@ -65,14 +69,15 @@ import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; -import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.PublishSubject; +import static java.util.Arrays.asList; + import static org.schabi.newpipe.util.AnimationUtils.animateView; public class SearchFragment - extends BaseListFragment + extends BaseListFragment implements BackPressable { /*////////////////////////////////////////////////////////////////////////// @@ -92,19 +97,28 @@ public class SearchFragment @State protected int filterItemCheckedId = -1; - private SearchEngine.Filter filter = SearchEngine.Filter.ANY; @State protected int serviceId = Constants.NO_SERVICE_ID; + + // this three represet the current search query @State - protected String searchQuery; + protected String searchString; @State - protected String lastSearchedQuery; + protected String[] contentFilter; + @State + protected String sortFilter; + + // these represtent the last search + @State + protected String lastSearchedString; + @State protected boolean wasSearchFocused = false; - private int currentPage = 0; - private int currentNextPage = 0; + private StreamingService service; + private String currentPageUrl; + private String nextPageUrl; private String contentCountry; private boolean isSuggestionsEnabled = true; private boolean isSearchHistoryEnabled = true; @@ -130,11 +144,11 @@ public class SearchFragment /*////////////////////////////////////////////////////////////////////////*/ - public static SearchFragment getInstance(int serviceId, String query) { + public static SearchFragment getInstance(int serviceId, String searchString) { SearchFragment searchFragment = new SearchFragment(); - searchFragment.setQuery(serviceId, query); + searchFragment.setQuery(serviceId, searchString, new String[0], ""); - if (!TextUtils.isEmpty(query)) { + if (!TextUtils.isEmpty(searchString)) { searchFragment.setSearchOnResume(); } @@ -202,13 +216,22 @@ public class SearchFragment if (DEBUG) Log.d(TAG, "onResume() called"); super.onResume(); - if (!TextUtils.isEmpty(searchQuery)) { + try { + service = NewPipe.getService(serviceId); + } catch (Exception e) { + ErrorActivity.reportError(getActivity(), e, getActivity().getClass(), + getActivity().findViewById(android.R.id.content), + ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, + "", + "", R.string.general_error)); + } + + if (!TextUtils.isEmpty(searchString)) { if (wasLoading.getAndSet(false)) { - if (currentNextPage > currentPage) loadMoreItems(); - else search(searchQuery); + search(searchString, contentFilter, sortFilter); } else if (infoListAdapter.getItemsList().size() == 0) { if (savedState == null) { - search(searchQuery); + search(searchString, contentFilter, sortFilter); } else if (!isLoading.get() && !wasSearchFocused) { infoListAdapter.clearStreamItemList(); showEmptyState(); @@ -218,7 +241,7 @@ public class SearchFragment if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver(); - if (TextUtils.isEmpty(searchQuery) || wasSearchFocused) { + if (TextUtils.isEmpty(searchString) || wasSearchFocused) { showKeyboardSearch(); showSuggestionsPanel(); } else { @@ -247,8 +270,9 @@ public class SearchFragment public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case ReCaptchaActivity.RECAPTCHA_REQUEST: - if (resultCode == Activity.RESULT_OK && !TextUtils.isEmpty(searchQuery)) { - search(searchQuery); + if (resultCode == Activity.RESULT_OK + && !TextUtils.isEmpty(searchString)) { + search(searchString, contentFilter, sortFilter); } else Log.e(TAG, "ReCaptcha failed"); break; @@ -282,20 +306,22 @@ public class SearchFragment @Override public void writeTo(Queue objectsToSave) { super.writeTo(objectsToSave); - objectsToSave.add(currentPage); - objectsToSave.add(currentNextPage); + objectsToSave.add(currentPageUrl); + objectsToSave.add(nextPageUrl); } @Override public void readFrom(@NonNull Queue savedObjects) throws Exception { super.readFrom(savedObjects); - currentPage = (int) savedObjects.poll(); - currentNextPage = (int) savedObjects.poll(); + currentPageUrl = (String) savedObjects.poll(); + nextPageUrl = (String) savedObjects.poll(); } @Override public void onSaveInstanceState(Bundle bundle) { - searchQuery = searchEditText != null ? searchEditText.getText().toString() : searchQuery; + searchString = searchEditText != null + ? searchEditText.getText().toString() + : searchString; super.onSaveInstanceState(bundle); } @@ -305,8 +331,11 @@ public class SearchFragment @Override public void reloadContent() { - if (!TextUtils.isEmpty(searchQuery) || (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) { - search(!TextUtils.isEmpty(searchQuery) ? searchQuery : searchEditText.getText().toString()); + if (!TextUtils.isEmpty(searchString) + || (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) { + search(!TextUtils.isEmpty(searchString) + ? searchString + : searchEditText.getText().toString(), new String[0], ""); } else { if (searchEditText != null) { searchEditText.setText(""); @@ -330,22 +359,32 @@ public class SearchFragment supportActionBar.setDisplayHomeAsUpEnabled(true); } - inflater.inflate(R.menu.menu_search, menu); + int itemId = 0; + boolean isFirstItem = true; + final Context c = getContext(); + for(String filter : service.getSearchQIHFactory().getAvailableContentFilter()) { + MenuItem item = menu.add(1, + itemId++, + 0, + ServiceHelper.getTranslatedFilterString(filter, c)); + if(isFirstItem) { + item.setChecked(true); + isFirstItem = false; + } + } + menu.setGroupCheckable(1, true, true); + restoreFilterChecked(menu, filterItemCheckedId); } @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_filter_all: - case R.id.menu_filter_video: - case R.id.menu_filter_channel: - case R.id.menu_filter_playlist: - changeFilter(item, getFilterFromMenuId(item.getItemId())); - return true; - default: - return super.onOptionsItemSelected(item); - } + + List contentFilter = new ArrayList<>(1); + contentFilter.add(item.getTitle().toString()); + changeContentFilter(item, contentFilter); + + return true; } private void restoreFilterChecked(Menu menu, int itemId) { @@ -354,21 +393,6 @@ public class SearchFragment if (item == null) return; item.setChecked(true); - filter = getFilterFromMenuId(itemId); - } - } - - private SearchEngine.Filter getFilterFromMenuId(int itemId) { - switch (itemId) { - case R.id.menu_filter_video: - return SearchEngine.Filter.STREAM; - case R.id.menu_filter_channel: - return SearchEngine.Filter.CHANNEL; - case R.id.menu_filter_playlist: - return SearchEngine.Filter.PLAYLIST; - case R.id.menu_filter_all: - default: - return SearchEngine.Filter.ANY; } } @@ -379,14 +403,21 @@ public class SearchFragment private TextWatcher textWatcher; private void showSearchOnStart() { - if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → " + searchQuery+", lastSearchedQuery → " + lastSearchedQuery); - searchEditText.setText(searchQuery); + if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → " + + searchString + + ", lastSearchedQuery → " + + lastSearchedString); + searchEditText.setText(searchString); - if (TextUtils.isEmpty(searchQuery) || TextUtils.isEmpty(searchEditText.getText())) { + if (TextUtils.isEmpty(searchString) || TextUtils.isEmpty(searchEditText.getText())) { searchToolbarContainer.setTranslationX(100); searchToolbarContainer.setAlpha(0f); searchToolbarContainer.setVisibility(View.VISIBLE); - searchToolbarContainer.animate().translationX(0).alpha(1f).setDuration(200).setInterpolator(new DecelerateInterpolator()).start(); + searchToolbarContainer.animate() + .translationX(0) + .alpha(1f) + .setDuration(200) + .setInterpolator(new DecelerateInterpolator()).start(); } else { searchToolbarContainer.setTranslationX(0); searchToolbarContainer.setAlpha(1f); @@ -396,47 +427,38 @@ public class SearchFragment private void initSearchListeners() { if (DEBUG) Log.d(TAG, "initSearchListeners() called"); - searchClear.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]"); - if (TextUtils.isEmpty(searchEditText.getText())) { - NavigationHelper.gotoMainFragment(getFragmentManager()); - return; - } - - searchEditText.setText(""); - suggestionListAdapter.setItems(new ArrayList()); - showKeyboardSearch(); + searchClear.setOnClickListener(v -> { + if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]"); + if (TextUtils.isEmpty(searchEditText.getText())) { + NavigationHelper.gotoMainFragment(getFragmentManager()); + return; } + + searchEditText.setText(""); + suggestionListAdapter.setItems(new ArrayList<>()); + showKeyboardSearch(); }); TooltipCompat.setTooltipText(searchClear, getString(R.string.clear)); - searchEditText.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]"); - if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) { - showSuggestionsPanel(); - } + searchEditText.setOnClickListener(v -> { + if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]"); + if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) { + showSuggestionsPanel(); } }); - searchEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]"); - if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) { - showSuggestionsPanel(); - } + searchEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> { + if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]"); + if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) { + showSuggestionsPanel(); } }); suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() { @Override public void onSuggestionItemSelected(SuggestionItem item) { - search(item.query); + search(item.query, new String[0], ""); searchEditText.setText(item.query); } @@ -469,21 +491,22 @@ public class SearchFragment } }; searchEditText.addTextChangedListener(textWatcher); - searchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (DEBUG) { - Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]"); - } - if (event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || event.getAction() == EditorInfo.IME_ACTION_SEARCH)) { - search(searchEditText.getText().toString()); - return true; - } - return false; - } - }); + searchEditText.setOnEditorActionListener( + (TextView v, int actionId, KeyEvent event) -> { + if (DEBUG) { + Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]"); + } + if (event != null + && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER + || event.getAction() == EditorInfo.IME_ACTION_SEARCH)) { + search(searchEditText.getText().toString(), new String[0], ""); + return true; + } + return false; + }); - if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver(); + if (suggestionDisposable == null || suggestionDisposable.isDisposed()) + initSuggestionObserver(); } private void unsetSearchListeners() { @@ -513,7 +536,8 @@ public class SearchFragment if (searchEditText == null) return; if (searchEditText.requestFocus()) { - InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + InputMethodManager imm = (InputMethodManager) activity.getSystemService( + Context.INPUT_METHOD_SERVICE); imm.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT); } } @@ -522,8 +546,10 @@ public class SearchFragment if (DEBUG) Log.d(TAG, "hideKeyboardSearch() called"); if (searchEditText == null) return; - InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + InputMethodManager imm = (InputMethodManager) activity.getSystemService( + Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), + InputMethodManager.HIDE_NOT_ALWAYS); searchEditText.clearFocus(); } @@ -554,10 +580,12 @@ public class SearchFragment @Override public boolean onBackPressed() { - if (suggestionsPanel.getVisibility() == View.VISIBLE && infoListAdapter.getItemsList().size() > 0 && !isLoading.get()) { + if (suggestionsPanel.getVisibility() == View.VISIBLE + && infoListAdapter.getItemsList().size() > 0 + && !isLoading.get()) { hideSuggestionsPanel(); hideKeyboardSearch(); - searchEditText.setText(lastSearchedQuery); + searchEditText.setText(lastSearchedString); return true; } return false; @@ -573,8 +601,10 @@ public class SearchFragment final Observable observable = suggestionPublisher .debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS) - .startWith(searchQuery != null ? searchQuery : "") - .filter(query -> isSuggestionsEnabled); + .startWith(searchString != null + ? searchString + : "") + .filter(searchString -> isSuggestionsEnabled); suggestionDisposable = observable .switchMap(query -> { @@ -645,56 +675,44 @@ public class SearchFragment // no-op } - private void search(final String query) { - if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]"); - if (query.isEmpty()) return; + private void search(final String searchString, String[] contentFilter, String sortFilter) { + if (DEBUG) Log.d(TAG, "search() called with: query = [" + searchString + "]"); + if (searchString.isEmpty()) return; try { - final StreamingService service = NewPipe.getServiceByUrl(query); + final StreamingService service = NewPipe.getServiceByUrl(searchString); if (service != null) { showLoading(); disposables.add(Observable - .fromCallable(new Callable() { - @Override - public Intent call() throws Exception { - return NavigationHelper.getIntentByLink(activity, service, query); - } - }) + .fromCallable(() -> + NavigationHelper.getIntentByLink(activity, service, searchString)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Consumer() { - @Override - public void accept(Intent intent) throws Exception { - getFragmentManager().popBackStackImmediate(); - activity.startActivity(intent); - } - }, new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - showError(getString(R.string.url_not_supported_toast), false); - } - })); + .subscribe(intent -> { + getFragmentManager().popBackStackImmediate(); + activity.startActivity(intent); + }, throwable -> + showError(getString(R.string.url_not_supported_toast), false))); return; } } catch (Exception e) { // Exception occurred, it's not a url } - lastSearchedQuery = query; - searchQuery = query; - currentPage = 0; + lastSearchedString = this.searchString; + this.searchString = searchString; infoListAdapter.clearStreamItemList(); hideSuggestionsPanel(); hideKeyboardSearch(); - historyRecordManager.onSearched(serviceId, query) + historyRecordManager.onSearched(serviceId, searchString) .observeOn(AndroidSchedulers.mainThread()) .subscribe( ignored -> {}, error -> showSnackBarError(error, UserAction.SEARCHED, - NewPipe.getNameOfService(serviceId), query, 0) + NewPipe.getNameOfService(serviceId), searchString, 0) ); - suggestionPublisher.onNext(query); + suggestionPublisher.onNext(searchString); startLoading(false); } @@ -703,11 +721,16 @@ public class SearchFragment super.startLoading(forceLoad); if (disposables != null) disposables.clear(); if (searchDisposable != null) searchDisposable.dispose(); - searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, contentCountry, filter) + searchDisposable = ExtractorHelper.searchFor(serviceId, + searchString, + Arrays.asList(contentFilter), + sortFilter, + contentCountry) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnEvent((searchResult, throwable) -> isLoading.set(false)) .subscribe(this::handleResult, this::onError); + } @Override @@ -715,8 +738,13 @@ public class SearchFragment isLoading.set(true); showListFooter(true); if (searchDisposable != null) searchDisposable.dispose(); - currentNextPage = currentPage + 1; - searchDisposable = ExtractorHelper.getMoreSearchItems(serviceId, searchQuery, currentNextPage, contentCountry, filter) + searchDisposable = ExtractorHelper.getMoreSearchItems( + serviceId, + searchString, + asList(contentFilter), + sortFilter, + nextPageUrl, + contentCountry) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnEvent((nextItemsResult, throwable) -> isLoading.set(false)) @@ -739,19 +767,22 @@ public class SearchFragment // Utils //////////////////////////////////////////////////////////////////////////*/ - private void changeFilter(MenuItem item, SearchEngine.Filter filter) { - this.filter = filter; + private void changeContentFilter(MenuItem item, List contentFilter) { this.filterItemCheckedId = item.getItemId(); item.setChecked(true); - if (!TextUtils.isEmpty(searchQuery)) { - search(searchQuery); + this.contentFilter = new String[] {contentFilter.get(0)}; + + if (!TextUtils.isEmpty(searchString)) { + search(searchString, this.contentFilter, sortFilter); } } - private void setQuery(int serviceId, String searchQuery) { + private void setQuery(int serviceId, String searchString, String[] contentfilter, String sortFilter) { this.serviceId = serviceId; - this.searchQuery = searchQuery; + this.searchString = searchString; + this.contentFilter = contentfilter; + this.sortFilter = sortFilter; } /*////////////////////////////////////////////////////////////////////////// @@ -772,8 +803,11 @@ public class SearchFragment if (DEBUG) Log.d(TAG, "onSuggestionError() called with: exception = [" + exception + "]"); if (super.onError(exception)) return; - int errorId = exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error; - onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS, NewPipe.getNameOfService(serviceId), searchQuery, errorId); + int errorId = exception instanceof ParsingException + ? R.string.parsing_error + : R.string.general_error; + onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS, + NewPipe.getNameOfService(serviceId), searchString, errorId); } /*////////////////////////////////////////////////////////////////////////// @@ -798,16 +832,19 @@ public class SearchFragment //////////////////////////////////////////////////////////////////////////*/ @Override - public void handleResult(@NonNull SearchResult result) { - if (!result.errors.isEmpty()) { - showSnackBarError(result.errors, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, 0); + public void handleResult(@NonNull SearchInfo result) { + if (!result.getErrors().isEmpty()) { + showSnackBarError(result.getErrors(), UserAction.SEARCHED, + NewPipe.getNameOfService(serviceId), searchString, 0); } - lastSearchedQuery = searchQuery; + lastSearchedString = searchString; + nextPageUrl = result.getNextPageUrl(); + currentPageUrl = result.getUrl(); if (infoListAdapter.getItemsList().size() == 0) { - if (!result.getResults().isEmpty()) { - infoListAdapter.addInfoItemList(result.getResults()); + if (!result.getRelatedItems().isEmpty()) { + infoListAdapter.addInfoItemList(result.getRelatedItems()); } else { infoListAdapter.clearStreamItemList(); showEmptyState(); @@ -821,12 +858,13 @@ public class SearchFragment @Override public void handleNextItems(ListExtractor.InfoItemsPage result) { showListFooter(false); - currentPage = Integer.parseInt(result.getNextPageUrl()); + currentPageUrl = result.getNextPageUrl(); infoListAdapter.addInfoItemList(result.getItems()); if (!result.getErrors().isEmpty()) { - showSnackBarError(result.getErrors(), UserAction.SEARCHED, NewPipe.getNameOfService(serviceId) - , "\"" + searchQuery + "\" → page " + currentPage, 0); + showSnackBarError(result.getErrors(), UserAction.SEARCHED, + NewPipe.getNameOfService(serviceId) + , "\"" + searchString + "\" → page: " + nextPageUrl, 0); } super.handleNextItems(result); } @@ -835,12 +873,15 @@ public class SearchFragment protected boolean onError(Throwable exception) { if (super.onError(exception)) return true; - if (exception instanceof SearchEngine.NothingFoundException) { + if (exception instanceof SearchExtractor.NothingFoundException) { infoListAdapter.clearStreamItemList(); showEmptyState(); } else { - int errorId = exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error; - onUnrecoverableError(exception, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, errorId); + int errorId = exception instanceof ParsingException + ? R.string.parsing_error + : R.string.general_error; + onUnrecoverableError(exception, UserAction.SEARCHED, + NewPipe.getNameOfService(serviceId), searchString, errorId); } return true; diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index f3f390c4d..02eabd9ef 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -6,6 +6,7 @@ import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.FragmentManager; +import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -19,9 +20,11 @@ import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.playlist.PlaylistLocalItem; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; +import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.local.playlist.LocalPlaylistManager; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; +import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; @@ -99,19 +102,26 @@ public final class BookmarkFragment itemListAdapter.setSelectedListener(new OnClickGesture() { @Override public void selected(LocalItem selectedItem) { - // Requires the parent fragment to find holder for fragment replacement - if (getParentFragment() == null) return; - final FragmentManager fragmentManager = getParentFragment().getFragmentManager(); + try { + // Requires the parent fragment to find holder for fragment replacement + if (getParentFragment() == null) return; + final FragmentManager fragmentManager = getParentFragment().getFragmentManager(); - if (selectedItem instanceof PlaylistMetadataEntry) { - final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); - NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid, - entry.name); + if (selectedItem instanceof PlaylistMetadataEntry) { + final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); + NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid, + entry.name); - } else if (selectedItem instanceof PlaylistRemoteEntity) { - final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); - NavigationHelper.openPlaylistFragment(fragmentManager, entry.getServiceId(), - entry.getUrl(), entry.getName()); + } else if (selectedItem instanceof PlaylistRemoteEntity) { + final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); + NavigationHelper.openPlaylistFragment( + fragmentManager, + entry.getServiceId(), + entry.getUrl(), + entry.getName()); + } + } catch (Exception e) { + ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.java b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.java index 5f6ea42ee..c194069fb 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.java @@ -15,6 +15,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -38,6 +39,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService; import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService; @@ -318,9 +320,15 @@ public class SubscriptionFragment extends BaseStateFragment() { @Override public void selected(ChannelInfoItem selectedItem) { - // Requires the parent fragment to find holder for fragment replacement - NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), - selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); + try { + // Requires the parent fragment to find holder for fragment replacement + NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), + selectedItem.getServiceId(), + selectedItem.getUrl(), + selectedItem.getName()); + } catch (Exception e) { + ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e); + } } }); diff --git a/app/src/main/java/org/schabi/newpipe/util/Constants.java b/app/src/main/java/org/schabi/newpipe/util/Constants.java index a6aec96e2..b01b6df6a 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Constants.java +++ b/app/src/main/java/org/schabi/newpipe/util/Constants.java @@ -6,7 +6,7 @@ public class Constants { public static final String KEY_TITLE = "key_title"; public static final String KEY_LINK_TYPE = "key_link_type"; public static final String KEY_OPEN_SEARCH = "key_open_search"; - public static final String KEY_QUERY = "key_query"; + public static final String KEY_SEARCH_STRING = "key_search_string"; public static final String KEY_THEME_CHANGE = "key_theme_change"; public static final String KEY_MAIN_PAGE_CHANGE = "key_main_page_change"; diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index 1897589c6..74e2d4cd5 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -37,9 +37,8 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.kiosk.KioskInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; -import org.schabi.newpipe.extractor.search.SearchEngine; -import org.schabi.newpipe.extractor.search.SearchResult; -import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor; +import org.schabi.newpipe.extractor.search.SearchInfo; +import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; @@ -50,7 +49,6 @@ import java.util.List; import io.reactivex.Maybe; import io.reactivex.Single; -import io.reactivex.annotations.NonNull; public final class ExtractorHelper { private static final String TAG = ExtractorHelper.class.getSimpleName(); @@ -66,29 +64,35 @@ public final class ExtractorHelper { } } - public static Single searchFor(final int serviceId, - final String query, - final int pageNumber, - final String contentCountry, - final SearchEngine.Filter filter) { + public static Single searchFor(final int serviceId, + final String searchString, + final List contentFilter, + final String sortFilter, + final String contentCountry) { checkServiceId(serviceId); return Single.fromCallable(() -> - SearchResult.getSearchResult(NewPipe.getService(serviceId).getSearchEngine(), - query, pageNumber, contentCountry, filter) - ); + SearchInfo.getInfo(NewPipe.getService(serviceId), + NewPipe.getService(serviceId) + .getSearchQIHFactory() + .fromQuery(searchString, contentFilter, sortFilter), + contentCountry)); } public static Single getMoreSearchItems(final int serviceId, - final String query, - final int nextPageNumber, - final String searchLanguage, - final SearchEngine.Filter filter) { + final String searchString, + final List contentFilter, + final String sortFilter, + final String pageUrl, + final String contentCountry) { checkServiceId(serviceId); - return searchFor(serviceId, query, nextPageNumber, searchLanguage, filter) - .map((@NonNull SearchResult searchResult) -> - new InfoItemsPage(searchResult.resultList, - nextPageNumber + "", - searchResult.errors)); + return Single.fromCallable(() -> + SearchInfo.getMoreItems(NewPipe.getService(serviceId), + NewPipe.getService(serviceId) + .getSearchQIHFactory() + .fromQuery(searchString, contentFilter, sortFilter), + contentCountry, + pageUrl)); + } public static Single> suggestionsFor(final int serviceId, @@ -233,7 +237,6 @@ public final class ExtractorHelper { serviceId == -1 ? "none" : NewPipe.getNameOfService(serviceId), url + (optionalErrorMessage == null ? "" : optionalErrorMessage), errorId)); } }); - } /** diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 12f6856de..85367e2a5 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -26,10 +26,13 @@ import org.schabi.newpipe.download.DownloadActivity; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; +import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler; import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; @@ -283,9 +286,11 @@ public class NavigationHelper { return fragmentManager.popBackStackImmediate(SEARCH_FRAGMENT_TAG, 0); } - public static void openSearchFragment(FragmentManager fragmentManager, int serviceId, String query) { + public static void openSearchFragment(FragmentManager fragmentManager, + int serviceId, + String searchString) { defaultTransaction(fragmentManager) - .replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, query)) + .replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, searchString)) .addToBackStack(SEARCH_FRAGMENT_TAG) .commit(); } @@ -314,7 +319,11 @@ public class NavigationHelper { .commit(); } - public static void openChannelFragment(FragmentManager fragmentManager, int serviceId, String url, String name) { + public static void openChannelFragment( + FragmentManager fragmentManager, + int serviceId, + String url, + String name) { if (name == null) name = ""; defaultTransaction(fragmentManager) .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, name)) @@ -322,7 +331,10 @@ public class NavigationHelper { .commit(); } - public static void openPlaylistFragment(FragmentManager fragmentManager, int serviceId, String url, String name) { + public static void openPlaylistFragment(FragmentManager fragmentManager, + int serviceId, + String url, + String name) { if (name == null) name = ""; defaultTransaction(fragmentManager) .replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, name)) @@ -370,10 +382,10 @@ public class NavigationHelper { // Through Intents //////////////////////////////////////////////////////////////////////////*/ - public static void openSearch(Context context, int serviceId, String query) { + public static void openSearch(Context context, int serviceId, String searchString) { Intent mIntent = new Intent(context, MainActivity.class); mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId); - mIntent.putExtra(Constants.KEY_QUERY, query); + mIntent.putExtra(Constants.KEY_SEARCH_STRING, searchString); mIntent.putExtra(Constants.KEY_OPEN_SEARCH, true); context.startActivity(mIntent); } @@ -467,7 +479,8 @@ public class NavigationHelper { switch (linkType) { case STREAM: - rIntent.putExtra(VideoDetailFragment.AUTO_PLAY, PreferenceManager.getDefaultSharedPreferences(context) + rIntent.putExtra(VideoDetailFragment.AUTO_PLAY, + PreferenceManager.getDefaultSharedPreferences(context) .getBoolean(context.getString(R.string.autoplay_through_intent_key), false)); break; } diff --git a/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java b/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java index d86f27f2f..7c781eb14 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java @@ -5,7 +5,6 @@ import android.preference.PreferenceManager; import android.support.annotation.DrawableRes; import android.support.annotation.StringRes; -import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.ServiceList; @@ -31,6 +30,18 @@ public class ServiceHelper { } } + public static String getTranslatedFilterString(String filter, Context c) { + switch(filter) { + case "all": return c.getString(R.string.all); + case "videos": return c.getString(R.string.videos); + case "channels": return c.getString(R.string.channels); + case "playlists": return c.getString(R.string.playlists); + case "tracks": return c.getString(R.string.tracks); + case "users": return c.getString(R.string.users); + default: return filter; + } + } + /** * Get a resource string with instructions for importing subscriptions for each service. * diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8686d21d3..502483667 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -125,7 +125,12 @@ Error report All Channel + Channels Playlist + Playlists + Videos + Tracks + Users Yes Later Disabled diff --git a/build.gradle b/build.gradle index a45c00aef..0448ed61e 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.2' + classpath 'com.android.tools.build:gradle:3.1.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files