Implemented search field for PVR views
* Implemented search field for PVR views. (#695) * Implemented search field for PVRChannelEPGListFragment. (#695)
This commit is contained in:
parent
a3ed983fee
commit
dd53f621e5
|
@ -0,0 +1,212 @@
|
|||
package org.xbmc.kore.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.text.TextUtils;
|
||||
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.widget.EditText;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
||||
|
||||
public abstract class AbstractSearchableFragment extends Fragment implements SearchView.OnQueryTextListener {
|
||||
|
||||
private String searchFilter = null;
|
||||
private String savedSearchFilter;
|
||||
private boolean supportsSearch;
|
||||
private boolean isPaused;
|
||||
|
||||
private SearchView searchView;
|
||||
|
||||
private final String BUNDLE_KEY_SEARCH_QUERY = "search_query";
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View root = super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
savedSearchFilter = savedInstanceState.getString(BUNDLE_KEY_SEARCH_QUERY);
|
||||
}
|
||||
searchFilter = savedSearchFilter;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.abstractcursorlistfragment, menu);
|
||||
|
||||
if (supportsSearch) {
|
||||
setupSearchMenuItem(menu, inflater);
|
||||
}
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to indicate your fragment supports search queries.
|
||||
* Get the entered search query using {@link #getSearchFilter()}
|
||||
* <br/>
|
||||
* Note: make sure this is set before {@link #onCreateOptionsMenu(Menu, MenuInflater)} is called.
|
||||
* For instance in {@link #onAttach(Activity)}
|
||||
*
|
||||
* @param supportsSearch true if you support search queries, false otherwise
|
||||
*/
|
||||
public void setSupportsSearch(boolean supportsSearch) {
|
||||
this.supportsSearch = supportsSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the search state of the list fragment
|
||||
*/
|
||||
public void saveSearchState() {
|
||||
savedSearchFilter = searchFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return text entered in searchview
|
||||
*/
|
||||
public String getSearchFilter() {
|
||||
return searchFilter;
|
||||
}
|
||||
|
||||
private void setupSearchMenuItem(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.media_search, menu);
|
||||
MenuItem searchMenuItem = menu.findItem(R.id.action_search);
|
||||
if (searchMenuItem != null) {
|
||||
searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);
|
||||
searchView.setOnQueryTextListener(this);
|
||||
searchView.setQueryHint(getString(R.string.action_search));
|
||||
if (!TextUtils.isEmpty(savedSearchFilter)) {
|
||||
searchMenuItem.expandActionView();
|
||||
searchView.setQuery(savedSearchFilter, false);
|
||||
//noinspection RestrictedApi
|
||||
searchView.clearFocus();
|
||||
}
|
||||
|
||||
MenuItemCompat.setOnActionExpandListener(searchMenuItem, new MenuItemCompat.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||
searchFilter = savedSearchFilter = null;
|
||||
refreshList();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Handle clearing search query using the close button (X button).
|
||||
View view = searchView.findViewById(R.id.search_close_btn);
|
||||
if (view != null) {
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
EditText editText = (EditText) searchView.findViewById(R.id.search_src_text);
|
||||
editText.setText("");
|
||||
searchView.setQuery("", false);
|
||||
searchFilter = savedSearchFilter = "";
|
||||
refreshList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search view callbacks
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
if ((!searchView.hasFocus()) && TextUtils.isEmpty(newText)) {
|
||||
//onQueryTextChange called as a result of manually expanding the searchView in setupSearchMenuItem(...)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* When this fragment is paused, onQueryTextChange is called with an empty string.
|
||||
* This causes problems restoring the list fragment when returning.
|
||||
*/
|
||||
if (isPaused)
|
||||
return true;
|
||||
|
||||
searchFilter = newText;
|
||||
|
||||
refreshList();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String newText) {
|
||||
// All is handled in onQueryTextChange
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
case R.id.action_refresh:
|
||||
refreshList();
|
||||
break;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
if (!TextUtils.isEmpty(searchFilter)) {
|
||||
savedSearchFilter = searchFilter;
|
||||
}
|
||||
outState.putString(BUNDLE_KEY_SEARCH_QUERY, savedSearchFilter);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event bus post. Called when the syncing process ended
|
||||
*
|
||||
* @param event Refreshes data
|
||||
*/
|
||||
public void onEventMainThread(MediaSyncEvent event) {
|
||||
onSyncProcessEnded(event);
|
||||
}
|
||||
|
||||
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to reload the items in the list
|
||||
*/
|
||||
protected abstract void refreshList();
|
||||
}
|
|
@ -20,6 +20,7 @@ import android.os.Bundle;
|
|||
import android.os.Handler;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -34,6 +35,7 @@ import org.xbmc.kore.host.HostManager;
|
|||
import org.xbmc.kore.jsonrpc.ApiCallback;
|
||||
import org.xbmc.kore.jsonrpc.method.PVR;
|
||||
import org.xbmc.kore.jsonrpc.type.PVRType;
|
||||
import org.xbmc.kore.ui.AbstractSearchableFragment;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
|
@ -49,7 +51,7 @@ import butterknife.Unbinder;
|
|||
/**
|
||||
* Fragment that presents the Guide for a channel
|
||||
*/
|
||||
public class PVRChannelEPGListFragment extends Fragment
|
||||
public class PVRChannelEPGListFragment extends AbstractSearchableFragment
|
||||
implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = LogUtils.makeLogTag(PVRChannelEPGListFragment.class);
|
||||
|
||||
|
@ -113,13 +115,16 @@ public class PVRChannelEPGListFragment extends Fragment
|
|||
});
|
||||
listView.setEmptyView(emptyView);
|
||||
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
setHasOptionsMenu(false);
|
||||
setHasOptionsMenu(true);
|
||||
setSupportsSearch(true);
|
||||
browseEPG();
|
||||
}
|
||||
|
||||
|
@ -144,6 +149,11 @@ public class PVRChannelEPGListFragment extends Fragment
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshList() {
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EPF for the channel and setup the listview
|
||||
*/
|
||||
|
@ -155,7 +165,10 @@ public class PVRChannelEPGListFragment extends Fragment
|
|||
if (!isAdded()) return;
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.no_broadcasts_found_refresh));
|
||||
setupEPGListview(result);
|
||||
|
||||
List<PVRType.DetailsBroadcast> finalResult = filter(result);
|
||||
|
||||
setupEPGListview(finalResult);
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
|
@ -173,6 +186,47 @@ public class PVRChannelEPGListFragment extends Fragment
|
|||
}, callbackHandler);
|
||||
}
|
||||
|
||||
|
||||
private List<PVRType.DetailsBroadcast> filter(List<PVRType.DetailsBroadcast> itemList) {
|
||||
String searchFilter = getSearchFilter();
|
||||
|
||||
if (TextUtils.isEmpty(searchFilter)) {
|
||||
return itemList;
|
||||
}
|
||||
|
||||
// Split searchFilter to multiple lowercase words
|
||||
String[] lcWords = searchFilter.toLowerCase().split(" ");;
|
||||
|
||||
List<PVRType.DetailsBroadcast> result = new ArrayList<>(itemList.size());
|
||||
for (PVRType.DetailsBroadcast item:itemList) {
|
||||
// Require all words to match the item:
|
||||
boolean allWordsMatch = true;
|
||||
for (String lcWord:lcWords) {
|
||||
if (!searchFilterWordMatches(lcWord, item)) {
|
||||
allWordsMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allWordsMatch) {
|
||||
continue; // skip this item
|
||||
}
|
||||
|
||||
result.add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean searchFilterWordMatches(String lcWord, PVRType.DetailsBroadcast item) {
|
||||
if (item.title.toLowerCase().contains(lcWord)) {
|
||||
return true;
|
||||
}
|
||||
if (item.plot.toLowerCase().contains(lcWord)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we get the Guide
|
||||
*
|
||||
|
|
|
@ -20,8 +20,8 @@ import android.content.Context;
|
|||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -41,10 +41,12 @@ import org.xbmc.kore.jsonrpc.ApiException;
|
|||
import org.xbmc.kore.jsonrpc.method.PVR;
|
||||
import org.xbmc.kore.jsonrpc.method.Player;
|
||||
import org.xbmc.kore.jsonrpc.type.PVRType;
|
||||
import org.xbmc.kore.ui.AbstractSearchableFragment;
|
||||
import org.xbmc.kore.ui.OnBackPressedListener;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
@ -54,7 +56,7 @@ import butterknife.Unbinder;
|
|||
/**
|
||||
* Fragment that presents the movie list
|
||||
*/
|
||||
public class PVRChannelsListFragment extends Fragment
|
||||
public class PVRChannelsListFragment extends AbstractSearchableFragment
|
||||
implements SwipeRefreshLayout.OnRefreshListener, OnBackPressedListener {
|
||||
private static final String TAG = LogUtils.makeLogTag(PVRChannelsListFragment.class);
|
||||
|
||||
|
@ -118,13 +120,16 @@ public class PVRChannelsListFragment extends Fragment
|
|||
});
|
||||
gridView.setEmptyView(emptyView);
|
||||
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
setHasOptionsMenu(false);
|
||||
setHasOptionsMenu(true);
|
||||
setSupportsSearch(true);
|
||||
|
||||
if (selectedChannelGroupId == -1) {
|
||||
if ((channelGroupAdapter == null) ||
|
||||
|
@ -174,6 +179,11 @@ public class PVRChannelsListFragment extends Fragment
|
|||
unbinder.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshList() {
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
|
@ -252,6 +262,46 @@ public class PVRChannelsListFragment extends Fragment
|
|||
}, callbackHandler);
|
||||
}
|
||||
|
||||
private List<PVRType.DetailsChannel> filter(List<PVRType.DetailsChannel> itemList) {
|
||||
String searchFilter = getSearchFilter();
|
||||
|
||||
if (TextUtils.isEmpty(searchFilter)) {
|
||||
return itemList;
|
||||
}
|
||||
|
||||
// Split searchFilter to multiple lowercase words
|
||||
String[] lcWords = searchFilter.toLowerCase().split(" ");;
|
||||
|
||||
List<PVRType.DetailsChannel> result = new ArrayList<>(itemList.size());
|
||||
for (PVRType.DetailsChannel item:itemList) {
|
||||
// Require all words to match the item:
|
||||
boolean allWordsMatch = true;
|
||||
for (String lcWord:lcWords) {
|
||||
if (!searchFilterWordMatches(lcWord, item)) {
|
||||
allWordsMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allWordsMatch) {
|
||||
continue; // skip this item
|
||||
}
|
||||
|
||||
result.add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean searchFilterWordMatches(String lcWord, PVRType.DetailsChannel item) {
|
||||
if (item.label.toLowerCase().contains(lcWord)) {
|
||||
return true;
|
||||
}
|
||||
if (item.broadcastnow != null && item.broadcastnow.title.toLowerCase().contains(lcWord)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we get the channel groups
|
||||
*
|
||||
|
@ -296,7 +346,10 @@ public class PVRChannelsListFragment extends Fragment
|
|||
|
||||
// To prevent the empty text from appearing on the first load, set it now
|
||||
emptyView.setText(getString(R.string.no_channels_found_refresh));
|
||||
setupChannelsGridview(result);
|
||||
|
||||
List<PVRType.DetailsChannel> finalResult = filter(result);
|
||||
|
||||
setupChannelsGridview(finalResult);
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ import android.content.res.Resources;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
@ -43,6 +43,7 @@ import org.xbmc.kore.jsonrpc.ApiCallback;
|
|||
import org.xbmc.kore.jsonrpc.method.PVR;
|
||||
import org.xbmc.kore.jsonrpc.method.Player;
|
||||
import org.xbmc.kore.jsonrpc.type.PVRType;
|
||||
import org.xbmc.kore.ui.AbstractSearchableFragment;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
|
@ -58,7 +59,7 @@ import butterknife.Unbinder;
|
|||
/**
|
||||
* Fragment that presents the PVR recordings list
|
||||
*/
|
||||
public class PVRRecordingsListFragment extends Fragment
|
||||
public class PVRRecordingsListFragment extends AbstractSearchableFragment
|
||||
implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = LogUtils.makeLogTag(PVRRecordingsListFragment.class);
|
||||
|
||||
|
@ -85,6 +86,7 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_pvr_list, container, false);
|
||||
|
||||
unbinder = ButterKnife.bind(this, root);
|
||||
|
||||
hostManager = HostManager.getInstance(getActivity());
|
||||
|
@ -99,6 +101,8 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
});
|
||||
gridView.setEmptyView(emptyView);
|
||||
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
@ -106,6 +110,7 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
setSupportsSearch(true);
|
||||
browseRecordings();
|
||||
}
|
||||
|
||||
|
@ -153,9 +158,7 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this to reload the items in the list
|
||||
*/
|
||||
@Override
|
||||
public void refreshList() {
|
||||
onRefresh();
|
||||
}
|
||||
|
@ -199,7 +202,6 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Swipe refresh layout callback
|
||||
*/
|
||||
|
@ -243,7 +245,12 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
boolean hideWatched = preferences.getBoolean(Settings.KEY_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_PVR_RECORDINGS_FILTER_HIDE_WATCHED);
|
||||
|
||||
if (!hideWatched) {
|
||||
String searchFilter = getSearchFilter();
|
||||
boolean hasSearchFilter = !TextUtils.isEmpty(searchFilter);
|
||||
// Split searchFilter to multiple lowercase words
|
||||
String[] lcWords = hasSearchFilter ? searchFilter.toLowerCase().split(" ") : null;
|
||||
|
||||
if (!(hideWatched || hasSearchFilter)) {
|
||||
return itemList;
|
||||
}
|
||||
|
||||
|
@ -268,7 +275,19 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
}
|
||||
}
|
||||
|
||||
// more conditions may be added here
|
||||
if (hasSearchFilter) {
|
||||
// Require all lowercase words to match the item:
|
||||
boolean allWordsMatch = true;
|
||||
for (String lcWord:lcWords) {
|
||||
if (!searchFilterWordMatches(lcWord, item)) {
|
||||
allWordsMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allWordsMatch) {
|
||||
continue; // skip this item
|
||||
}
|
||||
}
|
||||
|
||||
result.add(item);
|
||||
}
|
||||
|
@ -276,6 +295,14 @@ public class PVRRecordingsListFragment extends Fragment
|
|||
return result;
|
||||
}
|
||||
|
||||
private boolean searchFilterWordMatches(String lcWord, PVRType.DetailsRecording item) {
|
||||
if (item.title.toLowerCase().contains(lcWord)
|
||||
|| item.channel.toLowerCase().contains(lcWord)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void sort(List<PVRType.DetailsRecording> itemList) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
|
||||
|
|
Loading…
Reference in New Issue