package com.sunlightlabs.android.congress.fragments; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.ListFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; import com.sunlightlabs.android.congress.R; import com.sunlightlabs.android.congress.utils.Analytics; import com.sunlightlabs.android.congress.utils.FragmentUtils; import com.sunlightlabs.android.congress.utils.Utils; import com.sunlightlabs.congress.models.CongressException; import com.sunlightlabs.congress.models.UpcomingBill; import com.sunlightlabs.congress.services.UpcomingBillService; public class UpcomingFragment extends ListFragment { List<UpcomingBill> upcomingBills; public static UpcomingFragment newInstance() { UpcomingFragment fragment = new UpcomingFragment(); fragment.setRetainInstance(true); return fragment; } public UpcomingFragment() {} @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); loadUpcoming(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.list_no_divider, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setupControls(); if (upcomingBills != null) displayUpcomingBills(); } private void setupControls() { FragmentUtils.setLoading(this, R.string.upcoming_loading); ((Button) getView().findViewById(R.id.refresh)).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { upcomingBills = null; FragmentUtils.showLoading(UpcomingFragment.this); loadUpcoming(); } }); } public void loadUpcoming() { new UpcomingBillsTask(this).execute(); } @Override public void onListItemClick(ListView parent, View v, int position, long id) { if (isAdded()) { String bill_id = ((UpcomingAdapter.Bill) parent.getItemAtPosition(position)).id; Analytics.billUpcoming(getActivity(), bill_id); startActivity(Utils.billIntent(bill_id)); } } private void onLoadUpcomingBills(List<UpcomingBill> upcomingBills) { this.upcomingBills = upcomingBills; if (isAdded()) displayUpcomingBills(); } private void onLoadUpcomingBills(CongressException exception) { if (isAdded()) FragmentUtils.showRefresh(this, R.string.upcoming_error); } private void displayUpcomingBills() { if (upcomingBills.size() > 0) setListAdapter(new UpcomingAdapter(this, UpcomingAdapter.wrapUpcoming(upcomingBills))); else FragmentUtils.showEmpty(this, R.string.upcoming_empty); } static class UpcomingAdapter extends ArrayAdapter<UpcomingAdapter.Item> { LayoutInflater inflater; private static final int TYPE_DATE = 0; private static final int TYPE_BILL = 1; public UpcomingAdapter(Fragment context, List<UpcomingAdapter.Item> items) { super(context.getActivity(), 0, items); this.inflater = LayoutInflater.from(context.getActivity()); } @Override public boolean isEnabled(int position) { return getItemViewType(position) == UpcomingAdapter.TYPE_BILL; } @Override public boolean areAllItemsEnabled() { return false; } @Override public int getItemViewType(int position) { Item item = getItem(position); if (item instanceof UpcomingAdapter.Date) return UpcomingAdapter.TYPE_DATE; else return UpcomingAdapter.TYPE_BILL; } @Override public int getViewTypeCount() { return 2; } @Override public View getView(int position, View view, ViewGroup parent) { Item item = getItem(position); if (item instanceof Date) { if (view == null) view = inflater.inflate(R.layout.upcoming_date, null); Date date = (Date) item; ((TextView) view.findViewById(R.id.date_left)).setText(date.dateName); ((TextView) view.findViewById(R.id.date_right)).setText(date.dateFull); } else { // instanceof Action if (view == null) view = inflater.inflate(R.layout.upcoming_bill, null); Bill bill = (Bill) item; ((TextView) view.findViewById(R.id.code)).setText(com.sunlightlabs.congress.models.Bill.formatCode(bill.bill_type, bill.number)); ((TextView) view.findViewById(R.id.title)).setText(bill.title); } return view; } static class Item {} static class Date extends Item { String dateName, dateFull; } static class Bill extends Item { String id, bill_type, title; int number; String context; } static List<UpcomingAdapter.Item> wrapUpcoming(List<UpcomingBill> upcomingBills) { List<Item> items = new ArrayList<Item>(); SimpleDateFormat dateNameFormat = new SimpleDateFormat("EEEE"); SimpleDateFormat dateFullFormat = new SimpleDateFormat("MMM d"); SimpleDateFormat testFormat = new SimpleDateFormat("yyyy-MM-dd"); String today = testFormat.format(Calendar.getInstance().getTime()); Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, 1); String tomorrow = testFormat.format(cal.getTime()); String currentDayRange = ""; for (int i=0; i<upcomingBills.size(); i++) { UpcomingBill upcomingBill = upcomingBills.get(i); String range = (upcomingBill.range == null) ? "none" : upcomingBill.range; String testDay = (upcomingBill.legislativeDay == null) ? "future" : testFormat.format(upcomingBill.legislativeDay); String testDayRange = testDay + "-" + range; // a new header is needed if (!currentDayRange.equals(testDayRange)) { Date date = new Date(); // if no legislative_day, just call it the future if (testDay.equals("future")) { date.dateName = "SOMETIME"; } else { // specific day if (range.equals("day")) { if (today.equals(testDay)) date.dateName = "TODAY"; else if (tomorrow.equals(testDay)) date.dateName = "TOMORROW"; else date.dateName = dateNameFormat.format(upcomingBill.legislativeDay).toUpperCase(); date.dateFull = dateFullFormat.format(upcomingBill.legislativeDay).toUpperCase(); } // week of this day else if (range.equals("week")) { date.dateName = "WEEK OF"; date.dateFull = dateFullFormat.format(upcomingBill.legislativeDay).toUpperCase(); } // indefinite range, null or any other value (future-proof) else { // we're only here if range is indefinite, but legislative_day has a value // still, make no promises, this is an unexpected case date.dateName = "SOMETIME"; } } items.add(date); currentDayRange = testDayRange; } com.sunlightlabs.congress.models.Bill rootBill = upcomingBill.bill; if (rootBill == null) continue; Bill bill = new Bill(); bill.bill_type = rootBill.bill_type; bill.number = rootBill.number; String title; if (rootBill.short_title != null && !rootBill.short_title.equals("")) title = rootBill.short_title; else title = Utils.truncate(rootBill.official_title, 55); bill.title = title; bill.id = rootBill.id; items.add(bill); } return items; } } static class UpcomingBillsTask extends AsyncTask<String, Void, List<UpcomingBill>> { private UpcomingFragment context; private CongressException exception; public UpcomingBillsTask(UpcomingFragment context) { FragmentUtils.setupAPI(context); this.context = context; } @Override protected List<UpcomingBill> doInBackground(String... params) { try { return UpcomingBillService.comingUp(); } catch (CongressException e) { this.exception = new CongressException(e, "Error loading upcoming activity."); return null; } } @Override protected void onPostExecute(List<UpcomingBill> result) { if (result != null && exception == null) context.onLoadUpcomingBills(result); else context.onLoadUpcomingBills(exception); } } }