package com.sunlightlabs.android.congress.fragments; import android.content.Context; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.sunlightlabs.android.congress.R; import com.sunlightlabs.android.congress.tasks.LoadPhotoTask; import com.sunlightlabs.android.congress.utils.FragmentUtils; import com.sunlightlabs.android.congress.utils.LegislatorImage; import com.sunlightlabs.android.congress.utils.Utils; import com.sunlightlabs.congress.models.Committee; import com.sunlightlabs.congress.models.Committee.Membership; import com.sunlightlabs.congress.models.CongressException; import com.sunlightlabs.congress.models.Legislator; import com.sunlightlabs.congress.services.BillService; import com.sunlightlabs.congress.services.CommitteeService; import com.sunlightlabs.congress.services.LegislatorService; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.RejectedExecutionException; public class LegislatorListFragment extends ListFragment implements LoadPhotoTask.LoadsPhoto { public static final int SEARCH_ZIP = 0; public static final int SEARCH_LOCATION = 1; public static final int SEARCH_STATE = 2; public static final int SEARCH_LASTNAME = 3; public static final int SEARCH_COMMITTEE = 4; public static final int SEARCH_COSPONSORS = 5; public static final int SEARCH_CHAMBER = 6; List<Legislator> legislators; Map<String,LoadPhotoTask> loadPhotoTasks = new HashMap<String,LoadPhotoTask>(); int type; String chamber; String billId; String zipCode, lastName, state; Committee committee; double latitude = -1; double longitude = -1; public static LegislatorListFragment forChamber(String chamber) { LegislatorListFragment frag = new LegislatorListFragment(); Bundle args = new Bundle(); args.putInt("type", SEARCH_CHAMBER); args.putString("chamber", chamber); frag.setArguments(args); frag.setRetainInstance(true); return frag; } public static LegislatorListFragment forState(String state) { LegislatorListFragment frag = new LegislatorListFragment(); Bundle args = new Bundle(); args.putInt("type", SEARCH_STATE); args.putString("state", state); frag.setArguments(args); frag.setRetainInstance(true); return frag; } public static LegislatorListFragment forCommittee(Committee committee) { LegislatorListFragment frag = new LegislatorListFragment(); Bundle args = new Bundle(); args.putInt("type", SEARCH_COMMITTEE); args.putSerializable("committee", committee); frag.setArguments(args); frag.setRetainInstance(true); return frag; } public static LegislatorListFragment forZip(String zip) { LegislatorListFragment frag = new LegislatorListFragment(); Bundle args = new Bundle(); args.putInt("type", SEARCH_ZIP); args.putString("zip", zip); frag.setArguments(args); frag.setRetainInstance(true); return frag; } public static LegislatorListFragment forBill(String billId) { LegislatorListFragment frag = new LegislatorListFragment(); Bundle args = new Bundle(); args.putInt("type", SEARCH_COSPONSORS); args.putString("billId", billId); frag.setArguments(args); frag.setRetainInstance(true); return frag; } public static LegislatorListFragment forLastName(String lastName) { LegislatorListFragment frag = new LegislatorListFragment(); Bundle args = new Bundle(); args.putInt("type", SEARCH_LASTNAME); args.putString("last_name", lastName); frag.setArguments(args); frag.setRetainInstance(true); return frag; } public static LegislatorListFragment forLocation(double latitude, double longitude) { LegislatorListFragment frag = new LegislatorListFragment(); Bundle args = new Bundle(); args.putInt("type", SEARCH_LOCATION); args.putDouble("latitude", latitude); args.putDouble("longitude", longitude); frag.setArguments(args); frag.setRetainInstance(true); return frag; } public LegislatorListFragment() {} @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); type = args.getInt("type"); chamber = args.getString("chamber"); zipCode = args.getString("zip"); lastName = args.getString("last_name"); state = args.getString("state"); committee = (Committee) args.getSerializable("committee"); billId = args.getString("billId"); latitude = args.getDouble("latitude", -1); longitude = args.getDouble("longitude", -1); loadLegislators(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ViewGroup view = (ViewGroup) inflater.inflate(R.layout.list, container, false); if (type == SEARCH_CHAMBER) { ListView list = (ListView) view.findViewById(android.R.id.list); list.setFastScrollEnabled(true); } return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setupControls(); if (legislators != null) displayLegislators(); } public void setupControls() { ((Button) getView().findViewById(R.id.refresh)).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { refresh(); } }); if (type == SEARCH_COSPONSORS) FragmentUtils.setLoading(this, R.string.legislators_loading_cosponsors); else if (type == SEARCH_LOCATION) FragmentUtils.setLoading(this, R.string.legislators_loading_location); else FragmentUtils.setLoading(this, R.string.legislators_loading); } public void loadLegislators() { new LoadLegislatorsTask(this).execute(); } public void onLoadLegislators(List<Legislator> legislators) { if (!isAdded()) return; // if there's only one result, don't even make them click it if (legislators.size() == 1 && (type != SEARCH_LOCATION && type != SEARCH_COSPONSORS)) { selectLegislator(legislators.get(0)); getActivity().finish(); } else displayLegislators(); } public void onLoadLegislators(CongressException exception) { if (isAdded()) FragmentUtils.showRefresh(this, R.string.legislators_error); } public void displayLegislators() { if (legislators.size() > 0) setListAdapter(new LegislatorAdapter(this, legislators)); else { switch (type) { case SEARCH_ZIP: FragmentUtils.showEmpty(this, R.string.empty_zipcode); break; case SEARCH_LOCATION: FragmentUtils.showEmpty(this, R.string.empty_location); break; case SEARCH_LASTNAME: FragmentUtils.showEmpty(this, R.string.empty_last_name); break; default: FragmentUtils.showEmpty(this, R.string.legislators_error); } } } public void loadPhoto(String bioguide_id) { if (!loadPhotoTasks.containsKey(bioguide_id)) { try { loadPhotoTasks.put(bioguide_id, (LoadPhotoTask) new LoadPhotoTask(this, LegislatorImage.PIC_SMALL, bioguide_id).execute(bioguide_id)); } catch (RejectedExecutionException e) { Log.e(Utils.TAG, "[LegislatorListFragment] RejectedExecutionException occurred while loading photo.", e); onLoadPhoto(null, bioguide_id); // if we can't run it, then just show the no photo image and move on } } } public void onLoadPhoto(Drawable photo, Object tag) { if (!isAdded()) return; loadPhotoTasks.remove(tag); LegislatorAdapter.ViewHolder holder = new LegislatorAdapter.ViewHolder(); holder.bioguide_id = (String) tag; View result = getListView().findViewWithTag(holder); if (result != null) { if (photo != null) ((ImageView) result.findViewById(R.id.photo)).setImageDrawable(photo); else ((ImageView) result.findViewById(R.id.photo)).setImageResource(R.drawable.person); } } public Context getContext() { return getActivity(); } private void refresh() { legislators = null; FragmentUtils.setLoading(this, R.string.legislators_loading); FragmentUtils.showLoading(this); loadLegislators(); } @Override public void onListItemClick(ListView parent, View v, int position, long id) { selectLegislator((Legislator) parent.getItemAtPosition(position)); } public void selectLegislator(Legislator legislator) { startActivity(Utils.legislatorIntent(getActivity(), legislator)); } private static class LegislatorAdapter extends ArrayAdapter<Legislator> { LayoutInflater inflater; LegislatorListFragment context; public LegislatorAdapter(LegislatorListFragment context, List<Legislator> items) { super(context.getActivity(), 0, items); this.context = context; inflater = LayoutInflater.from(context.getActivity()); } @Override public boolean areAllItemsEnabled() { return true; } @Override public int getViewTypeCount() { return 1; } public View getView(int position, View convertView, ViewGroup parent) { View view; ViewHolder holder; if (convertView == null) { view = inflater.inflate(R.layout.legislator_item, null); holder = new ViewHolder(); holder.title = (TextView) view.findViewById(R.id.committee_title); holder.name = (TextView) view.findViewById(R.id.name); holder.position = (TextView) view.findViewById(R.id.position); holder.photo = (ImageView) view.findViewById(R.id.photo); view.setTag(holder); } else { view = convertView; holder = (ViewHolder) view.getTag(); } Legislator legislator = getItem(position); // used as the hook to get the legislator image in place when it's loaded holder.bioguide_id = legislator.bioguide_id; holder.name.setText(nameFor(legislator)); holder.position.setText(positionFor(legislator)); // if (context.type == SEARCH_COMMITTEE) { // if (legislator.membership != null && legislator.membership.title != null) { // holder.title.setText(legislator.membership.title); // holder.title.setVisibility(View.VISIBLE); // } else // holder.title.setVisibility(View.GONE); // } ImageView photo = (ImageView) view.findViewById(R.id.photo); LegislatorImage.setImageView(legislator.bioguide_id, LegislatorImage.PIC_SMALL, context.getContext(), holder.photo); return view; } public String nameFor(Legislator legislator) { if (context.type == SEARCH_COMMITTEE) { return legislator.title + ". " + legislator.firstName() + " " + legislator.last_name; } else return legislator.last_name + ", " + legislator.firstName(); } public String positionFor(Legislator legislator) { String stateName = Utils.stateCodeToName(context.getActivity(), legislator.state); if (context.type == SEARCH_COMMITTEE) { String position = legislator.party + " - " + stateName; if (legislator.membership != null && legislator.membership.title != null) return legislator.membership.title + " - " + position; else return position; } else { String district; if (legislator.chamber.equals("senate")) district = "Senator"; else district = "District " + legislator.district; return legislator.party + " - " + stateName + " - " + district; } } static class ViewHolder { TextView title, name, position; ImageView photo; String bioguide_id; @Override public boolean equals(Object holder) { ViewHolder other = (ViewHolder) holder; return other != null && other instanceof ViewHolder && this.bioguide_id.equals(other.bioguide_id); } } } private static class LoadLegislatorsTask extends AsyncTask<Void, Void, List<Legislator>> { LegislatorListFragment context; CongressException exception; public LoadLegislatorsTask(LegislatorListFragment context) { super(); this.context = context; FragmentUtils.setupAPI(context); } @Override protected List<Legislator> doInBackground(Void... nothing) { List<Legislator> legislators = new ArrayList<Legislator>(); List<Legislator> temp; try { switch (context.type) { case SEARCH_ZIP: temp = LegislatorService.allForZipCode(context.zipCode); break; case SEARCH_LOCATION: temp = LegislatorService.allForLatLong(context.latitude, context.longitude); break; case SEARCH_LASTNAME: temp = LegislatorService.allWhere("query", context.lastName); break; case SEARCH_COMMITTEE: temp = CommitteeService.find(context.committee.id).members; break; case SEARCH_STATE: temp = LegislatorService.allWhere("state", context.state); break; case SEARCH_COSPONSORS: temp = BillService.find(context.billId, new String[] {"cosponsors"}).cosponsors; break; case SEARCH_CHAMBER: return LegislatorService.allWhere("chamber", context.chamber); default: return legislators; } if (context.type == SEARCH_COMMITTEE) { // put Chair and Ranking Member first, then // put majority in order of rank, then minority in order of rank List<Legislator> leaders = new ArrayList<Legislator>(); List<Legislator> rankAndFile = new ArrayList<Legislator>(); for (int i=0; i< temp.size(); i++) { Legislator legislator = temp.get(i); Membership membership = legislator.membership; if (membership.title != null && (membership.title.contains("Chair") || membership.title.contains("Ranking"))) leaders.add(legislator); else rankAndFile.add(legislator); } Collections.sort(leaders, new Comparator<Legislator>() { @Override public int compare(Legislator a, Legislator b) { return a.membership.title.compareTo(b.membership.title); } }); Collections.sort(rankAndFile, new Comparator<Legislator>() { @Override public int compare(Legislator a, Legislator b) { if (a.membership.side.equals(b.membership.side)) return a.membership.rank - b.membership.rank; else return a.membership.side.compareTo(b.membership.side); } }); legislators.addAll(leaders); legislators.addAll(rankAndFile); } else { // sort legislators Senators-first List<Legislator> upper = new ArrayList<Legislator>(); List<Legislator> lower = new ArrayList<Legislator>(); for (int i = 0; i < temp.size(); i++) { if (temp.get(i).chamber.equals("senate")) upper.add(temp.get(i)); else lower.add(temp.get(i)); } Collections.sort(upper); Collections.sort(lower); legislators.addAll(upper); legislators.addAll(lower); } return legislators; } catch (CongressException exception) { this.exception = exception; return null; } } @Override protected void onPostExecute(List<Legislator> legislators) { context.legislators = legislators; if (exception == null) context.onLoadLegislators(legislators); else context.onLoadLegislators(exception); } } }