package com.androsz.electricsleepbeta.app; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.database.StaleDataException; import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.text.format.DateUtils; import android.text.format.Time; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.ViewSwitcher; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import com.androsz.electricsleepbeta.R; import com.androsz.electricsleepbeta.db.SleepSession; import com.androsz.electricsleepbeta.widget.calendar.MonthView; import com.androsz.electricsleepbeta.widget.calendar.Utils; import com.viewpagerindicator.TitlePageIndicator; import com.viewpagerindicator.TitleProvider; public class HistoryMonthFragment extends AnalyticFragment implements LoaderManager.LoaderCallbacks<Cursor> { private static final String TAG = HistoryMonthFragment.class .getSimpleName(); private final class IndicatorPageChangeListener implements OnPageChangeListener { private final TitlePageIndicator indicator; private IndicatorPageChangeListener(TitlePageIndicator indicator) { this.indicator = indicator; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_IDLE) { MonthView leftMonth = (MonthView) monthPager.getChildAt(0); MonthView centerMonth = (MonthView) monthPager.getChildAt(1); MonthView rightMonth = (MonthView) monthPager.getChildAt(2); final Time oldCenterTime = new Time(centerMonth.getTime()); String[] newTitles = new String[3]; int focusedPage = HistoryMonthFragment.this.focusedPage; if (focusedPage == 0) { final Time oldTopTime = new Time(leftMonth.getTime()); final Time time = new Time(oldTopTime); time.month--; time.normalize(true); // TODO: load and switch shown events leftMonth.setTime(time); centerMonth.setTime(oldTopTime); rightMonth.setTime(oldCenterTime); } else if (focusedPage == 2) { final Time oldBottomTime = new Time(rightMonth.getTime()); final Time time = new Time(oldBottomTime); time.month++; time.normalize(true); leftMonth.setTime(oldCenterTime); centerMonth.setTime(oldBottomTime); rightMonth.setTime(time); } Activity a = getActivity(); newTitles[0] = Utils.formatMonthYear(a, leftMonth.getTime()); newTitles[1] = Utils.formatMonthYear(a, centerMonth.getTime()); newTitles[2] = Utils.formatMonthYear(a, rightMonth.getTime()); monthAdapter.setTitles(newTitles); // always set to middle page to continue to be able to // scroll up/down indicator.setCurrentItem(1, false); eventsChanged(focusedPage); } } @Override public void onPageSelected(int position) { focusedPage = position; } } private class MonthPagerAdapter extends PagerAdapter implements TitleProvider { private String[] titles = new String[] { "", "", "" }; public String[] getTitles() { return titles; } public void setTitles(String[] titles) { this.titles = titles.clone(); } public MonthView addMonthViewAt(ViewPager container, int position, Time time) { final MonthView mv = new MonthView(HistoryMonthFragment.this); mv.setLayoutParams(new ViewSwitcher.LayoutParams( android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.MATCH_PARENT)); mv.setSelectedTime(time); container.addView(mv, position); return mv; } @Override public void destroyItem(View container, int position, Object object) { // simply reuse items... // ((ViewPager) container).removeViewAt(position); } @Override public int getCount() { return titles.length; } @Override public String getTitle(int position) { String title = titles[position]; return title; } @Override public Object instantiateItem(View container, int position) { MonthView childAt = (MonthView) ((ViewPager) container) .getChildAt(position); if (childAt == null) { final Time time = new Time(); time.setToNow(); // set to first day in month. this prevents errors when the // current // month (TODAY) has more days than the neighbor month. time.set(1, time.month, time.year); time.month += (position - 1); // add the offset from the center // time time.normalize(true); MonthView mv = addMonthViewAt((ViewPager) container, position, time); titles[position] = Utils.formatMonthYear(getActivity(), mv.getTime()); return mv; } return childAt; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public void restoreState(Parcelable state, ClassLoader loader) { } @Override public Parcelable saveState() { return null; } @Override public void startUpdate(View container) { } boolean alreadyLoaded = false; @Override public void finishUpdate(View container) { if (!alreadyLoaded && ((ViewPager) container).getChildCount() == getCount()) { ((HostActivity) getActivity()).getSupportLoaderManager() .getLoader(0).forceLoad(); alreadyLoaded = true; } } } private class SessionsContentObserver extends ContentObserver { public SessionsContentObserver() { super(new Handler()); } @Override public void onChange(boolean selfChange) { ((HostActivity) getActivity()).getSupportLoaderManager() .getLoader(0).forceLoad(); super.onChange(selfChange); } } private static final int DAY_OF_WEEK_KINDS[] = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY }; private static final int DAY_OF_WEEK_LABEL_IDS[] = { R.id.day0, R.id.day1, R.id.day2, R.id.day3, R.id.day4, R.id.day5, R.id.day6 }; private ViewPager monthPager; private MonthPagerAdapter monthAdapter; private int startDay; List<Long[]> mSessions = new ArrayList<Long[]>(0); private int focusedPage = 0; private SessionsContentObserver sessionsObserver; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_DATE_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { eventsChanged(-1); } } }; void eventsChanged(final int whichPage) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { // if (whichPage == -1) { ViewPager vp = monthPager; for (int i = 0; i < vp.getChildCount(); i++) { final MonthView mv = (MonthView) monthPager.getChildAt(i); Time t = mv.getTime(); mv.forceReloadEvents(mSessions);/* * getSessionsInInterval(t. * toMillis(true), 31)); */ } // } else { // final MonthView mv = (MonthView) // monthPager.getChildAt(whichPage); // Time t = mv.getTime(); // mv.forceReloadEvents(getSessionsInInterval(mv.getTime().toMillis(true), // 31)); // } } }); } public ArrayList<Long[]> getSessionsInInterval(long startMillis, int days) { final ArrayList<Long[]> sessions = new ArrayList<Long[]>(20); final Time local = new Time(); local.set(startMillis); // expand start and days to include days shown from previous month // and next month. can be slightly wasteful. // start -= 1000 * 60 * 60 * 24 * 7; // 7 days // days += 7; final int startJulianDay = Time.getJulianDay(startMillis, local.gmtoff); local.monthDay += days; local.normalize(true); final int endJulianDay = Time.getJulianDay(local.toMillis(true), local.gmtoff); Log.d(TAG, "Getting interval from: " + startJulianDay + " to: " + endJulianDay); synchronized (mSessions) { for (final Long[] session : mSessions) { final long sessionStartJulianDay = session[2]; if (sessionStartJulianDay >= startJulianDay && sessionStartJulianDay < endJulianDay) { Log.d(TAG, "Adding session: " + session); sessions.add(session); } } // TODO ? // SleepSession.computePositions(sessions.values()); return sessions; } } public int getStartDay() { return startDay; } /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); HostActivity a = (HostActivity) getActivity(); sessionsObserver = new SessionsContentObserver(); a.getContentResolver().registerContentObserver( SleepSession.CONTENT_URI, true, sessionsObserver); a.getSupportLoaderManager().initLoader(0, null, HistoryMonthFragment.this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { getSherlockActivity().setSupportProgressBarIndeterminateVisibility(true); View root = inflater.inflate(R.layout.fragment_history_month, container, false); // Get first day of week based on locale and populate the day headers startDay = Calendar.getInstance().getFirstDayOfWeek(); final int diff = startDay - Calendar.SUNDAY - 1; final int startDay = Utils.getFirstDayOfWeek(); for (int day = 0; day < 7; day++) { final String dayString = DateUtils.getDayOfWeekString( (DAY_OF_WEEK_KINDS[day] + diff) % 7 + 1, DateUtils.LENGTH_MEDIUM); final TextView label = (TextView) root.findViewById(DAY_OF_WEEK_LABEL_IDS[day]); label.setText(dayString); } monthAdapter = new MonthPagerAdapter(); monthPager = (ViewPager) root.findViewById(R.id.monthpager); monthPager.setAdapter(monthAdapter); final TitlePageIndicator indicator = (TitlePageIndicator) root.findViewById(R.id.indicator); indicator.setFooterColor(getResources().getColor(R.color.primary1)); indicator.setViewPager(monthPager, 1); indicator.setOnPageChangeListener(new IndicatorPageChangeListener( indicator)); return root; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new CursorLoader(getActivity(), SleepSession.CONTENT_URI, null, null, null, null); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater mi) { mi.inflate(R.menu.menu_history_calendar, menu); super.onCreateOptionsMenu(menu, mi); } @Override public void onDestroy() { super.onDestroy(); getActivity().getContentResolver().unregisterContentObserver( sessionsObserver); } @Override public void onLoaderReset(Loader<Cursor> loader) { } @Override public void onLoadFinished(Loader<Cursor> loader, final Cursor data) { new Thread(new Runnable() { // @Override public void run() { // android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); try { mSessions = new ArrayList<Long[]>(0); mSessions = SleepSession.getStartEndTimestamps(data); } catch (IllegalArgumentException ex) { Log.d(TAG, "Failure to provide proper arguments when accessing session data.", ex); } catch (IllegalStateException ex) { Log.d(TAG, "Sleep sessions in illegal state.", ex); } catch (StaleDataException ex) { Log.d(TAG, "Sleep session data was stale.", ex); } finally { eventsChanged(-1); } } }).start(); getSherlockActivity().setSupportProgressBarIndeterminateVisibility(false); } @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case R.id.menu_list: getSherlockActivity().getSupportFragmentManager() .beginTransaction() .replace(android.R.id.content, new HistoryListFragment()) .commit(); return true; } return super.onOptionsItemSelected(item); } @Override public void onPause() { super.onPause(); getActivity().unregisterReceiver(mIntentReceiver); } @Override public void onResume() { super.onResume(); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_DATE_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); getActivity().registerReceiver(mIntentReceiver, filter); } }