package org.ohmage.fragments; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Color; import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.support.v4.content.Loader; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.GridView; import android.widget.TextSwitcher; import android.widget.TextView; import android.widget.ViewSwitcher; import org.ohmage.R; import org.ohmage.activity.ResponseListActivity; import org.ohmage.db.DbContract.Campaigns; import org.ohmage.db.DbContract.Responses; import org.ohmage.logprobe.Analytics; import org.ohmage.ui.OhmageFilterable.CampaignFilter; import org.ohmage.ui.OhmageFilterable.CampaignSurveyFilter; import org.ohmage.ui.OhmageFilterable.TimeFilter; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; /** * <p>The {@link ResponseHistoryCalendarFragment} shows a calendar view of a single month of responses.</p> * * <p>The {@link ResponseHistoryCalendarFragment} accepts {@link CampaignFilter#EXTRA_CAMPAIGN_URN}, {@link CampaignSurveyFilter# EXTRA_SURVEY_ID}, * {@link TimeFilter#EXTRA_MONTH}, and {@link TimeFilter#EXTRA_YEAR} as extras</p> * @author cketcham * */ public class ResponseHistoryCalendarFragment extends FilterableFragment { private MonthCellAdapter mAdapter; private TextSwitcher mTextSwitcher; private GridView mCalendarView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAdapter = new MonthCellAdapter(getActivity(), R.id.calendar_gridcell_num_responses); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle args) { View view = inflater.inflate(R.layout.calendar_view, container, false); //Summary Text Switcher mTextSwitcher = (TextSwitcher) view.findViewById(R.id.summary_text_switcher); mTextSwitcher.setFactory(new ViewSwitcher.ViewFactory() { @Override public View makeView() { TextView textview = new TextView(getActivity()); textview.setTextColor(Color.WHITE); textview.setBackgroundColor(Color.DKGRAY); textview.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); textview.setTypeface(null, Typeface.ITALIC); textview.setGravity(Gravity.CENTER); int padding_in_dp = 3; final float scale = getResources().getDisplayMetrics().density; int padding_in_px = (int) (padding_in_dp * scale + 0.5f); textview.setPadding(0, 0, 0, padding_in_px); return textview; } }); mTextSwitcher.setInAnimation(getActivity(), android.R.anim.fade_in); mTextSwitcher.setOutAnimation(getActivity(), android.R.anim.fade_out); mCalendarView = (GridView) view.findViewById(R.id.calendar); mCalendarView.setAdapter(mAdapter); return view; } protected static class ResponseCalendarQuery { public static final String[] PROJECTION = new String[] { Responses._ID, Responses.RESPONSE_TIME }; public static final int ID = 0; public static final int TIME = 1; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new ResponseLoader(this, ResponseCalendarQuery.PROJECTION).onCreateLoader(id, args); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { mAdapter.setMonth(getMonth(), getYear()); mAdapter.swapCursor(cursor); } @Override public void onLoaderReset(Loader<Cursor> loader) { mAdapter.swapCursor(null); } public void onDateClicked(int date) { Intent intent = new Intent(getActivity(), ResponseListActivity.class); if(getCampaignUrn() != null) intent.putExtra(CampaignFilter.EXTRA_CAMPAIGN_URN, getCampaignUrn()); if(getSurveyId() != null) intent.putExtra(CampaignSurveyFilter.EXTRA_SURVEY_ID, getSurveyId()); intent.putExtra(TimeFilter.EXTRA_DAY, date); intent.putExtra(TimeFilter.EXTRA_MONTH, getMonth()); intent.putExtra(TimeFilter.EXTRA_YEAR, getYear()); startActivity(intent); } public class MonthCellAdapter extends BaseAdapter implements OnClickListener { private static final String tag = "GridCellAdapter"; private final Context mContext; private final ArrayList<String> list = new ArrayList<String>(); private HashMap<String,Integer> eventsPerMonthMap = new HashMap<String,Integer>(); private final String[] months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; private Button numOfResponsesDisplay; private TextView dateDisplay; private final int currentDayOfMonth; private final int currentMonth; private final int currentYear; private int mMonth; private int mYear; private Cursor mCursor; // Days in Current Month public MonthCellAdapter(Context context, int textViewResourceId) { super(); mContext = context; Calendar calendar = Calendar.getInstance(); currentDayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); currentMonth = calendar.get(Calendar.MONTH); currentYear = calendar.get(Calendar.YEAR); } public void setMonth(int month, int year) { mMonth = month; mYear = year; } public void swapCursor(Cursor cursor) { if(mCursor != cursor && mCursor != null) mCursor.close(); mCursor = cursor; if(mCursor != null) { printMonth(mMonth, mYear); eventsPerMonthMap = findNumberOfEventsPerMonth(mCursor); notifyDataSetChanged(); } } /** * Prints Month */ private void printMonth(int mm, int yy) { list.clear(); Calendar c = Calendar.getInstance(); c.set(Calendar.MONTH, mm); c.set(Calendar.YEAR, yy); int daysInMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH); c.set(Calendar.DATE, 1); int firstDayOfMonth = c.get(Calendar.DAY_OF_WEEK) - 1; c.add(Calendar.MONTH, -1); int daysInPrevMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH); // Previous month days for (int i = daysInPrevMonth - firstDayOfMonth; i < daysInPrevMonth; i++) list.add((i+1) + "-OUTOFTHISMONTH"); // Current Month Days for (int i = 1; i <= daysInMonth; i++) { if (currentYear == yy && currentMonth == mm && i == currentDayOfMonth) list.add(i + "-TODAY"); else list.add(i + "-THISMONTH"); } // Next Month days for (int i = 0; i < list.size() % 7; i++) list.add((i+1) + "-OUTOFTHISMONTH"); } /** * NOTE: YOU NEED TO IMPLEMENT THIS PART Given the YEAR, MONTH, retrieve * ALL entries from a SQLite database for that month. Iterate over the * List of All entries, and get the dateCreated, which is converted into * day. * * @param year * @param month * @return */ private HashMap<String, Integer> findNumberOfEventsPerMonth(Cursor cursor) { ContentResolver cr = getActivity().getContentResolver(); //Create Uri Uri uri; if(getCampaignUrn() == null){ uri = Responses.CONTENT_URI; } else { if(getSurveyId() == null) uri = Campaigns.buildResponsesUri(getCampaignUrn()); else uri = Campaigns.buildResponsesUri(getCampaignUrn(), getSurveyId()); } HashMap<String, Integer> map = new HashMap<String, Integer>(); Calendar cal = Calendar.getInstance(); int numOfResponse = 0; for(cursor.moveToFirst();!cursor.isAfterLast();cursor.moveToNext()){ Long time = cursor.getLong(ResponseCalendarQuery.TIME); cal.setTimeInMillis(time); Integer responseDay = new Integer(cal.get(Calendar.DAY_OF_MONTH)); if(map.containsKey(responseDay.toString())){ Integer value = map.get(responseDay.toString()) + 1; map.put(responseDay.toString(), value); } else{ map.put(responseDay.toString(),1); } numOfResponse ++; } Cursor responseCursorTotal = cr.query(uri, new String[] { "_id" }, null, null, null); mTextSwitcher.setText(getMonthAsString(mMonth) + ": " + numOfResponse + " / Total: " + responseCursorTotal.getCount()); responseCursorTotal.close(); return map; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; if (row == null) { LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); row = inflater.inflate(R.layout.calendar_view_gridcell, parent, false); } // Get a reference to the Day gridcell numOfResponsesDisplay = (Button) row.findViewById(R.id.calendar_gridcell_num_responses); // ACCOUNT FOR SPACING String[] day_color = list.get(position).split("-"); String theday = day_color[0]; // Set the Day GridCell if (eventsPerMonthMap.containsKey(theday)) { numOfResponsesDisplay.setText(eventsPerMonthMap.get(theday).toString()); numOfResponsesDisplay.setTag(theday); numOfResponsesDisplay.setOnClickListener(this); } else { numOfResponsesDisplay.setText(null); numOfResponsesDisplay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Analytics.widget(v, "Calendar Date (No responses)"); } }); } dateDisplay = (TextView) row.findViewById(R.id.calendar_gridcell_date); dateDisplay.setBackgroundColor(Color.WHITE); dateDisplay.setText(theday); numOfResponsesDisplay.setTextColor(Color.BLACK); numOfResponsesDisplay.setBackgroundResource(R.drawable.calendar_button_selector); if (day_color[1].equals("OUTOFTHISMONTH")) { dateDisplay.setTextColor(Color.LTGRAY); dateDisplay.setBackgroundColor(Color.WHITE); numOfResponsesDisplay.setTextColor(Color.WHITE); numOfResponsesDisplay.setBackgroundColor(Color.WHITE); } if (day_color[1].equals("THISMONTH")) { dateDisplay.setTextColor(Color.WHITE); dateDisplay.setBackgroundColor(Color.DKGRAY); } if (day_color[1].equals("TODAY")) { dateDisplay.setTextColor(Color.WHITE); dateDisplay.setBackgroundColor(Color.DKGRAY); numOfResponsesDisplay.setBackgroundResource(R.drawable.calendar_button_selector_today); } return row; } @Override public void onClick(View view) { Analytics.widget(view, "Calendar Date"); if(view.getTag() instanceof String) { try { onDateClicked(Integer.valueOf((String) view.getTag())); } catch (NumberFormatException e) { e.printStackTrace(); } } } private String getMonthAsString(int i) { return months[i]; } @Override public String getItem(int position) { return list.get(position); } @Override public int getCount() { return list.size(); } } }