From 3a5b9203d827370f78fff9e61ba9b110ca2479e4 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 2 Aug 2016 15:06:02 +0200 Subject: [PATCH] put search fragment into own package --- .../java/org/schabi/newpipe/MainActivity.java | 59 +++++ .../newpipe/SearchInfoItemFragment.java | 86 ------- .../schabi/newpipe/SuggestionListAdapter.java | 1 - .../schabi/newpipe/dummy/DummyContent.java | 72 ------ .../SearchInfoItemFragment.java | 224 ++++++++++++++++++ .../SearchSuggestionListener.java | 32 +++ .../SuggestionListAdapter.java | 66 ++++++ .../SuggestionSearchRunnable.java | 90 +++++++ app/src/main/res/layout/activity_main.xml | 2 +- .../res/layout/fragment_searchinfoitem.xml | 2 +- app/src/main/res/menu/main_menu.xml | 11 + app/src/main/res/menu/search_menu.xml | 9 + 12 files changed, 493 insertions(+), 161 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/SearchInfoItemFragment.java delete mode 100644 app/src/main/java/org/schabi/newpipe/dummy/DummyContent.java create mode 100644 app/src/main/java/org/schabi/newpipe/search_fragment/SearchInfoItemFragment.java create mode 100644 app/src/main/java/org/schabi/newpipe/search_fragment/SearchSuggestionListener.java create mode 100644 app/src/main/java/org/schabi/newpipe/search_fragment/SuggestionListAdapter.java create mode 100644 app/src/main/java/org/schabi/newpipe/search_fragment/SuggestionSearchRunnable.java create mode 100644 app/src/main/res/menu/main_menu.xml create mode 100644 app/src/main/res/menu/search_menu.xml diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 4fdaa31ff..1a7112a65 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -1,13 +1,72 @@ package org.schabi.newpipe; +import android.content.Intent; +import android.media.AudioManager; +import android.support.v4.app.Fragment; +import android.support.v4.app.NavUtils; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.support.v7.widget.SearchView; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import org.schabi.newpipe.extractor.ServiceList; + +import java.util.Vector; + +/** + * Created by Christian Schabesberger on 02.08.16. + */ public class MainActivity extends AppCompatActivity { + private Fragment mainFragment = null; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + + setVolumeControlStream(AudioManager.STREAM_MUSIC); + mainFragment = getSupportFragmentManager() + .findFragmentById(R.id.search_fragment); + } + + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + MenuInflater inflater = getMenuInflater(); + + inflater.inflate(R.menu.main_menu, menu); + + mainFragment.onCreateOptionsMenu(menu, inflater); + 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_show_downloads: { + Intent intent = new Intent(this, org.schabi.newpipe.download.MainActivity.class); + startActivity(intent); + return true; + } + default: + return mainFragment.onOptionsItemSelected(item) || + super.onOptionsItemSelected(item); + } } } diff --git a/app/src/main/java/org/schabi/newpipe/SearchInfoItemFragment.java b/app/src/main/java/org/schabi/newpipe/SearchInfoItemFragment.java deleted file mode 100644 index 373c009df..000000000 --- a/app/src/main/java/org/schabi/newpipe/SearchInfoItemFragment.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.schabi.newpipe; - -import android.content.Context; -import android.os.Bundle; -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.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import org.schabi.newpipe.dummy.DummyContent; -import org.schabi.newpipe.dummy.DummyContent.DummyItem; - -import java.util.List; - -/** - * A fragment representing a list of Items. - *

- * Activities containing this fragment MUST implement the {@link OnListFragmentInteractionListener} - * interface. - */ -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; - - /** - * Mandatory empty constructor for the fragment manager to instantiate the - * fragment (e.g. upon screen orientation changes). - */ - public SearchInfoItemFragment() { - } - - // TODO: Customize parameter initialization - @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; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (getArguments() != null) { - mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_searchinfoitem, container, false); - - // 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.setAdapter(null); - } - return view; - } - - - @Override - public void onAttach(Context context) { - super.onAttach(context); - } - - @Override - public void onDetach() { - super.onDetach(); - } -} diff --git a/app/src/main/java/org/schabi/newpipe/SuggestionListAdapter.java b/app/src/main/java/org/schabi/newpipe/SuggestionListAdapter.java index 10ba5efb0..599cb66ee 100644 --- a/app/src/main/java/org/schabi/newpipe/SuggestionListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/SuggestionListAdapter.java @@ -9,7 +9,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import java.util.ArrayList; import java.util.List; /** diff --git a/app/src/main/java/org/schabi/newpipe/dummy/DummyContent.java b/app/src/main/java/org/schabi/newpipe/dummy/DummyContent.java deleted file mode 100644 index 4b84286ae..000000000 --- a/app/src/main/java/org/schabi/newpipe/dummy/DummyContent.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.schabi.newpipe.dummy; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Helper class for providing sample content for user interfaces created by - * Android template wizards. - *

