package org.intermine.app.fragment;
/*
* Copyright (C) 2015 InterMine
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. See the LICENSE file for more
* information or http://www.gnu.org/copyleft/lesser.html.
*
*/
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
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.AbsListView;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.octo.android.robospice.persistence.exception.SpiceException;
import com.octo.android.robospice.request.listener.RequestListener;
import org.intermine.app.R;
import org.intermine.app.activity.BaseActivity;
import org.intermine.app.activity.MainActivity;
import org.intermine.app.adapter.ApiPager;
import org.intermine.app.adapter.GenesAdapter;
import org.intermine.app.controller.LoadOnScrollViewController;
import org.intermine.app.core.Gene;
import org.intermine.app.core.GenesList;
import org.intermine.app.listener.OnGeneSelectedListener;
import org.intermine.app.net.ResponseHelper;
import org.intermine.app.net.request.get.GeneSearchRequest;
import org.intermine.app.util.Sharing;
import org.intermine.app.util.Strs;
import org.intermine.app.util.Views;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import butterknife.InjectView;
import butterknife.OnItemClick;
public class SearchFragment extends BaseFragment implements SearchView.OnQueryTextListener {
private static final String QUERY_KEY = "query_key";
private static final String EXPAND_SEARCH_VIEW_KEY = "expand_search_view_key";
@InjectView(R.id.genes)
protected ListView mGenesListView;
@InjectView(R.id.not_found_results_container)
protected View mNotFoundView;
@InjectView(R.id.info_container)
protected View mInfoContainer;
@InjectView(R.id.progress_view)
protected ProgressBar mProgressView;
@InjectView(R.id.progress_bar)
protected ProgressBar mProgressBar;
protected boolean mLoading;
protected LoadOnScrollViewController mViewController;
private SearchView mSearchView;
private ApiPager mPager;
private LoadOnScrollViewController.LoadOnScrollDataController mDataController;
private OnSearchRequestsFinishedAsyncTask mAsyncTask;
private GenesAdapter mGenesAdapter;
private List<Gene> mGenes;
private Map<String, Integer> mMine2ResultsCount = new HashMap<>();
private CountDownLatch mCountDownLatch;
private OnGeneSelectedListener mListener;
private String mGeneFavoritesListName;
private String mQuery = Strs.EMPTY_STRING;
private boolean mExpandSearchViewOnStartup;
private int mStatusBarColor;
private MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() {
@Override
public void onItemCheckedStateChanged(ActionMode mode, int pos, long id, boolean checked) {
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater menuInflater = mode.getMenuInflater();
menuInflater.inflate(R.menu.gene_view_menu, menu);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mStatusBarColor = getActivity().getWindow().getStatusBarColor();
getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.dark_gray));
}
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.favourites:
addGenesToFavorites();
Toast.makeText(getActivity(), R.string.genes_added_to_favorites, Toast.LENGTH_LONG).show();
mode.finish();
return true;
case R.id.share:
Intent intent = Sharing.generateIntentToSendText(getSelectedGenes());
startActivity(Intent.createChooser(intent,
getResources().getString(R.string.share_message)));
mode.finish();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getActivity().getWindow().setStatusBarColor(mStatusBarColor);
}
}
};
public static SearchFragment newInstance(boolean expandSearchView) {
SearchFragment fragment = new SearchFragment();
Bundle bundle = new Bundle();
bundle.putBoolean(EXPAND_SEARCH_VIEW_KEY, expandSearchView);
fragment.setArguments(bundle);
return fragment;
}
// --------------------------------------------------------------------------------------------
// Fragment Lifecycle
// --------------------------------------------------------------------------------------------
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mGenesListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL);
mGenesListView.setMultiChoiceModeListener(mMultiListener);
mGenes = new ArrayList<>();
mGenesAdapter = new GenesAdapter(getActivity());
mGenesAdapter.updateGenes(mGenes);
mGenesListView.setAdapter(mGenesAdapter);
mViewController = new LoadOnScrollViewController(getDataController(), getActivity());
mGenesListView.setOnScrollListener(mViewController);
mGenesListView.addFooterView(mViewController.getFooterView());
if (null != savedInstanceState) {
mQuery = savedInstanceState.getString(QUERY_KEY);
if (!Strs.isNullOrEmpty(mQuery)) {
onQueryTextSubmit(mQuery);
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mGeneFavoritesListName = getString(R.string.gene_favorites_list_name);
Bundle bundle = getArguments();
if (null != bundle) {
mExpandSearchViewOnStartup = bundle.getBoolean(EXPAND_SEARCH_VIEW_KEY);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
return inflater.inflate(R.layout.search_fragment, container, false);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mListener = (OnGeneSelectedListener) context;
((MainActivity) context).onSectionAttached(getString(R.string.search));
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (!Strs.isNullOrEmpty(mQuery)) {
outState.putString(QUERY_KEY, mQuery);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.gene_search_menu, menu);
MenuItem item = menu.findItem(R.id.search_action);
mSearchView = (SearchView) item.getActionView();
mSearchView.setOnQueryTextListener(this);
mSearchView.setQueryHint(getString(R.string.gene_search_hint));
final EditText searchTextView = (EditText) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
try {
Field mCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes");
mCursorDrawableRes.setAccessible(true);
mCursorDrawableRes.set(searchTextView, 0);
} catch (Exception e) {
}
if (mExpandSearchViewOnStartup) {
item.expandActionView();
}
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public void onDestroyOptionsMenu() {
super.onDestroyOptionsMenu();
mSearchView = null;
}
// --------------------------------------------------------------------------------------------
// Callbacks
// --------------------------------------------------------------------------------------------
@Override
public boolean onQueryTextSubmit(String query) {
mQuery = query;
if (!Strs.isNullOrEmpty(mQuery)) {
if (null != mSearchView) {
mSearchView.clearFocus();
}
mPager = null;
setProgress(true);
performSearchRequests(mQuery, GeneSearchRequest.JSON_FORMAT, 0);
}
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
return true;
}
@OnItemClick(R.id.genes)
public void onGeneSelected(int position) {
if (null != mListener) {
Gene gene = (Gene) mGenesAdapter.getItem(position);
mListener.onGeneSelected(gene);
}
}
protected void setProgress(boolean loading) {
mLoading = loading;
// TODO: fix
Views.setGone(mInfoContainer);
if (loading) {
Views.setVisible(mProgressView);
Views.setGone(mGenesListView, mNotFoundView);
} else {
Views.setVisible(mGenesListView);
Views.setGone(mProgressView);
}
}
protected List<Gene> getSelectedGenes() {
List<Gene> genes = org.intermine.app.util.Collections.newArrayList();
SparseBooleanArray checkedItemIds = mGenesListView.getCheckedItemPositions();
for (int i = 0; i < mGenesAdapter.getCount(); i++) {
if (checkedItemIds.get(i)) {
Gene gene = (Gene) mGenesAdapter.getItem(i);
genes.add(gene);
}
}
return genes;
}
// --------------------------------------------------------------------------------------------
// Helper Methods
// --------------------------------------------------------------------------------------------
protected void addGenesToFavorites() {
SparseBooleanArray checkedItemIds = mGenesListView.getCheckedItemPositions();
for (int i = 0; i < mGenesAdapter.getCount(); i++) {
if (checkedItemIds.get(i)) {
Gene gene = (Gene) mGenesAdapter.getItem(i);
getStorage().addGeneToFavorites(gene);
}
}
}
protected LoadOnScrollViewController.LoadOnScrollDataController getDataController() {
if (null == mDataController) {
mDataController = generateDataController();
}
return mDataController;
}
protected LoadOnScrollViewController.LoadOnScrollDataController generateDataController() {
return new LoadOnScrollViewController.LoadOnScrollDataController() {
@Override
public boolean hasMore() {
return mPager == null || mPager.hasMorePages();
}
@Override
public boolean isLoading() {
return mLoading;
}
@Override
public void loadMore() {
if (mPager == null) {
performSearchRequests(mQuery, GeneSearchRequest.JSON_FORMAT, 0);
} else {
mPager = mPager.next();
performSearchRequests(mQuery, GeneSearchRequest.JSON_FORMAT,
mPager.getCurrentPage() * mPager.getPerPage());
}
mViewController.onStartLoad();
mLoading = true;
}
};
}
private void performSearchRequests(String query, String format, int start) {
Set<String> selectedMines = getStorage().getSelectedMineNames();
if (null != mAsyncTask && !mAsyncTask.getStatus().equals(AsyncTask.Status.FINISHED)) {
mAsyncTask.cancel(true);
}
mCountDownLatch = new CountDownLatch(selectedMines.size());
mAsyncTask = new OnSearchRequestsFinishedAsyncTask();
mAsyncTask.execute();
for (String mine : selectedMines) {
Integer count = mMine2ResultsCount.get(mine);
if (0 == start || (null != count &&
(mPager.getCurrentPage() + 1) * mPager.getPerPage() < count)) {
GeneSearchRequest request = new GeneSearchRequest(getActivity(),
query, mine, format, start);
execute(request, new GeneSearchRequestListener());
} else {
mCountDownLatch.countDown();
}
}
}
// --------------------------------------------------------------------------------------------
// Inner Classes
// --------------------------------------------------------------------------------------------
private class GeneSearchRequestListener implements RequestListener<GenesList> {
@Override
public void onRequestFailure(SpiceException e) {
mCountDownLatch.countDown();
ResponseHelper.handleSpiceException(e, (BaseActivity) getActivity(), null);
}
@Override
public void onRequestSuccess(GenesList result) {
mCountDownLatch.countDown();
// first page load
if (null == mPager) {
mGenes.clear();
mPager = new ApiPager(0, 0, GeneSearchRequest.DEFAULT_SIZE);
mMine2ResultsCount = new HashMap<>();
}
if (0 == mPager.getCurrentPage()) {
mPager = new ApiPager(mPager.getTotal() + result.getResultsCount(), 0, GeneSearchRequest.DEFAULT_SIZE);
}
if (null != result && !result.isEmpty()) {
mGenes.addAll(result);
Collections.sort(mGenes);
mGenesAdapter.notifyDataSetChanged();
mMine2ResultsCount.put(result.get(0).getMine(), result.getResultsCount());
}
if (!mGenes.isEmpty()) {
setProgress(false);
}
}
}
private class OnSearchRequestsFinishedAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected void onPreExecute() {
if (!mGenes.isEmpty()) {
Views.setVisible(mProgressBar);
}
}
@Override
protected Void doInBackground(Void... params) {
while (0 < mCountDownLatch.getCount()) {
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
mViewController.onFinishLoad();
Views.setGone(mProgressBar);
if (mGenes.isEmpty()) {
Views.setGone(mGenesListView);
Views.setVisible(mNotFoundView);
} else {
Views.setVisible(mGenesListView);
Views.setGone(mNotFoundView);
}
setProgress(false);
}
@Override
protected void onCancelled() {
Views.setGone(mProgressBar);
}
}
}