package ca.grocerygo.android.fragment; import android.app.Activity; import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.content.LocalBroadcastManager; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import ca.grocerygo.android.CategoryTopFragmentActivity; import ca.grocerygo.android.GlobalSearchFragmentActivity; import ca.grocerygo.android.R; import ca.grocerygo.android.database.CartTable; import ca.grocerygo.android.database.FlyerTable; import ca.grocerygo.android.database.GroceryTable; import ca.grocerygo.android.database.StoreParentTable; import ca.grocerygo.android.database.contentprovider.GroceryotgProvider; import ca.grocerygo.android.settings.SettingsManager; import ca.grocerygo.android.utils.GroceryGoUtils; import ca.grocerygo.android.utils.GroceryStoreDistanceMap; import ca.grocerygo.android.utils.ServerURLs; import com.actionbarsherlock.app.SherlockListFragment; import com.tjerkw.slideexpandable.library.SlideExpandableListAdapter; import java.util.ArrayList; import java.util.List; public class GroceryListFragment extends SherlockListFragment implements LoaderManager.LoaderCallbacks<Cursor> { private static final String CATEGORY_POSITION = "position"; private Context mContext; private GroceryListCursorAdapter mAdapter; private String mQuery = ""; private ProgressBar mProgressView; private TextView mEmptyTextView; private Integer categoryId = GroceryListCursorAdapter.GLOBAL_SEARCH_CATEGORY; private SparseArray<Float> mDistanceMap; private BroadcastReceiver mRestartReceiver; private BroadcastReceiver mLocationReceiver; public static GroceryListFragment newInstance(int pos) { GroceryListFragment f = new GroceryListFragment(); // Supply num input as an argument. Bundle args = new Bundle(); args.putInt(CATEGORY_POSITION, pos); f.setArguments(args); return f; } @Override public void onAttach(Activity activity) { super.onAttach(activity); this.mContext = activity; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); if (args != null) { categoryId = args.getInt(CATEGORY_POSITION); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.grocery_fragment_list, container, false); mProgressView = (ProgressBar) v.findViewById(R.id.refresh_progress); mEmptyTextView = (TextView) v.findViewById(R.id.empty_grocery_list); if (mProgressView != null) mProgressView.setVisibility(View.VISIBLE); if (mEmptyTextView != null) mEmptyTextView.setVisibility(View.INVISIBLE); final GroceryListFragment frag = this; mRestartReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(GroceryGoUtils.BROADCAST_ACTION_RELOAD_GROCERY_LIST)) { if(isAdded()) { // Restart the loader, refreshing all views Bundle b = new Bundle(); b.putString("query", mQuery); b.putBoolean("reload", false); getLoaderManager().restartLoader(0, b, frag); } } } }; IntentFilter mRestartIntentFilter = new IntentFilter(GroceryGoUtils.BROADCAST_ACTION_RELOAD_GROCERY_LIST); mRestartIntentFilter.addCategory(Intent.CATEGORY_DEFAULT); LocalBroadcastManager.getInstance(mContext).registerReceiver(mRestartReceiver, mRestartIntentFilter); mLocationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(GroceryGoUtils.BROADCAST_ACTION_RELOAD_LOCATION)) { if (isAdded()) { // Reload the store distance map mDistanceMap = GroceryStoreDistanceMap.getmStoreDistanceMap(); // Restart the loader, refreshing all views Bundle b = new Bundle(); b.putString("query", mQuery); b.putBoolean("reload", false); getLoaderManager().restartLoader(0, b, frag); } } } }; IntentFilter mLocationIntentFilter = new IntentFilter(GroceryGoUtils.BROADCAST_ACTION_RELOAD_LOCATION); mLocationIntentFilter.addCategory(Intent.CATEGORY_DEFAULT); LocalBroadcastManager.getInstance(mContext).registerReceiver(mLocationReceiver, mLocationIntentFilter); return v; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mDistanceMap = GroceryStoreDistanceMap.getmStoreDistanceMap(); String[] from = new String[]{GroceryTable.COLUMN_GROCERY_ID, GroceryTable.COLUMN_GROCERY_NAME, GroceryTable.COLUMN_GROCERY_NAME, GroceryTable.COLUMN_GROCERY_PRICE, StoreParentTable.COLUMN_STORE_PARENT_NAME, FlyerTable.COLUMN_FLYER_ID, FlyerTable.COLUMN_FLYER_URL, CartTable.COLUMN_CART_FLAG_SHOPLIST, GroceryTable.COLUMN_GROCERY_EXPIRY}; int[] to = new int[]{R.id.grocery_row_id, R.id.grocery_row_label, R.id.grocery_row_details, R.id.grocery_row_price, R.id.grocery_row_store_parent_name, R.id.grocery_row_store_id, R.id.grocery_row_flyer_url, R.id.grocery_row_in_shopcart, R.id.grocery_row_expiry}; // Handles the search filter Bundle args = ((Activity) mContext).getIntent().getExtras(); if (args != null) { if (args.containsKey(GlobalSearchFragmentActivity.GLOBAL_SEARCH)) { // Update the query - this is used by the loader when fetching results from database mQuery = args.getString(SearchManager.QUERY).trim(); } } int layoutId = R.layout.grocery_fragment_list_row; mAdapter = new GroceryListCursorAdapter(mContext, layoutId, null, from, to, this.mDistanceMap); mAdapter.setViewBinder(new GroceryViewBinder(mContext)); SlideExpandableListAdapter wrappedAdapter = new SlideExpandableListAdapter(mAdapter, R.id.expandable_toggle_button, R.id.expandable); // Make a VERY short animation duration wrappedAdapter.setAnimationDuration(0); setListAdapter(wrappedAdapter); this.loadDataWithQuery(false, mQuery); } public void loadDataWithQuery(Boolean reload, String query) { if (mAdapter == null) return; Bundle b = new Bundle(); b.putString("query", query); if (reload) { b.putBoolean("reload", true); getLoaderManager().restartLoader(0, b, this); } else { b.putBoolean("reload", false); getLoaderManager().restartLoader(0, b, this); } } public void fillData() { // Prepare the asynchronous loader. Bundle b = new Bundle(); b.putString("query", mQuery); b.putBoolean("reload", false); getLoaderManager().initLoader(0, b, this); } @Override public void onResume() { super.onResume(); } @Override public void onPause() { super.onPause(); } @Override public void onDestroyView() { super.onDestroyView(); LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mRestartReceiver); } @Override public void onDestroy() { super.onDestroy(); } @Override public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { String query = bundle.getString("query").trim(); List<String> selectionArgs = new ArrayList<String>(); boolean isAtLeastOneWhere = false; String[] projection = {GroceryTable.TABLE_GROCERY + "." + GroceryTable.COLUMN_ID, GroceryTable.COLUMN_GROCERY_ID, GroceryTable.COLUMN_GROCERY_NAME, GroceryTable.COLUMN_GROCERY_PRICE, StoreParentTable.COLUMN_STORE_PARENT_NAME, FlyerTable.TABLE_FLYER + "." + FlyerTable.COLUMN_FLYER_ID, FlyerTable.TABLE_FLYER + "." + FlyerTable.COLUMN_FLYER_URL, CartTable.COLUMN_CART_GROCERY_ID, CartTable.COLUMN_CART_FLAG_SHOPLIST, GroceryTable.COLUMN_GROCERY_EXPIRY}; String selection; if (categoryId == GroceryListCursorAdapter.GLOBAL_SEARCH_CATEGORY) { selection = ""; } else { if (!isAtLeastOneWhere) { isAtLeastOneWhere = true; } selection = GroceryTable.TABLE_GROCERY + "." + GroceryTable.COLUMN_GROCERY_CATEGORY + "=?"; selectionArgs.add(categoryId.toString()); } // If user entered a search query, filter the results based on grocery name if (!query.isEmpty()) { if (!isAtLeastOneWhere) { isAtLeastOneWhere = true; } else { selection += " AND "; } selection += GroceryTable.TABLE_GROCERY + "." + GroceryTable.COLUMN_GROCERY_NAME + " LIKE ?"; selectionArgs.add("%" + query + "%"); } SparseBooleanArray selectedStores = SettingsManager.getStoreFilter(mContext); if (selectedStores != null && selectedStores.size() > 0) { // Go through selected stores and add them to query String storeSelection = ""; for (int storeNum = 0; storeNum < selectedStores.size(); storeNum++) { if (selectedStores.valueAt(storeNum) == true) { if (storeSelection.isEmpty()) { if (!isAtLeastOneWhere) { isAtLeastOneWhere = true; } else { selection += " AND "; } storeSelection = "("; storeSelection += StoreParentTable.TABLE_STORE_PARENT + "." + StoreParentTable.COLUMN_STORE_PARENT_ID + " = ?"; } else { storeSelection += " OR " + StoreParentTable.TABLE_STORE_PARENT + "." + StoreParentTable.COLUMN_STORE_PARENT_ID + " = ?"; } selectionArgs.add(((Integer) selectedStores.keyAt(storeNum)).toString()); } } if (!storeSelection.isEmpty()) { storeSelection += ")"; selection += storeSelection; } } // Store location filter int storeLocationPref = SettingsManager.getStoreLocationFilter(mContext); if (storeLocationPref == 1) { // Display only the closest store location for each store parent (which has a flyer) // Map between storeParents and corresponding stores SparseArray<ArrayList<Integer>> storeParentStores = GroceryStoreDistanceMap.getmStoreParentStoreMap(); // Selected storeParents (from settings) SparseBooleanArray selectedStoreParents = SettingsManager.getStoreFilter(mContext); ArrayList<Integer> filterStores = new ArrayList<Integer>(); ArrayList<Integer> filterFlyers = new ArrayList<Integer>(); if (selectedStoreParents != null && selectedStoreParents.size() > 0) { for (int k=0; k<selectedStoreParents.size(); k++) { ArrayList<Integer> storeIds = storeParentStores.get(selectedStoreParents.keyAt(k)); filterStores.add(GroceryGoUtils.getClosestStore(storeIds, mDistanceMap)); } } else { // go through all storeParents, and find the closest store location for (int k=0; k<storeParentStores.size(); k++) { ArrayList<Integer> storeIds = storeParentStores.get(k); filterStores.add(GroceryGoUtils.getClosestStore(storeIds, mDistanceMap)); } } // Translate store IDs into flyer IDs to be used in the SQL query SparseArray<ArrayList<Integer>> flyerStoreMap = GroceryStoreDistanceMap.getmFlyerStoreMap(); for (int k=0; k<flyerStoreMap.size(); k++) { int flyerId = flyerStoreMap.keyAt(k); ArrayList<Integer> listStores = flyerStoreMap.valueAt(k); for (int m=0; m<filterStores.size(); m++) { if (listStores.contains(filterStores.get(m))) { filterFlyers.add(flyerId); break; } } } // Update the where clause for the query iff there are any flyers matching the filter // (some stores have no associated flyer) if (filterFlyers.size() > 0) { if (!isAtLeastOneWhere) { isAtLeastOneWhere = true; } else { selection += " AND "; } selection += "("; for (int k=0; k<filterFlyers.size(); k++) { if (k > 0) selection += " OR "; selection += GroceryTable.TABLE_GROCERY + "." + GroceryTable.COLUMN_GROCERY_FLYER + " = ?"; selectionArgs.add(filterFlyers.get(k).toString()); } selection += ")"; } } if (CategoryTopFragmentActivity.mPriceRangeMin != null) { if (!isAtLeastOneWhere) { isAtLeastOneWhere = true; } else { selection += " AND "; } selection += GroceryTable.COLUMN_GROCERY_PRICE + " >= ?"; selectionArgs.add(CategoryTopFragmentActivity.mPriceRangeMin.toString()); } if (CategoryTopFragmentActivity.mPriceRangeMax != null) { if (!isAtLeastOneWhere) { isAtLeastOneWhere = true; } else { selection += " AND "; } selection += GroceryTable.COLUMN_GROCERY_PRICE + " <= ?"; selectionArgs.add(CategoryTopFragmentActivity.mPriceRangeMax.toString()); } final String[] selectionArgsArr = new String[selectionArgs.size()]; selectionArgs.toArray(selectionArgsArr); return new CursorLoader(mContext, GroceryotgProvider.CONTENT_URI_GRO_JOINSTORE, projection, selection, selectionArgsArr, GroceryTable.COLUMN_GROCERY_SCORE); } @Override public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { mAdapter.swapCursor(cursor); if (mProgressView != null) mProgressView.setVisibility(View.GONE); if (cursor.getCount() == 0) { if (!mQuery.isEmpty()) { displayEmptyListMessage(buildNoSearchResultString()); } else { displayEmptyListMessage(buildNoNewContentString()); } } // Now in the event we are searching, set the number of found items Integer cnt = mAdapter.getCount(); TextView numResults = (TextView) ((Activity) mContext).findViewById(R.id.search_num_results); if (numResults != null) { numResults.setText(cnt.toString()); } } private void displayEmptyListMessage(String emptyStringMsg) { mEmptyTextView.setText(emptyStringMsg); mEmptyTextView.setVisibility(View.VISIBLE); ListView listView = (ListView) getListView(); listView.setEmptyView(mEmptyTextView); } private String buildNoNewContentString() { String emptyStringFormat = mContext.getString(R.string.no_new_content); return (ServerURLs.getLastRefreshed() == null) ? String.format(emptyStringFormat, " Never") : String.format(emptyStringFormat, ServerURLs.getLastRefreshed()); } private String buildNoSearchResultString() { return mContext.getString(R.string.no_search_results); } @Override public void onLoaderReset(Loader<Cursor> cursorLoader) { mAdapter.swapCursor(null); } }