package org.ohmage.fragments; import android.database.Cursor; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import org.achartengine.GraphicalView; import org.ohmage.AccountHelper; import org.ohmage.R; import org.ohmage.UserPreferencesHelper; import org.ohmage.Utilities; import org.ohmage.charts.Histogram; import org.ohmage.charts.HistogramBase.HistogramRenderer; import org.ohmage.loader.MobilityAggregateLoader; import org.ohmage.loader.PromptFeedbackLoader.FeedbackItem; import org.ohmage.mobility.glue.MobilityInterface; import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.LinkedList; import java.util.List; import java.util.Locale; public class RecentMobilityChartFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { private static final int MAX_DATA_COLUMNS = 10; private static final int LOAD_MOBILITY_DATA = 0; private static final int LOAD_MOBILITY_BASELINE_AGGREGATE = 1; private static final int LOAD_MOBILITY_LASTWEEK_AGGREGATE = 2; private ViewGroup mContainer; private TextView mTodayStill; private TextView mTodayWalk; private TextView mTodayRun; private TextView mTodayDrive; private AccountHelper mAccount; private TextView mAggregateBaseline; private TextView mAggregateLastweek; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mAccount = new AccountHelper(getActivity()); getLoaderManager().initLoader(LOAD_MOBILITY_DATA, null, this); getLoaderManager().initLoader(LOAD_MOBILITY_BASELINE_AGGREGATE, null, this); getLoaderManager().initLoader(LOAD_MOBILITY_LASTWEEK_AGGREGATE, null, this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.recent_mobility_activity_fragment_layout, container, false); mContainer = (ViewGroup) view.findViewById(R.id.feedback_response_graph); mTodayStill = (TextView) view.findViewById(R.id.recent_mobility_still); mTodayWalk = (TextView) view.findViewById(R.id.recent_mobility_walk); mTodayRun = (TextView) view.findViewById(R.id.recent_mobility_run); mTodayDrive = (TextView) view.findViewById(R.id.recent_mobility_drive); mAggregateBaseline = (TextView) view.findViewById(R.id.aggregate_baseline_count); mAggregateLastweek = (TextView) view.findViewById(R.id.aggregate_lastweek_count); return view; } public interface MobilityQuery { String[] PROJECTION = { MobilityInterface.KEY_DAY, MobilityInterface.KEY_MODE, MobilityInterface.KEY_DURATION }; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { switch(id) { case LOAD_MOBILITY_DATA: return new CursorLoader(getActivity(), MobilityInterface.AGGREGATES_URI, MobilityQuery.PROJECTION, MobilityInterface.KEY_DAY + " <= date('now', 'localtime')" + " AND " + MobilityInterface.KEY_DAY + " > date('now', 'localtime', '-10 days') AND " + MobilityInterface.KEY_USERNAME + "=?", new String[] { mAccount.getUsername() }, MobilityInterface.KEY_DAY + " DESC"); case LOAD_MOBILITY_BASELINE_AGGREGATE: return new MobilityAggregateLoader(getActivity(), UserPreferencesHelper.getBaseLineStartTime(getActivity()), UserPreferencesHelper.getBaseLineEndTime(getActivity()), mAccount.getUsername()); case LOAD_MOBILITY_LASTWEEK_AGGREGATE: Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -7); long endtime = cal.getTimeInMillis(); cal.add(Calendar.DATE, -7); return new MobilityAggregateLoader(getActivity(), cal.getTimeInMillis(), endtime, mAccount.getUsername()); } return null; } @Override public void onLoaderReset(Loader<Cursor> loader) { if(LOAD_MOBILITY_DATA == loader.getId()) { mTodayStill.setText(null); mTodayWalk.setText(null); mTodayRun.setText(null); mTodayDrive.setText(null); } if(LOAD_MOBILITY_BASELINE_AGGREGATE == loader.getId()) { mAggregateBaseline.setText(R.string.empty_value); } else if(LOAD_MOBILITY_LASTWEEK_AGGREGATE == loader.getId()) { mAggregateLastweek.setText(R.string.empty_value); } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if(data == null) return; // Move to the beginning of the data data.moveToPosition(-1); if(LOAD_MOBILITY_DATA == loader.getId()) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); List<FeedbackItem> values = new LinkedList<FeedbackItem>(); for (int i = 0; i < MAX_DATA_COLUMNS; i++) { FeedbackItem point = new FeedbackItem(); point.time = System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS * i; point.value = 0.0; // Count up the data for each day try { while(data.moveToNext() && DateUtils.isToday(formatter.parse(data.getString(0)).getTime() + DateUtils.DAY_IN_MILLIS * i)) { String mode = data.getString(1); if(MobilityInterface.WALK.equals(mode) || MobilityInterface.RUN.equals(mode) || MobilityInterface.BIKE.equals(mode)) point.value+= data.getDouble(2); // If this came from today we should set the correct textview if (DateUtils.isToday(formatter.parse(data.getString(0)).getTime())) { if (MobilityInterface.STILL.equals(mode)) mTodayStill.setText(formatTimeAmount(data.getLong(2))); else if (MobilityInterface.WALK.equals(mode)) mTodayWalk.setText(formatTimeAmount(data.getLong(2))); else if (MobilityInterface.RUN.equals(mode)) mTodayRun.setText(formatTimeAmount(data.getLong(2))); else if (MobilityInterface.DRIVE.equals(mode)) mTodayDrive.setText(formatTimeAmount(data.getLong(2))); } } } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } // Change the value from ms to hours point.value /= DateUtils.HOUR_IN_MILLIS; values.add(point); data.moveToPrevious(); } HistogramRenderer renderer = new HistogramRenderer(getActivity()); renderer.setMargins(new int[] { Utilities.dpToPixels(20), Utilities.dpToPixels(30), Utilities.dpToPixels(10), Utilities.dpToPixels(10) }); renderer.setYLabels(5); renderer.setYAxisMin(0); renderer.setYTitle("# of Hours"); renderer.setInScroll(true); mContainer.removeAllViews(); mContainer.addView(new GraphicalView(getActivity(), new Histogram(getActivity(), renderer, values, MAX_DATA_COLUMNS))); } else if(LOAD_MOBILITY_BASELINE_AGGREGATE == loader.getId()) { long time = ((MobilityAggregateLoader) loader).getTimeActive(); if(time != 0) mAggregateBaseline.setText(formatTimeAmount(time)); } else if(LOAD_MOBILITY_LASTWEEK_AGGREGATE == loader.getId()) { long time = ((MobilityAggregateLoader) loader).getTimeActive(); if(time != 0) mAggregateLastweek.setText(formatTimeAmount(time)); } } /** * Formats the time amount if its less than one hour it will return the * minutes. If it is greater than an hour it will return the number of * hours with one decimal point. * * @param time * @return */ private CharSequence formatTimeAmount(long time) { if(time / DateUtils.HOUR_IN_MILLIS < 1) { return (time / DateUtils.MINUTE_IN_MILLIS) + " min"; } else { NumberFormat numberFormatter = NumberFormat.getNumberInstance(Locale.US); numberFormatter.setMaximumFractionDigits(1); return numberFormatter.format(time / new Double(DateUtils.HOUR_IN_MILLIS)) + " h"; } } }