package com.sunlightlabs.android.congress.fragments;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.sunlightlabs.android.congress.LegislatorCosponsors;
import com.sunlightlabs.android.congress.R;
import com.sunlightlabs.android.congress.tasks.LoadBillTask;
import com.sunlightlabs.android.congress.tasks.LoadPhotoTask;
import com.sunlightlabs.android.congress.utils.Analytics;
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.Bill;
import com.sunlightlabs.congress.models.CongressException;
import com.sunlightlabs.congress.models.Legislator;
import com.sunlightlabs.congress.models.UpcomingBill;
public class BillInfoFragment extends Fragment implements LoadPhotoTask.LoadsPhoto, LoadBillTask.LoadsBill {
// fields from the intent
private Bill bill;
private Legislator sponsor;
private List<UpcomingBill> latestUpcoming;
// fields fetched remotely
private String summary;
private Drawable sponsorPhoto;
private static SimpleDateFormat timelineFormat = new SimpleDateFormat("MMM dd, yyyy");
public static BillInfoFragment create(Bill bill) {
BillInfoFragment frag = new BillInfoFragment();
Bundle args = new Bundle();
args.putSerializable("bill", bill);
frag.setArguments(args);
frag.setRetainInstance(true);
return frag;
}
public BillInfoFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentUtils.setupAPI(this);
bill = (Bill) getArguments().getSerializable("bill");
sponsor = bill.sponsor;
latestUpcoming = bill.upcoming;
loadSummary();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.bill, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupControls();
if (summary != null)
displaySummary();
if (sponsorPhoto != null)
displayPhoto();
}
public void setupControls() {
// if this was coming in from a search result and has associated highlight data, show it
if (bill.search != null && bill.search.highlight != null) {
String field = Bill.matchField(bill.search.highlight);
// don't bother showing the short title, or the official title if it's the official title being shown
if (field != null && !field.equals("popular_title") && !field.equals("keywords") && !field.equals("short_title") && !(field.equals("official_title") && bill.short_title == null)) {
View searchView = getView().findViewById(R.id.bill_search_data);
String matchText = "\"" + bill.search.query + "\" matched the bill's " + Bill.matchText(field) + ":";
String highlightText = Utils.truncate(bill.search.highlight.get(field).get(0), 300, false);
if (field.equals("versions") || field.equals("summary"))
highlightText = "..." + highlightText + "...";
((TextView) searchView.findViewById(R.id.match_field)).setText(matchText);
((TextView) searchView.findViewById(R.id.highlight_field)).setText(Html.fromHtml(highlightText));
searchView.setVisibility(View.VISIBLE);
}
}
TextView titleView = (TextView) getView().findViewById(R.id.title);
String title;
if (bill.short_title != null) {
title = Utils.truncate(bill.short_title, 400);
titleView.setTextSize(18);
} else if (bill.official_title != null) {
title = bill.official_title;
titleView.setTextSize(16);
} else {
title = getResources().getString(R.string.bill_no_title);
titleView.setTextSize(18);
}
titleView.setText(title);
addBillTimeline();
if (sponsor != null) {
View sponsorView = getView().findViewById(R.id.bill_sponsor);
String name = sponsor.title + ". " + sponsor.firstName() + " " + sponsor.last_name;
((TextView) sponsorView.findViewById(R.id.name)).setText(name);
String stateName = Utils.stateCodeToName(getContext(), sponsor.state);
String description = sponsor.party + " - " + stateName;
((TextView) sponsorView.findViewById(R.id.description)).setText(description);
sponsorView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(Utils.legislatorIntent(sponsor.bioguide_id));
}
});
// make container of sponsor and cosponsors visible
getView().findViewById(R.id.bill_all_sponsors).setVisibility(View.VISIBLE);
if (bill.cosponsors_count > 0) {
View cosponsorView = getView().findViewById(R.id.bill_cosponsors);
((TextView) cosponsorView.findViewById(R.id.bill_cosponsor_number)).setText("+ " + bill.cosponsors_count);
int otherName = (bill.cosponsors_count == 1) ? R.string.bill_cosponsor_other_singular : R.string.bill_cosponsor_other_plural;
((TextView) cosponsorView.findViewById(R.id.bill_cosponsor_others)).setText(otherName);
cosponsorView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), LegislatorCosponsors.class)
.putExtra("billId", bill.id)
.putExtra("bill", bill);
startActivity(intent);
}
});
getView().findViewById(R.id.bill_cosponsors_container).setVisibility(View.VISIBLE);
getView().findViewById(R.id.bill_sponsor_line).setVisibility(View.VISIBLE);
}
// kick off the photo loading task after the new bill data is all displayed
loadPhoto();
} else {
TextView noSponsor = (TextView) getView().findViewById(R.id.bill_no_sponsor);
noSponsor.setText(R.string.bill_no_sponsor);
noSponsor.setVisibility(View.VISIBLE);
}
// prepare the upcoming container if one is necessary
if (latestUpcoming != null && latestUpcoming.size() > 0) {
TextView upcomingHeader = (TextView) getView().findViewById(R.id.upcoming_header);
upcomingHeader.setText(R.string.upcoming_header);
upcomingHeader.setVisibility(View.VISIBLE);
ViewGroup upcomingContainer = (ViewGroup) getView().findViewById(R.id.upcoming_container);
for (int i=0; i<latestUpcoming.size(); i++)
upcomingContainer.addView(upcomingView(latestUpcoming.get(i)));
upcomingContainer.setVisibility(View.VISIBLE);
}
((TextView) getView().findViewById(R.id.summary_header)).setText(R.string.bill_summary_header);
View summaryLoading = getView().findViewById(R.id.summary_loading);
((TextView) summaryLoading.findViewById(R.id.loading_message)).setText("Loading summary...");
summaryLoading.setVisibility(View.VISIBLE);
}
public View upcomingView(final UpcomingBill upcoming) {
LayoutInflater inflater = getActivity().getLayoutInflater();
ViewGroup view = (ViewGroup) inflater.inflate(R.layout.bill_upcoming_item, null);
String text;
if (upcoming.range == null || upcoming.legislativeDay == null)
text = "SOMETIME";
else if (upcoming.range.equals("day"))
text = Utils.nearbyOrFullDate(upcoming.legislativeDay);
else if (upcoming.range.equals("week"))
text = "WEEK OF " + Utils.fullDate(upcoming.legislativeDay);
else
text = "SOMETIME";
((TextView) view.findViewById(R.id.date)).setText(text);
((TextView) view.findViewById(R.id.where)).setText(upcomingSource(upcoming.sourceType, upcoming.chamber));
View moreView = view.findViewById(R.id.more);
if (upcoming.sourceUrl != null) {
moreView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Analytics.billUpcomingMore(getActivity(), upcoming.sourceType);
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(upcoming.sourceUrl)));
}
});
} else
moreView.setVisibility(View.GONE);
return view;
}
public String upcomingSource(String type, String chamber) {
if (type.equals("senate_daily"))
return "On the Senate Floor";
else if (type.equals("house_daily") || type.equals("house_weekly") || type.equals("house_floor"))
return "On the House Floor";
// fallbacks, if we add more upcoming source types
else if (chamber.equals("senate"))
return "In the Senate";
else if (chamber.equals("house"))
return "In the House";
else // should never happen
return "In Congress";
}
public void displayPhoto() {
View sponsorView = getView().findViewById(R.id.bill_sponsor);
((ImageView) sponsorView.findViewById(R.id.picture)).setImageDrawable(sponsorPhoto);
}
public void displaySummary() {
getView().findViewById(R.id.summary_loading).setVisibility(View.GONE);
if (summary != null && summary.length() > 0) {
String formatted = Bill.formatSummary(summary, bill.short_title);
TextView summaryView = (TextView) getView().findViewById(R.id.bill_summary);
summaryView.setText(formatted);
summaryView.setVisibility(View.VISIBLE);
} else {
TextView noSummary = (TextView) getView().findViewById(R.id.bill_no_summary);
if (bill.versionUrls != null && bill.versionUrls.containsKey("html")) {
noSummary.setText(Html.fromHtml("No summary available.<br/><br/><a href=\""
+ bill.versionUrls.get("html")
+ "\">Read the official text.</a>"));
} else if (bill.versionUrls != null && bill.versionUrls.containsKey("pdf")) {
noSummary.setText(Html.fromHtml("No summary available.<br/><br/><a href=\""
+ bill.versionUrls.get("pdf")
+ "\">Read the official text (PDF).</a>"));
} else {
noSummary.setText(Html.fromHtml("No summary available.<br/><br/><a href=\""
+ bill.fallbackTextUrl()
+ "\">Read the text of this bill on GovTrack.us.</a>"));
}
noSummary.setMovementMethod(LinkMovementMethod.getInstance());
noSummary.setVisibility(View.VISIBLE);
}
}
public void loadSummary() {
new LoadBillTask(this, bill.id).execute("summary");
}
public void loadPhoto() {
new LoadPhotoTask(this, LegislatorImage.PIC_LARGE).execute(sponsor.bioguide_id);
}
public void onLoadBill(Bill bill) {
if (bill.summary != null)
this.summary = bill.summary;
else
this.summary = "";
if (isAdded())
displaySummary();
}
public void onLoadBill(CongressException exception) {
if (isAdded())
Utils.alert(getActivity(), R.string.error_connection);
}
public void onLoadPhoto(Drawable photo, Object tag) {
sponsorPhoto = photo;
if (isAdded())
displayPhoto();
}
// Take the layout view given, and append all applicable bill_event TextViews
// describing the basic timeline of the bill
public void addBillTimeline() {
ViewGroup inner = (ViewGroup) getView().findViewById(R.id.header_inner);
if (bill.introduced_on != null)
addTimelinePiece(inner, "Introduced on", bill.introduced_on.getTime());
String house_passage_result = bill.house_passage_result;
long house_passage_result_at = bill.house_passage_result_at == null ? 0 : bill.house_passage_result_at.getTime();
if (house_passage_result != null && house_passage_result_at > 0) {
if (house_passage_result.equals("pass"))
addTimelinePiece(inner, "Passed the House on", house_passage_result_at);
else if (house_passage_result.equals("fail"))
addTimelinePiece(inner, "Failed the House on", house_passage_result_at);
}
String senate_cloture_result = bill.senate_cloture_result;
long senate_cloture_result_at = bill.senate_cloture_result_at == null ? 0 : bill.senate_cloture_result_at.getTime();
if (senate_cloture_result != null && senate_cloture_result_at > 0) {
if (senate_cloture_result.equals("pass"))
addTimelinePiece(inner, "Passed cloture in the Senate on", senate_cloture_result_at);
else if (senate_cloture_result.equals("fail"))
addTimelinePiece(inner, "Failed cloture in the Senate on", senate_cloture_result_at);
}
String senate_passage_result = bill.senate_passage_result;
long senate_passage_result_at = bill.senate_passage_result_at == null ? 0 : bill.senate_passage_result_at.getTime();
if (senate_passage_result != null && senate_passage_result_at > 0) {
if (senate_passage_result.equals("pass"))
addTimelinePiece(inner, "Passed the Senate on", senate_passage_result_at);
else if (senate_passage_result.equals("fail"))
addTimelinePiece(inner, "Failed the Senate on", senate_passage_result_at);
}
long vetoed_at = bill.vetoed_at == null ? 0 : bill.vetoed_at.getTime();
if (bill.vetoed && vetoed_at > 0)
addTimelinePiece(inner, "Vetoed on", vetoed_at);
String house_override_result = bill.house_override_result;
long house_override_result_at = bill.house_override_result_at == null ? 0 : bill.house_override_result_at.getTime();
if (house_override_result != null && house_override_result_at > 0) {
if (house_override_result.equals("pass"))
addTimelinePiece(inner, "Override passed in the House on", house_override_result_at);
else if (house_override_result.equals("fail"))
addTimelinePiece(inner, "Override failed in the House on", house_override_result_at);
}
String senate_override_result = bill.senate_override_result;
long senate_override_result_at = bill.senate_override_result_at == null ? 0 : bill.senate_override_result_at.getTime();
if (senate_override_result != null && senate_override_result_at > 0) {
if (senate_override_result.equals("pass"))
addTimelinePiece(inner, "Override passed in the Senate on", senate_override_result_at);
else if (senate_override_result.equals("fail"))
addTimelinePiece(inner, "Override failed in the Senate on", senate_override_result_at);
}
long awaiting_signature_since = bill.awaiting_signature_since == null ? 0 : bill.awaiting_signature_since.getTime();
if (bill.awaiting_signature && awaiting_signature_since > 0)
addTimelinePiece(inner, "Awaiting signature since", awaiting_signature_since);
long enacted_at = bill.enacted_at == null ? 0 : bill.enacted_at.getTime();
if (bill.enacted && enacted_at > 0)
addTimelinePiece(inner, "Enacted on", enacted_at);
}
public void addTimelinePiece(ViewGroup container, String prefix, long timestamp) {
String date = prefix + " " + timelineFormat.format(new Date(timestamp));
TextView piece = (TextView) LayoutInflater.from(getActivity()).inflate(R.layout.bill_event, null);
piece.setText(date);
container.addView(piece);
}
public int sizeOfTitle(String title) {
int length = title.length();
if (length <= 100)
return 18;
else if (length <= 200)
return 16;
else if (length <= 300)
return 14;
else if (length <= 400)
return 12;
else // should be truncated above this anyhow
return 12;
}
public Context getContext() {
return getActivity();
}
}