diff --git a/app/src/main/java/org/schabi/newpipe/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/ErrorActivity.java index 5661f30bd..feca3e906 100644 --- a/app/src/main/java/org/schabi/newpipe/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/ErrorActivity.java @@ -276,7 +276,7 @@ public class ErrorActivity extends AppCompatActivity { returnActivity.isAssignableFrom(Activity.class)) { intent = new Intent(this, returnActivity); } else { - intent = new Intent(this, VideoItemListActivity.class); + intent = new Intent(this, MainActivity.class); } intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this, intent); diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 1a7112a65..634b12e15 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -49,7 +49,7 @@ public class MainActivity extends AppCompatActivity { switch(id) { case android.R.id.home: { - Intent intent = new Intent(this, VideoItemListActivity.class); + Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this, intent); return true; diff --git a/app/src/main/java/org/schabi/newpipe/SuggestionListAdapter.java b/app/src/main/java/org/schabi/newpipe/SuggestionListAdapter.java deleted file mode 100644 index 599cb66ee..000000000 --- a/app/src/main/java/org/schabi/newpipe/SuggestionListAdapter.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.schabi.newpipe; - -import android.content.Context; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import java.util.List; - -/** - * Created by Madiyar on 23.02.2016. - * - * Copyright (C) Christian Schabesberger 2015 - * SuggestionListAdapter.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 . - */ - -public class SuggestionListAdapter extends CursorAdapter { - - private String[] columns = new String[]{"_id", "title"}; - - public SuggestionListAdapter(Context context) { - super(context, null, false); - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - ViewHolder viewHolder; - - View view = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent, false); - viewHolder = new ViewHolder(); - viewHolder.suggestionTitle = (TextView) view.findViewById(android.R.id.text1); - view.setTag(viewHolder); - - - return view; - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - ViewHolder viewHolder = (ViewHolder) view.getTag(); - viewHolder.suggestionTitle.setText(cursor.getString(1)); - } - - - public void updateAdapter(List suggestions) { - MatrixCursor cursor = new MatrixCursor(columns); - int i = 0; - for (String s : suggestions) { - String[] temp = new String[2]; - temp[0] = Integer.toString(i); - temp[1] = s; - i++; - cursor.addRow(temp); - } - changeCursor(cursor); - } - - public String getSuggestion(int position) { - return ((Cursor) getItem(position)).getString(1); - } - - private class ViewHolder { - public TextView suggestionTitle; - } -} diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemDetailActivity.java b/app/src/main/java/org/schabi/newpipe/VideoItemDetailActivity.java index 3089741a5..dc073de57 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemDetailActivity.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemDetailActivity.java @@ -138,7 +138,7 @@ public class VideoItemDetailActivity extends AppCompatActivity { // http://developer.android.com/design/patterns/navigation.html#up-vs-back - Intent intent = new Intent(this, VideoItemListActivity.class); + Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this, intent); return true; diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java index f0a5ef144..3adaa3cf8 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java @@ -199,11 +199,11 @@ public class VideoItemDetailFragment extends Fragment { } catch(StreamInfo.StreamExctractException e) { if(!streamInfo.errors.isEmpty()) { // !!! if this case ever kicks in someone gets kicked out !!! - ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null, + ErrorActivity.reportError(h, getActivity(), e, VideoItemDetailFragment.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); } else { - ErrorActivity.reportError(h, getActivity(), streamInfo.errors, VideoItemListActivity.class, null, + ErrorActivity.reportError(h, getActivity(), streamInfo.errors, VideoItemDetailFragment.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream)); } @@ -215,7 +215,7 @@ public class VideoItemDetailFragment extends Fragment { }); e.printStackTrace(); } catch (ParsingException e) { - ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null, + ErrorActivity.reportError(h, getActivity(), e, VideoItemDetailFragment.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, service.getServiceInfo().name, videoUrl, R.string.parsing_error)); h.post(new Runnable() { @@ -226,7 +226,7 @@ public class VideoItemDetailFragment extends Fragment { }); e.printStackTrace(); } catch(Exception e) { - ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null, + ErrorActivity.reportError(h, getActivity(), e, VideoItemDetailFragment.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, service.getServiceInfo().name, videoUrl, R.string.general_error)); h.post(new Runnable() { diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java b/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java deleted file mode 100644 index 71c10cdaf..000000000 --- a/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java +++ /dev/null @@ -1,411 +0,0 @@ -package org.schabi.newpipe; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.media.AudioManager; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.support.v4.app.NavUtils; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.SearchView; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.widget.Toast; - -import org.schabi.newpipe.download.MainActivity; -import org.schabi.newpipe.extractor.ExtractionException; -import org.schabi.newpipe.extractor.SearchEngine; -import org.schabi.newpipe.extractor.ServiceList; -import org.schabi.newpipe.extractor.StreamingService; - -import java.io.IOException; -import java.util.List; -import java.util.Vector; - -/** - * Copyright (C) Christian Schabesberger 2015 - * VideoItemListActivity.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 . - */ - -public class VideoItemListActivity extends AppCompatActivity - implements VideoItemListFragment.Callbacks { - - private static final String TAG = VideoItemListFragment.class.toString(); - - // arguments to give to this activity - public static final String VIDEO_INFO_ITEMS = "video_info_items"; - - // savedInstanceBundle arguments - private static final String QUERY = "query"; - private static final String STREAMING_SERVICE = "streaming_service"; - - // activity modes - private static final int SEARCH_MODE = 0; - private static final int PRESENT_VIDEOS_MODE = 1; - - private int mode; - private int currentStreamingServiceId = -1; - private String searchQuery = ""; - - private VideoItemListFragment listFragment; - private VideoItemDetailFragment videoFragment; - private Menu menu; - - private SuggestionListAdapter suggestionListAdapter; - private SuggestionSearchRunnable suggestionSearchRunnable; - private Thread searchThread; - - private class SearchVideoQueryListener implements SearchView.OnQueryTextListener { - - @Override - public boolean onQueryTextSubmit(String query) { - try { - searchQuery = query; - listFragment.search(query); - - // hide virtual keyboard - InputMethodManager inputManager = - (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - try { - //noinspection ConstantConditions - inputManager.hideSoftInputFromWindow( - getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); - } catch(NullPointerException e) { - Log.e(TAG, "Could not get widget with focus"); - Toast.makeText(VideoItemListActivity.this, "Could not get widget with focus", - Toast.LENGTH_SHORT).show(); - e.printStackTrace(); - } - // clear focus - // 1. to not open up the keyboard after switching back to this - // 2. It's a workaround to a seeming bug by the Android OS it self, causing - // onQueryTextSubmit to trigger twice when focus is not cleared. - // See: http://stackoverflow.com/questions/17874951/searchview-onquerytextsubmit-runs-twice-while-i-pressed-once - getCurrentFocus().clearFocus(); - } catch(Exception e) { - e.printStackTrace(); - } - View bg = findViewById(R.id.mainBG); - bg.setVisibility(View.GONE); - return true; - } - - @Override - public boolean onQueryTextChange(String newText) { - if(!newText.isEmpty()) { - searchSuggestions(newText); - } - return true; - } - - } - private class SearchSuggestionListener implements SearchView.OnSuggestionListener{ - - private SearchView searchView; - - private SearchSuggestionListener(SearchView searchView) { - this.searchView = searchView; - } - - @Override - public boolean onSuggestionSelect(int position) { - String suggestion = suggestionListAdapter.getSuggestion(position); - searchView.setQuery(suggestion,true); - return false; - } - - @Override - public boolean onSuggestionClick(int position) { - String suggestion = suggestionListAdapter.getSuggestion(position); - searchView.setQuery(suggestion,true); - return false; - } - } - - private class SuggestionResultRunnable implements Runnable{ - - private List suggestions; - - private SuggestionResultRunnable(List suggestions) { - this.suggestions = suggestions; - } - - @Override - public void run() { - suggestionListAdapter.updateAdapter(suggestions); - } - } - - private class SuggestionSearchRunnable implements Runnable{ - private final int serviceId; - private final String query; - final Handler h = new Handler(); - private Context context; - private SuggestionSearchRunnable(int serviceId, String query) { - this.serviceId = serviceId; - this.query = query; - context = VideoItemListActivity.this; - } - - @Override - public void run() { - try { - SearchEngine engine = - ServiceList.getService(serviceId).getSearchEngineInstance(new Downloader()); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - String searchLanguageKey = context.getString(R.string.search_language_key); - String searchLanguage = sp.getString(searchLanguageKey, - getString(R.string.default_language_value)); - List suggestions = engine.suggestionList(query,searchLanguage,new Downloader()); - h.post(new SuggestionResultRunnable(suggestions)); - } catch (ExtractionException e) { - ErrorActivity.reportError(h, VideoItemListActivity.this, e, null, findViewById(R.id.videoitem_list), - ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, - ServiceList.getNameOfService(serviceId), query, R.string.parsing_error)); - e.printStackTrace(); - } catch (IOException e) { - postNewErrorToast(h, R.string.network_error); - e.printStackTrace(); - } catch (Exception e) { - ErrorActivity.reportError(h, VideoItemListActivity.this, e, null, findViewById(R.id.videoitem_list), - ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, - ServiceList.getNameOfService(serviceId), query, R.string.general_error)); - } - } - } - /** - * Whether or not the activity is in two-pane mode, i.e. running on a tablet - * device. - */ - private boolean mTwoPane; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_videoitem_list); - StreamingService streamingService = null; - - View bg = findViewById(R.id.mainBG); - bg.setVisibility(View.VISIBLE); - - try { - //------ todo: remove this line when multiservice support is implemented ------ - currentStreamingServiceId = ServiceList.getIdOfService("Youtube"); - streamingService = ServiceList.getService(currentStreamingServiceId); - } catch (Exception e) { - e.printStackTrace(); - ErrorActivity.reportError(VideoItemListActivity.this, e, null, findViewById(R.id.videoitem_list), - ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, - ServiceList.getNameOfService(currentStreamingServiceId), "", R.string.general_error)); - } - setVolumeControlStream(AudioManager.STREAM_MUSIC); - listFragment = (VideoItemListFragment) getSupportFragmentManager() - .findFragmentById(R.id.videoitem_list); - listFragment.setStreamingService(streamingService); - - if(savedInstanceState != null - && mode != PRESENT_VIDEOS_MODE) { - searchQuery = savedInstanceState.getString(QUERY); - currentStreamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE); - if(!searchQuery.isEmpty()) { - listFragment.search(searchQuery); - } - } - - if (findViewById(R.id.videoitem_detail_container) != null) { - // The detail container view will be present only in the - // large-screen layouts (res/values-large and - // res/values-sw600dp). If this view is present, then the - // activity should be in two-pane mode. - mTwoPane = true; - - // In two-pane mode, list items should be given the - // 'activated' state when touched. - - ((VideoItemListFragment) getSupportFragmentManager() - .findFragmentById(R.id.videoitem_list)) - .setActivateOnItemClick(true); - - SearchView searchView = (SearchView)findViewById(R.id.searchViewTablet); - if(mode != PRESENT_VIDEOS_MODE) { - // Somehow the seticonifiedbydefault property set by the layout xml is not working on - // the support version on SearchView, so it needs to be set programmatically. - searchView.setIconifiedByDefault(false); - searchView.setIconified(false); - if(!searchQuery.isEmpty()) { - searchView.setQuery(searchQuery,false); - } - searchView.setOnQueryTextListener(new SearchVideoQueryListener()); - suggestionListAdapter = new SuggestionListAdapter(this); - searchView.setSuggestionsAdapter(suggestionListAdapter); - searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView)); - } else { - searchView.setVisibility(View.GONE); - } - } - - PreferenceManager.setDefaultValues(this, R.xml.settings, false); - } - - @Override - public void onResume() { - super.onResume(); - App.checkStartTor(this); - } - - /** - * Callback method from {@link VideoItemListFragment.Callbacks} - * indicating that the item with the given ID was selected. - */ - @Override - public void onItemSelected(String id) { - VideoListAdapter listAdapter = (VideoListAdapter) ((VideoItemListFragment) - getSupportFragmentManager() - .findFragmentById(R.id.videoitem_list)) - .getListAdapter(); - String webpageUrl = listAdapter.getVideoList().get((int) Long.parseLong(id)).webpage_url; - if (mTwoPane) { - // In two-pane mode, show the detail view in this activity by - // adding or replacing the detail fragment using a - // fragment transaction. - Bundle arguments = new Bundle(); - //arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id); - arguments.putString(VideoItemDetailFragment.VIDEO_URL, webpageUrl); - arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId); - videoFragment = new VideoItemDetailFragment(); - videoFragment.setArguments(arguments); - videoFragment.setOnInvokeCreateOptionsMenuListener(new VideoItemDetailFragment.OnInvokeCreateOptionsMenuListener() { - @Override - public void createOptionsMenu() { - menu.clear(); - onCreateOptionsMenu(menu); - } - }); - getSupportFragmentManager().beginTransaction() - .replace(R.id.videoitem_detail_container, videoFragment) - .commit(); - } else { - // In single-pane mode, simply start the detail activity - // for the selected item ID. - Intent detailIntent = new Intent(this, VideoItemDetailActivity.class); - //detailIntent.putExtra(VideoItemDetailFragment.ARG_ITEM_ID, id); - detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, webpageUrl); - detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId); - startActivity(detailIntent); - } - } - - - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - this.menu = menu; - MenuInflater inflater = getMenuInflater(); - if(mode != PRESENT_VIDEOS_MODE && - findViewById(R.id.videoitem_detail_container) == null) { - inflater.inflate(R.menu.videoitem_list, menu); - MenuItem searchItem = menu.findItem(R.id.action_search); - SearchView searchView = (SearchView) searchItem.getActionView(); - searchView.setFocusable(false); - searchView.setOnQueryTextListener( - new SearchVideoQueryListener()); - suggestionListAdapter = new SuggestionListAdapter(this); - searchView.setSuggestionsAdapter(suggestionListAdapter); - searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView)); - if(!searchQuery.isEmpty()) { - searchView.setQuery(searchQuery,false); - searchView.setIconifiedByDefault(false); - } - } else if (videoFragment != null){ - videoFragment.onCreateOptionsMenu(menu, inflater); - } else { - inflater.inflate(R.menu.videoitem_two_pannel, menu); - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - - switch(id) { - case android.R.id.home: { - Intent intent = new Intent(this, VideoItemListActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - NavUtils.navigateUpTo(this, intent); - return true; - } - case R.id.action_settings: { - Intent intent = new Intent(this, SettingsActivity.class); - startActivity(intent); - return true; - } - case R.id.action_report_error: { - ErrorActivity.reportError(VideoItemListActivity.this, new Vector(), - null, null, - ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT, - ServiceList.getNameOfService(currentStreamingServiceId), - "user_report", R.string.user_report)); - return true; - } - case R.id.action_show_downloads: { - Intent intent = new Intent(this, org.schabi.newpipe.download.MainActivity.class); - startActivity(intent); - return true; - } - default: - return videoFragment.onOptionsItemSelected(item) || - super.onOptionsItemSelected(item); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - /* - if (mActivatedPosition != ListView.INVALID_POSITION) { - // Serialize and persist the activated item position. - outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition); - } - */ - outState.putString(QUERY, searchQuery); - outState.putInt(STREAMING_SERVICE, currentStreamingServiceId); - } - - private void searchSuggestions(String query) { - suggestionSearchRunnable = - new SuggestionSearchRunnable(currentStreamingServiceId, query); - searchThread = new Thread(suggestionSearchRunnable); - searchThread.start(); - - } - - private void postNewErrorToast(Handler h, final int stringResource) { - h.post(new Runnable() { - @Override - public void run() { - Toast.makeText(VideoItemListActivity.this, getString(stringResource), - Toast.LENGTH_SHORT).show(); - } - }); - } -} diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java deleted file mode 100644 index 2b9d57658..000000000 --- a/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java +++ /dev/null @@ -1,371 +0,0 @@ -package org.schabi.newpipe; - -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.support.v4.app.ListFragment; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.AbsListView; -import android.widget.ListView; -import android.widget.Toast; - -import java.io.IOException; -import java.util.List; - -import org.schabi.newpipe.extractor.ExtractionException; -import org.schabi.newpipe.extractor.SearchResult; -import org.schabi.newpipe.extractor.StreamPreviewInfo; -import org.schabi.newpipe.extractor.SearchEngine; -import org.schabi.newpipe.extractor.StreamingService; - - -/** - * Copyright (C) Christian Schabesberger 2015 - * VideoItemListFragment.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 . - */ - -public class VideoItemListFragment extends ListFragment { - - private static final String TAG = VideoItemListFragment.class.toString(); - - private StreamingService streamingService; - private VideoListAdapter videoListAdapter; - - // activity modes - private static final int SEARCH_MODE = 0; - private static final int PRESENT_VIDEOS_MODE = 1; - - private int mode; - private String query = ""; - private int lastPage; - - private Thread searchThread; - private SearchRunnable searchRunnable; - // used to track down if results posted by threads ar still valid - private int currentRequestId = -1; - private ListView list; - - private View footer; - - // used to suppress request for loading a new page while another page is already loading. - private boolean loadingNextPage = true; - - private class ResultRunnable implements Runnable { - private final SearchResult result; - private final int requestId; - public ResultRunnable(SearchResult result, int requestId) { - this.result = result; - this.requestId = requestId; - } - @Override - public void run() { - updateListOnResult(result, requestId); - if (android.os.Build.VERSION.SDK_INT >= 19) { - getListView().removeFooterView(footer); - } - } - } - - private class SearchRunnable implements Runnable { - public static final String YOUTUBE = "Youtube"; - private final SearchEngine engine; - private final String query; - private final int page; - final Handler h = new Handler(); - private volatile boolean runs = true; - private final int requestId; - public SearchRunnable(SearchEngine engine, String query, int page, int requestId) { - this.engine = engine; - this.query = query; - this.page = page; - this.requestId = requestId; - } - void terminate() { - runs = false; - } - @Override - public void run() { - SearchResult result = null; - try { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); - String searchLanguageKey = getContext().getString(R.string.search_language_key); - String searchLanguage = sp.getString(searchLanguageKey, - getString(R.string.default_language_value)); - result = SearchResult - .getSearchResult(engine, query, page, searchLanguage, new Downloader()); - - if(runs) { - h.post(new ResultRunnable(result, requestId)); - } - - // look for errors during extraction - // soft errors: - if(result != null && - !result.errors.isEmpty()) { - Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:"); - for(Throwable e : result.errors) { - e.printStackTrace(); - Log.e(TAG, "------"); - } - - Activity a = getActivity(); - View rootView = a.findViewById(R.id.videoitem_list); - ErrorActivity.reportError(h, getActivity(), result.errors, null, rootView, - ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, - /* todo: this shoudl not be assigned static */ YOUTUBE, query, R.string.light_parsing_error)); - - } - // hard errors: - } catch(IOException e) { - postNewNothingFoundToast(h, R.string.network_error); - e.printStackTrace(); - } catch(SearchEngine.NothingFoundException e) { - postNewErrorToast(h, e.getMessage()); - } catch(ExtractionException e) { - ErrorActivity.reportError(h, getActivity(), e, null, null, - ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, - /* todo: this shoudl not be assigned static */ - YOUTUBE, query, R.string.parsing_error)); - //postNewErrorToast(h, R.string.parsing_error); - e.printStackTrace(); - - } catch(Exception e) { - ErrorActivity.reportError(h, getActivity(), e, null, null, - ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, - /* todo: this shoudl not be assigned static */ YOUTUBE, query, R.string.general_error)); - - e.printStackTrace(); - } - } - } - - public void present(List videoList) { - mode = PRESENT_VIDEOS_MODE; - setListShown(true); - getListView().smoothScrollToPosition(0); - - updateList(videoList); - } - - public void search(String query) { - mode = SEARCH_MODE; - this.query = query; - this.lastPage = 1; - videoListAdapter.clearVideoList(); - setListShown(false); - startSearch(query, lastPage); - //todo: Somehow this command is not working on older devices, - // although it was introduced with API level 8. Test this and find a solution. - getListView().smoothScrollToPosition(0); - } - - private void nextPage() { - loadingNextPage = true; - lastPage++; - Log.d(TAG, getString(R.string.search_page) + Integer.toString(lastPage)); - startSearch(query, lastPage); - } - - private void startSearch(String query, int page) { - currentRequestId++; - terminateThreads(); - searchRunnable = new SearchRunnable(streamingService.getSearchEngineInstance(new Downloader()), - query, page, currentRequestId); - searchThread = new Thread(searchRunnable); - searchThread.start(); - } - - public void setStreamingService(StreamingService streamingService) { - this.streamingService = streamingService; - } - - private void updateListOnResult(SearchResult result, int requestId) { - if(requestId == currentRequestId) { - setListShown(true); - updateList(result.resultList); - if(!result.suggestion.isEmpty()) { - - Toast.makeText(getActivity(), - String.format(getActivity().getString(R.string.did_you_mean), result.suggestion), - Toast.LENGTH_LONG).show(); - } - } - } - - private void updateList(List list) { - try { - videoListAdapter.addVideoList(list); - terminateThreads(); - } catch(java.lang.IllegalStateException e) { - Toast.makeText(getActivity(), "Trying to set value while activity doesn't exist anymore.", - Toast.LENGTH_SHORT).show(); - Log.w(TAG, "Trying to set value while activity doesn't exist anymore."); - } catch(Exception e) { - Toast.makeText(getActivity(), getString(R.string.general_error), - Toast.LENGTH_SHORT).show(); - e.printStackTrace(); - } finally { - loadingNextPage = false; - } - } - - private void terminateThreads() { - if(searchThread != null) { - searchRunnable.terminate(); - // No need to join, since we don't really terminate the thread. We just demand - // it to post its result runnable into the gui main loop. - } - } - - /** - * The serialization (saved instance state) Bundle key representing the - * activated item position. Only used on tablets. - */ - private static final String STATE_ACTIVATED_POSITION = "activated_position"; - - /** - * The current activated item position. Only used on tablets. - */ - private int mActivatedPosition = ListView.INVALID_POSITION; - - /** - * A callback interface that all activities containing this fragment must - * implement. This mechanism allows activities to be notified of item - * selections. - */ - public interface Callbacks { - /** - * Callback for when an item has been selected. - */ - void onItemSelected(String id); - } - - private Callbacks mCallbacks; - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - list = getListView(); - videoListAdapter = new VideoListAdapter(getActivity(), this); - footer = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)) - .inflate(R.layout.paginate_footer, null, false); - - - setListAdapter(videoListAdapter); - - // Restore the previously serialized activated item position. - if (savedInstanceState != null - - && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) { - setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION)); - } - - - getListView().setOnScrollListener(new AbsListView.OnScrollListener() { - long lastScrollDate; - - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - } - - @Override - public void onScroll(AbsListView view, int firstVisibleItem, - int visibleItemCount, int totalItemCount) { - if (mode != PRESENT_VIDEOS_MODE - && list.getChildAt(0) != null - && list.getLastVisiblePosition() == list.getAdapter().getCount() - 1 - && list.getChildAt(list.getChildCount() - 1).getBottom() >= list.getHeight()) { - long time = System.currentTimeMillis(); - if ((time - lastScrollDate) > 200 - && !loadingNextPage) { - lastScrollDate = time; - getListView().addFooterView(footer); - nextPage(); - } - } - } - - }); - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - - // Activities containing this fragment must implement its callbacks. - if (!(context instanceof Callbacks)) { - throw new IllegalStateException("Activity must implement fragment's callbacks."); - } - - mCallbacks = (Callbacks) context; - } - - @Override - public void onListItemClick(ListView listView, View view, int position, long id) { - super.onListItemClick(listView, view, position, id); - setActivatedPosition(position); - mCallbacks.onItemSelected(Long.toString(id)); - } - - /** - * Turns on activate-on-click mode. When this mode is on, list items will be - * given the 'activated' state when touched. - */ - public void setActivateOnItemClick(@SuppressWarnings("SameParameterValue") boolean activateOnItemClick) { - // When setting CHOICE_MODE_SINGLE, ListView will automatically - // give items the 'activated' state when touched. - getListView().setChoiceMode(activateOnItemClick - ? ListView.CHOICE_MODE_SINGLE - : ListView.CHOICE_MODE_NONE); - } - - private void setActivatedPosition(int position) { - if (position == ListView.INVALID_POSITION) { - getListView().setItemChecked(mActivatedPosition, false); - } else { - getListView().setItemChecked(position, true); - } - - mActivatedPosition = position; - } - - private void postNewErrorToast(Handler h, final String message) { - h.post(new Runnable() { - @Override - public void run() { - setListShown(true); - Toast.makeText(getActivity(), message, - Toast.LENGTH_SHORT).show(); - } - }); - } - - private void postNewNothingFoundToast(Handler h, final int stringResource) { - h.post(new Runnable() { - @Override - public void run() { - setListShown(true); - Toast.makeText(getActivity(), getString(stringResource), - Toast.LENGTH_LONG).show(); - } - }); - } -} diff --git a/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java b/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java deleted file mode 100644 index 1464d86b9..000000000 --- a/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.schabi.newpipe; - -import android.app.Activity; -import android.content.Context; -import android.support.v4.content.ContextCompat; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListView; - -import org.schabi.newpipe.extractor.StreamPreviewInfo; - -import java.util.List; -import java.util.Vector; - -/** - * Created by Christian Schabesberger on 11.08.15. - * - * Copyright (C) Christian Schabesberger 2015 - * VideoListAdapter.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 . - */ - -class VideoListAdapter extends BaseAdapter { - private final Context context; - private final StreamInfoItemViewCreator viewCreator; - private Vector videoList = new Vector<>(); - private final ListView listView; - - public VideoListAdapter(Activity activity, VideoItemListFragment videoListFragment) { - viewCreator = new StreamInfoItemViewCreator(LayoutInflater.from(activity), activity, null); - this.listView = videoListFragment.getListView(); - this.listView.setDivider(null); - this.listView.setDividerHeight(0); - this.context = activity; - } - - public void addVideoList(List videos) { - videoList.addAll(videos); - notifyDataSetChanged(); - } - - public void clearVideoList() { - videoList = new Vector<>(); - notifyDataSetChanged(); - } - - public Vector getVideoList() { - return videoList; - } - - @Override - public int getCount() { - return videoList.size(); - } - - @Override - public Object getItem(int position) { - return videoList.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - convertView = viewCreator.getViewFromVideoInfoItem(convertView, parent, videoList.get(position)); - - if(listView.isItemChecked(position)) { - convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.light_youtube_primary_color)); - } else { - convertView.setBackgroundColor(0); - } - - return convertView; - } -} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/download/MainActivity.java b/app/src/main/java/org/schabi/newpipe/download/MainActivity.java index 953289cb6..a3075f2e8 100644 --- a/app/src/main/java/org/schabi/newpipe/download/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/download/MainActivity.java @@ -23,25 +23,16 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewTreeObserver; import android.widget.AdapterView; -import android.widget.Button; import android.widget.EditText; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; -import android.support.v7.widget.SearchView; import org.schabi.newpipe.ErrorActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.SettingsActivity; -import org.schabi.newpipe.VideoItemDetailActivity; -import org.schabi.newpipe.VideoItemListActivity; -import org.schabi.newpipe.extractor.ServiceList; import java.io.File; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; import java.util.Vector; import us.shandian.giga.get.DownloadManager; @@ -257,7 +248,7 @@ public class MainActivity extends AppCompatActivity implements AdapterView.OnIte switch (id) { case android.R.id.home: { - Intent intent = new Intent(this, VideoItemListActivity.class); + Intent intent = new Intent(this, org.schabi.newpipe.MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this, intent); return true; diff --git a/app/src/main/java/org/schabi/newpipe/search_fragment/SearchInfoItemFragment.java b/app/src/main/java/org/schabi/newpipe/search_fragment/SearchInfoItemFragment.java index aae87e1a4..07198b3ce 100644 --- a/app/src/main/java/org/schabi/newpipe/search_fragment/SearchInfoItemFragment.java +++ b/app/src/main/java/org/schabi/newpipe/search_fragment/SearchInfoItemFragment.java @@ -2,10 +2,9 @@ package org.schabi.newpipe.search_fragment; import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.os.Bundle; -import android.os.Handler; import android.support.v4.app.Fragment; -import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView; @@ -16,10 +15,13 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; +import android.widget.LinearLayout; import android.widget.Toast; import org.schabi.newpipe.ErrorActivity; import org.schabi.newpipe.R; +import org.schabi.newpipe.VideoItemDetailActivity; +import org.schabi.newpipe.VideoItemDetailFragment; import org.schabi.newpipe.extractor.SearchResult; import org.schabi.newpipe.extractor.ServiceList; @@ -78,17 +80,16 @@ public class SearchInfoItemFragment extends Fragment { } } - // TODO: Customize parameter argument names - private static final String ARG_COLUMN_COUNT = "column-count"; - // TODO: Customize parameters - private int mColumnCount = 1; - private int streamingServiceId = -1; private String searchQuery = ""; + private boolean isLoading = false; private SearchView searchView = null; + private int pageNumber = 0; private SuggestionListAdapter suggestionListAdapter = null; private StreamInfoListAdapter streamInfoListAdapter = null; + private LinearLayoutManager streamInfoListLayoutManager = null; + private RecyclerView recyclerView = null; // savedInstanceBundle arguments private static final String QUERY = "query"; @@ -105,9 +106,6 @@ public class SearchInfoItemFragment extends Fragment { @SuppressWarnings("unused") public static SearchInfoItemFragment newInstance(int columnCount) { SearchInfoItemFragment fragment = new SearchInfoItemFragment(); - Bundle args = new Bundle(); - args.putInt(ARG_COLUMN_COUNT, columnCount); - fragment.setArguments(args); return fragment; } @@ -115,10 +113,6 @@ public class SearchInfoItemFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (getArguments() != null) { - mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT); - } - if(savedInstanceState != null) { searchQuery = savedInstanceState.getString(QUERY); streamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE); @@ -140,6 +134,7 @@ public class SearchInfoItemFragment extends Fragment { @Override public void onResult(SearchResult result) { streamInfoListAdapter.addVideoList(result.resultList); + isLoading = false; } @Override @@ -147,6 +142,7 @@ public class SearchInfoItemFragment extends Fragment { //setListShown(true); Toast.makeText(getActivity(), getString(stringResource), Toast.LENGTH_SHORT).show(); + isLoading = false; } @Override @@ -154,6 +150,7 @@ public class SearchInfoItemFragment extends Fragment { //setListShown(true); Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show(); + isLoading = false; } }); } @@ -166,16 +163,43 @@ public class SearchInfoItemFragment extends Fragment { // Set the adapter if (view instanceof RecyclerView) { Context context = view.getContext(); - RecyclerView recyclerView = (RecyclerView) view; - if (mColumnCount <= 1) { - recyclerView.setLayoutManager(new LinearLayoutManager(context)); - } else { - recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount)); - } + recyclerView = (RecyclerView) view; + streamInfoListLayoutManager = new LinearLayoutManager(context); + recyclerView.setLayoutManager(streamInfoListLayoutManager); streamInfoListAdapter = new StreamInfoListAdapter(getActivity(), getActivity().findViewById(android.R.id.content)); + streamInfoListAdapter.setOnItemSelectedListener(new StreamInfoListAdapter.OnItemSelectedListener() { + @Override + public void selected(String url) { + Intent i = new Intent(getActivity(), VideoItemDetailActivity.class); + i.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); + i.putExtra(VideoItemDetailFragment.VIDEO_URL, url); + getActivity().startActivity(i); + } + }); recyclerView.setAdapter(streamInfoListAdapter); + + + recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + int pastVisiblesItems, visibleItemCount, totalItemCount; + super.onScrolled(recyclerView, dx, dy); + if(dy > 0) //check for scroll down + { + visibleItemCount = streamInfoListLayoutManager.getChildCount(); + totalItemCount = streamInfoListLayoutManager.getItemCount(); + pastVisiblesItems = streamInfoListLayoutManager.findFirstVisibleItemPosition(); + + if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount && !isLoading) + { + pageNumber++; + search(searchQuery, pageNumber); + } + } + } + }); } return view; @@ -219,10 +243,12 @@ public class SearchInfoItemFragment extends Fragment { private void search(String query) { streamInfoListAdapter.clearVideoList(); - search(query, 0); + pageNumber = 0; + search(query, pageNumber); } private void search(String query, int page) { + isLoading = true; SearchWorker sw = SearchWorker.getInstance(); sw.search(streamingServiceId, query, page, getActivity()); } @@ -233,22 +259,4 @@ public class SearchInfoItemFragment extends Fragment { Thread suggestionThread = new Thread(suggestionSearchRunnable); suggestionThread.start(); } - - private void postNewErrorToast(Handler h, final int stringResource) { - h.post(new Runnable() { - @Override - public void run() { - - } - }); - } - - private void postNewNothingFoundToast(Handler h, final int stringResource) { - h.post(new Runnable() { - @Override - public void run() { - - } - }); - } } diff --git a/app/src/main/java/org/schabi/newpipe/search_fragment/SearchWorker.java b/app/src/main/java/org/schabi/newpipe/search_fragment/SearchWorker.java index 3e7464333..e4b53344c 100644 --- a/app/src/main/java/org/schabi/newpipe/search_fragment/SearchWorker.java +++ b/app/src/main/java/org/schabi/newpipe/search_fragment/SearchWorker.java @@ -161,9 +161,8 @@ public class SearchWorker { public void search(int serviceId, String query, int page, Activity a) { if(runnable != null) { terminate(); - } else { - runnable = new SearchRunnable(serviceId, query, page, a, requestId); } + runnable = new SearchRunnable(serviceId, query, page, a, requestId); Thread thread = new Thread(runnable); thread.start(); } diff --git a/app/src/main/java/org/schabi/newpipe/StreamInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/search_fragment/StreamInfoItemHolder.java similarity index 86% rename from app/src/main/java/org/schabi/newpipe/StreamInfoItemHolder.java rename to app/src/main/java/org/schabi/newpipe/search_fragment/StreamInfoItemHolder.java index 8d0df8f89..660fa1022 100644 --- a/app/src/main/java/org/schabi/newpipe/StreamInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/search_fragment/StreamInfoItemHolder.java @@ -1,10 +1,12 @@ -package org.schabi.newpipe; +package org.schabi.newpipe.search_fragment; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import org.schabi.newpipe.R; + /** * Created by the-scrabi on 01.08.16. */ @@ -16,6 +18,7 @@ public class StreamInfoItemHolder extends RecyclerView.ViewHolder { itemDurationView, itemUploadDateView, itemViewCountView; + public View mainLayout; public StreamInfoItemHolder(View v) { super(v); @@ -25,5 +28,6 @@ public class StreamInfoItemHolder extends RecyclerView.ViewHolder { itemDurationView = (TextView) v.findViewById(R.id.itemDurationView); itemUploadDateView = (TextView) v.findViewById(R.id.itemUploadDateView); itemViewCountView = (TextView) v.findViewById(R.id.itemViewCountView); + mainLayout = v.findViewById(R.id.item_main_layout); } } diff --git a/app/src/main/java/org/schabi/newpipe/search_fragment/StreamInfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/search_fragment/StreamInfoListAdapter.java index 112e706d1..89f006432 100644 --- a/app/src/main/java/org/schabi/newpipe/search_fragment/StreamInfoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/search_fragment/StreamInfoListAdapter.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.search_fragment; import android.app.Activity; -import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -12,7 +11,6 @@ import com.nostra13.universalimageloader.core.ImageLoader; import org.schabi.newpipe.ImageErrorLoadingListener; import org.schabi.newpipe.R; -import org.schabi.newpipe.StreamInfoItemHolder; import org.schabi.newpipe.StreamInfoItemViewCreator; import org.schabi.newpipe.extractor.AbstractVideoInfo; import org.schabi.newpipe.extractor.StreamPreviewInfo; @@ -25,18 +23,29 @@ import java.util.Vector; */ public class StreamInfoListAdapter extends RecyclerView.Adapter { + public interface OnItemSelectedListener { + void selected(String url); + } + private Activity activity = null; private View rootView = null; private List streamList = new Vector<>(); private ImageLoader imageLoader = ImageLoader.getInstance(); private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build(); + private OnItemSelectedListener onItemSelectedListener; + + StreamInfoListAdapter(Activity a, View rootView) { activity = a; this.rootView = rootView; } + public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) { + this.onItemSelectedListener = onItemSelectedListener; + } + public void addVideoList(List videos) { streamList.addAll(videos); notifyDataSetChanged(); @@ -62,7 +71,7 @@ public class StreamInfoListAdapter extends RecyclerView.Adapter -