- * TODO: Replace all uses of this class before publishing your app. - */ -public class DummyContent { - - /** - * An array of sample (dummy) items. - */ - public static final List ITEMS = new ArrayList(); - - /** - * A map of sample (dummy) items, by ID. - */ - public static final Map ITEM_MAP = new HashMap(); - - private static final int COUNT = 25; - - static { - // Add some sample items. - for (int i = 1; i <= COUNT; i++) { - addItem(createDummyItem(i)); - } - } - - private static void addItem(DummyItem item) { - ITEMS.add(item); - ITEM_MAP.put(item.id, item); - } - - private static DummyItem createDummyItem(int position) { - return new DummyItem(String.valueOf(position), "Item " + position, makeDetails(position)); - } - - private static String makeDetails(int position) { - StringBuilder builder = new StringBuilder(); - builder.append("Details about Item: ").append(position); - for (int i = 0; i < position; i++) { - builder.append("\nMore details information here."); - } - return builder.toString(); - } - - /** - * A dummy item representing a piece of content. - */ - public static class DummyItem { - public final String id; - public final String content; - public final String details; - - public DummyItem(String id, String content, String details) { - this.id = id; - this.content = content; - this.details = details; - } - - @Override - public String toString() { - return content; - } - } -} 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 new file mode 100644 index 000000000..582a295e7 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/search_fragment/SearchInfoItemFragment.java @@ -0,0 +1,224 @@ +package org.schabi.newpipe.search_fragment; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.support.v4.app.Fragment; +import android.support.v4.widget.CursorAdapter; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.SearchView; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.TextView; +import android.widget.Toast; + +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.ErrorActivity; +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.ExtractionException; +import org.schabi.newpipe.extractor.SearchEngine; +import org.schabi.newpipe.extractor.ServiceList; + +import java.io.IOException; +import java.util.List; + +/** + * Created by Christian Schabesberger on 02.08.16. + */ + +public class SearchInfoItemFragment extends Fragment { + + private static final String TAG = SearchInfoItemFragment.class.toString(); + + public class SearchQueryListener implements SearchView.OnQueryTextListener { + + @Override + public boolean onQueryTextSubmit(String query) { + Activity a = getActivity(); + try { + searchQuery = query; + search(query); + + // hide virtual keyboard + InputMethodManager inputManager = + (InputMethodManager) a.getSystemService(Context.INPUT_METHOD_SERVICE); + try { + //noinspection ConstantConditions + inputManager.hideSoftInputFromWindow( + a.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } catch(NullPointerException e) { + e.printStackTrace(); + ErrorActivity.reportError(a, e, null, + a.findViewById(android.R.id.content), + ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, + ServiceList.getNameOfService(streamingServiceId), + "Could not get widget with focus", R.string.general_error)); + } + // 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 + a.getCurrentFocus().clearFocus(); + } catch(Exception e) { + e.printStackTrace(); + } + View bg = a.findViewById(R.id.mainBG); + bg.setVisibility(View.GONE); + return true; + } + + @Override + public boolean onQueryTextChange(String newText) { + if(!newText.isEmpty()) { + searchSuggestions(newText); + } + return true; + } + } + + // 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 SearchView searchView = null; + private SuggestionListAdapter suggestionListAdapter = null; + + // savedInstanceBundle arguments + private static final String QUERY = "query"; + private static final String STREAMING_SERVICE = "streaming_service"; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public SearchInfoItemFragment() { + } + + // TODO: Customize parameter initialization + @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; + } + + @Override + 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); + } else { + try { + streamingServiceId = ServiceList.getIdOfService("Youtube"); + } catch(Exception e) { + e.printStackTrace(); + ErrorActivity.reportError(getActivity(), e, null, + getActivity().findViewById(android.R.id.content), + ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, + ServiceList.getNameOfService(streamingServiceId), + "", R.string.general_error)); + } + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_searchinfoitem, container, false); + + // 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.setAdapter(null); + } + return view; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + } + + @Override + public void onDetach() { + super.onDetach(); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.search_menu, menu); + + MenuItem searchItem = menu.findItem(R.id.action_search); + searchView = (SearchView) searchItem.getActionView(); + setupSearchView(searchView); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + + private void setupSearchView(SearchView searchView) { + suggestionListAdapter = new SuggestionListAdapter(getActivity()); + searchView.setSuggestionsAdapter(suggestionListAdapter); + searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView, suggestionListAdapter)); + searchView.setOnQueryTextListener(new SearchQueryListener()); + if(!searchQuery.isEmpty()) { + searchView.setQuery(searchQuery, false); + searchView.setIconifiedByDefault(false); + } + } + + private void search(String query) { + + } + + private void searchSuggestions(String query) { + SuggestionSearchRunnable suggestionSearchRunnable = + new SuggestionSearchRunnable(streamingServiceId, query, getActivity(), suggestionListAdapter); + Thread suggestionThread = new Thread(suggestionSearchRunnable); + suggestionThread.start(); + } + + private void postNewErrorToast(Handler h, final int stringResource) { + h.post(new Runnable() { + @Override + public void run() { + Toast.makeText(getActivity(), getString(stringResource), + Toast.LENGTH_SHORT).show(); + } + }); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/search_fragment/SearchSuggestionListener.java b/app/src/main/java/org/schabi/newpipe/search_fragment/SearchSuggestionListener.java new file mode 100644 index 000000000..833af8521 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/search_fragment/SearchSuggestionListener.java @@ -0,0 +1,32 @@ +package org.schabi.newpipe.search_fragment; + +import android.support.v7.widget.SearchView; + +/** + * Created by the-scrabi on 02.08.16. + */ + +public class SearchSuggestionListener implements SearchView.OnSuggestionListener{ + + private SearchView searchView; + private SuggestionListAdapter adapter; + + public SearchSuggestionListener(SearchView searchView, SuggestionListAdapter adapter) { + this.searchView = searchView; + this.adapter = adapter; + } + + @Override + public boolean onSuggestionSelect(int position) { + String suggestion = adapter.getSuggestion(position); + searchView.setQuery(suggestion,true); + return false; + } + + @Override + public boolean onSuggestionClick(int position) { + String suggestion = adapter.getSuggestion(position); + searchView.setQuery(suggestion,true); + return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/search_fragment/SuggestionListAdapter.java b/app/src/main/java/org/schabi/newpipe/search_fragment/SuggestionListAdapter.java new file mode 100644 index 000000000..5a8ae2ce7 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/search_fragment/SuggestionListAdapter.java @@ -0,0 +1,66 @@ +package org.schabi.newpipe.search_fragment; + +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 the-scrabi on 02.08.16. + */ + +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; + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/search_fragment/SuggestionSearchRunnable.java b/app/src/main/java/org/schabi/newpipe/search_fragment/SuggestionSearchRunnable.java new file mode 100644 index 000000000..94308200d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/search_fragment/SuggestionSearchRunnable.java @@ -0,0 +1,90 @@ +package org.schabi.newpipe.search_fragment; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.widget.Toast; + +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.ErrorActivity; +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.ExtractionException; +import org.schabi.newpipe.extractor.SearchEngine; +import org.schabi.newpipe.extractor.ServiceList; + +import java.io.IOException; +import java.util.List; + +/** + * Created by the-scrabi on 02.08.16. + */ + +public class SuggestionSearchRunnable implements Runnable{ + + private class SuggestionResultRunnable implements Runnable{ + + private List suggestions; + private SuggestionListAdapter adapter; + + private SuggestionResultRunnable(List suggestions, SuggestionListAdapter adapter) { + this.suggestions = suggestions; + this.adapter = adapter; + } + + @Override + public void run() { + adapter.updateAdapter(suggestions); + } + } + + private final int serviceId; + private final String query; + final Handler h = new Handler(); + private Activity a = null; + private SuggestionListAdapter adapter; + public SuggestionSearchRunnable(int serviceId, String query, + Activity activity, SuggestionListAdapter adapter) { + this.serviceId = serviceId; + this.query = query; + this.a = activity; + this.adapter = adapter; + } + + @Override + public void run() { + try { + SearchEngine engine = + ServiceList.getService(serviceId).getSearchEngineInstance(new Downloader()); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(a); + String searchLanguageKey = a.getString(R.string.search_language_key); + String searchLanguage = sp.getString(searchLanguageKey, + a.getString(R.string.default_language_value)); + List suggestions = engine.suggestionList(query,searchLanguage,new Downloader()); + h.post(new SuggestionResultRunnable(suggestions, adapter)); + } catch (ExtractionException e) { + ErrorActivity.reportError(h, a, e, null, a.findViewById(android.R.id.content), + 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, a, e, null, a.findViewById(android.R.id.content), + ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, + ServiceList.getNameOfService(serviceId), query, R.string.general_error)); + } + } + + private void postNewErrorToast(Handler h, final int stringResource) { + h.post(new Runnable() { + @Override + public void run() { + Toast.makeText(a, a.getString(stringResource), + Toast.LENGTH_SHORT).show(); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f225e82fc..f35477c49 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -10,7 +10,7 @@ diff --git a/app/src/main/res/layout/fragment_searchinfoitem.xml b/app/src/main/res/layout/fragment_searchinfoitem.xml index b4b75ebfa..b948e0205 100644 --- a/app/src/main/res/layout/fragment_searchinfoitem.xml +++ b/app/src/main/res/layout/fragment_searchinfoitem.xml @@ -9,5 +9,5 @@ android:layout_marginLeft="16dp" android:layout_marginRight="16dp" app:layoutManager="LinearLayoutManager" - tools:context="org.schabi.newpipe.SearchInfoItemFragment" + tools:context=".search_fragment.SearchInfoItemFragment" tools:listitem="@layout/video_item" /> diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml new file mode 100644 index 000000000..b7e615bbc --- /dev/null +++ b/app/src/main/res/menu/main_menu.xml @@ -0,0 +1,11 @@ + +

+ + + + \ No newline at end of file diff --git a/app/src/main/res/menu/search_menu.xml b/app/src/main/res/menu/search_menu.xml new file mode 100644 index 000000000..79158063a --- /dev/null +++ b/app/src/main/res/menu/search_menu.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file