package com.sunlightlabs.android.congress.fragments; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.regex.Pattern; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.text.util.Linkify; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; import com.sunlightlabs.android.congress.R; import com.sunlightlabs.android.congress.notifications.Footer; import com.sunlightlabs.android.congress.notifications.Subscription; import com.sunlightlabs.android.congress.utils.DateAdapterHelper; import com.sunlightlabs.android.congress.utils.FragmentUtils; import com.sunlightlabs.android.congress.utils.PaginationListener; import com.sunlightlabs.android.congress.utils.Utils; import com.sunlightlabs.congress.models.CongressException; import com.sunlightlabs.congress.models.FloorUpdate; import com.sunlightlabs.congress.services.FloorUpdateService; public class FloorUpdateFragment extends ListFragment implements PaginationListener.Paginates, DateAdapterHelper.StickyHeader { public static final int PER_PAGE = 20; private String chamber; private List<FloorUpdate> updates; FloorUpdateAdapter adapterHelper; PaginationListener pager; View loadingView; public static FloorUpdateFragment forChamber(String chamber) { FloorUpdateFragment frag = new FloorUpdateFragment(); Bundle args = new Bundle(); args.putString("chamber", chamber); frag.setArguments(args); frag.setRetainInstance(true); return frag; } public FloorUpdateFragment() {} @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); chamber = getArguments().getString("chamber"); loadUpdates(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View main = inflater.inflate(R.layout.list_footer, container, false); return main; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setupControls(); if (updates != null) displayUpdates(); } @Override public void onResume() { super.onResume(); if (updates != null) setupSubscription(); } public void setupControls() { ((Button) getView().findViewById(R.id.refresh)).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { refresh(); } }); adapterHelper = new FloorUpdateAdapter(this); LayoutInflater inflater = LayoutInflater.from(getActivity()); loadingView = inflater.inflate(R.layout.loading_page, null); loadingView.setVisibility(View.GONE); getListView().addFooterView(loadingView); pager = new PaginationListener(this); adapterHelper.setOnScrollListener(pager); FragmentUtils.setLoading(this, R.string.floor_updates_loading); } private void refresh() { updates = null; FragmentUtils.setLoading(this, R.string.floor_updates_loading); FragmentUtils.showLoading(this); loadUpdates(); } public void loadUpdates() { new LoadUpdatesTask(this, chamber, 1).execute(); } @Override public void loadNextPage(int page) { adapterHelper.setOnScrollListener(null); loadingView.setVisibility(View.VISIBLE); new LoadUpdatesTask(this, chamber, page).execute(); } public void onLoadUpdates(List<FloorUpdate> updates, int page) { if (!isAdded()) return; if (page == 1) { this.updates = updates; displayUpdates(); } else { this.updates.addAll(updates); adapterHelper.notifyDataSetChanged(); loadingView.setVisibility(View.GONE); } // only re-enable the pagination if we got a full page back if (updates.size() == PER_PAGE) adapterHelper.setOnScrollListener(pager); } public void onLoadUpdates(CongressException exception) { if (isAdded()) FragmentUtils.showRefresh(this, R.string.floor_updates_error); } public void displayUpdates() { if (updates.size() > 0) { setListAdapter(adapterHelper.adapterFor(updates)); setupSubscription(); } else FragmentUtils.showRefresh(this, R.string.floor_updates_error); // should not happen } private void setupSubscription() { Footer.setup(this, new Subscription(chamber, chamber, "FloorUpdatesSubscriber", chamber), updates); } static class FloorUpdateAdapter extends DateAdapterHelper<FloorUpdate> { static SimpleDateFormat timeFormat = new SimpleDateFormat("h:mm aa"); public FloorUpdateAdapter(ListFragment context) { super(context); } @Override public Date dateFor(FloorUpdate update) { return update.timestamp; } @Override public void updateStickyHeader(Date date, View view, TextView left, TextView right) { String time = timeFormat.format(date); left.setText(Utils.nearbyOrFullDate(date)); right.setText(time); } @Override public View contentView(FloorUpdate update, boolean showTime) { View view = inflater.inflate(R.layout.floor_update, null); view.setEnabled(false); TextView timeView = (TextView) view.findViewById(R.id.timestamp); timeView.setText(timeFormat.format(update.timestamp)); if (update.update != null) { TextView text = (TextView) view.findViewById(R.id.text); text.setText(update.update); GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(update.timestamp); int year = calendar.get(Calendar.YEAR); Linkify.addLinks(text, Pattern.compile("(S\\.|H\\.)(\\s?J\\.|\\s?R\\.|\\s?Con\\.| ?)(\\s?Res\\.)*\\s?\\d+", Pattern.CASE_INSENSITIVE), "congress://com.sunlightlabs.android.congress/bill/" + update.congress+ "/"); Linkify.addLinks(text, Pattern.compile("Roll (?:no.|Call) (\\d+)"), "congress://com.sunlightlabs.android.congress/roll/" + update.chamber + "/" + year + "/"); } if (showTime) timeView.setVisibility(View.VISIBLE); else timeView.setVisibility(View.INVISIBLE); return view; } } private static class LoadUpdatesTask extends AsyncTask<Void, Void, List<FloorUpdate>> { private FloorUpdateFragment context; private CongressException exception; int page; String chamber; public LoadUpdatesTask(FloorUpdateFragment context, String chamber, int page) { FragmentUtils.setupAPI(context); this.context = context; this.page = page; this.chamber = chamber; } @Override protected List<FloorUpdate> doInBackground(Void... nothing) { List<FloorUpdate> updates = new ArrayList<FloorUpdate>(); try { updates = FloorUpdateService.latest(chamber, page, PER_PAGE); } catch (CongressException e) { Log.e(Utils.TAG, "Error while loading floor updates for " + chamber + ": " + e.getMessage()); this.exception = e; return null; } return updates; } @Override protected void onPostExecute(List<FloorUpdate> updates) { if (updates == null && exception != null) context.onLoadUpdates(exception); else context.onLoadUpdates(updates, page); } } }