org.apps8os.motivator.ui.MoodHistoryActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.apps8os.motivator.ui.MoodHistoryActivity.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Helsingin Diakonissalaitos and the authors
 * 
 * The MIT License (MIT)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 
 * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 ******************************************************************************/
package org.apps8os.motivator.ui;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

import org.apps8os.motivator.R;
import org.apps8os.motivator.data.DayDataHandler;
import org.apps8os.motivator.data.DayInHistory;
import org.apps8os.motivator.data.Sprint;
import org.apps8os.motivator.data.SprintDataHandler;
import org.apps8os.motivator.utils.UtilityMethods;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.DatePicker;

import com.github.amlcurran.showcaseview.ShowcaseView;
import com.github.amlcurran.showcaseview.targets.ActionItemTarget;
import com.github.amlcurran.showcaseview.targets.ViewTarget;
import com.viewpagerindicator.TitlePageIndicator;

/**
 * Represents the mood history of the user. In this activity, the functionality and layout
 * changes based on the orientation. Portrait orientation has the mood history of the user by day.
 * @author Toni Jrvinen
 *
 */

public class MoodHistoryActivity extends Activity {

    public static final String FRAGMENT_POSITION = "fragment_position";

    private static final long SELECT_ATTRIBUTE_HELP_ID = 10001;
    private static final long TIMELINE_HELP_ID = 10002;

    private DayDataHandler mDayDataHandler;
    private ViewPager mViewPager;
    private FragmentDatePagerAdapter mPagerAdapterDay;
    private FragmentWeekPagerAdapter mPagerAdapterWeek;
    private Sprint mCurrentSprint;
    private Locale mLocale;
    private SimpleDateFormat mDateFormat;

    private static long mSprintStartDateInMillis = 1393632000000L;
    private static int mDaysInSprint = 200;
    private int mNumberOfTodayInSprint;
    private int mNumberOfWeeksInSprint;
    private int mSelectedDay;
    private int mSelectedWeek;
    private String[] mDayPageTitles;
    private String[] mWeekPageTitles;
    private Menu mMenu;
    private Resources mRes;

    private TitlePageIndicator titleIndicator;
    private Calendar mStartDate;
    private int mSelectedAttribute = DayInHistory.AMOUNT_OF_DRINKS;

    private static long mSprintEndDateInMillis;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mood_history);
        mDayDataHandler = new DayDataHandler(this);

        mCurrentSprint = getIntent().getExtras().getParcelable(Sprint.CURRENT_SPRINT);
        mRes = getResources();
        mSprintStartDateInMillis = mCurrentSprint.getStartTime();
        mSprintEndDateInMillis = mCurrentSprint.getEndTime();

        mDaysInSprint = mCurrentSprint.getDaysInSprint();
        ActionBar actionBar = getActionBar();

        Calendar mToday = Calendar.getInstance();
        UtilityMethods.setToDayStart(mToday);
        mToday.setFirstDayOfWeek(Calendar.MONDAY);

        mNumberOfTodayInSprint = mCurrentSprint.getCurrentDayOfTheSprint();

        // Check if the sprint is already over.
        if (mNumberOfTodayInSprint > mDaysInSprint) {
            mNumberOfTodayInSprint = mDaysInSprint;
        }
        actionBar.setSubtitle(mCurrentSprint.getSprintTitle());
        actionBar.setTitle(mNumberOfTodayInSprint + " " + getString(R.string.days));
        actionBar.setBackgroundDrawable(mRes.getDrawable(R.drawable.action_bar_orange));

        mStartDate = Calendar.getInstance();
        mStartDate.setFirstDayOfWeek(Calendar.MONDAY);
        mStartDate.setTimeInMillis(mSprintStartDateInMillis);

        // Calculate the number of weeks in the sprint
        mNumberOfWeeksInSprint = mToday.get(Calendar.WEEK_OF_YEAR) - mStartDate.get(Calendar.WEEK_OF_YEAR) + 1;
        if (mNumberOfWeeksInSprint < 0) {
            mNumberOfWeeksInSprint = 52 + 1 - mStartDate.get(Calendar.WEEK_OF_YEAR)
                    + mToday.get(Calendar.WEEK_OF_YEAR);
        } else {

        }

        // Sets the default selections as today or this week.
        mSelectedDay = mNumberOfTodayInSprint - 1;
        mSelectedWeek = mNumberOfWeeksInSprint - 1;
        mViewPager = (ViewPager) findViewById(R.id.activity_mood_history_viewpager);

        titleIndicator = (TitlePageIndicator) findViewById(R.id.indicator);

        // Page change listener to keep the selected week and day in a member.
        titleIndicator.setOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageScrollStateChanged(int arg0) {
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
            }

            @Override
            public void onPageSelected(int arg0) {
                if (mRes.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
                    mSelectedDay = arg0;
                } else if (mRes.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    mSelectedWeek = arg0;
                    MoodHistoryWeekFragment fragment = mPagerAdapterWeek.getWeekFragment(arg0);
                    if (fragment != null) {
                        fragment.updateSelectedAttribute(mSelectedAttribute, false);
                    }
                }
            }
        });

        setPageTitles();

        // Load correct layout and functionality based on orientation
        if (mRes.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            loadPortraitView();
        } else if (mRes.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            loadLandscapeView();
        }

    }

    /**
     * Loading different view when orientation is portrait or landscape.
     */
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Selecting the different attributes in the week view, not visible in portrait day view.
        if (mMenu != null) {
            MenuItem selectAttribute = mMenu.findItem(R.id.mood_history_select_attribute);
            if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
                loadPortraitView();
                selectAttribute.setVisible(false);
            } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                loadLandscapeView();
                selectAttribute.setVisible(true);

                new ShowcaseView.Builder(this, true)
                        .setTarget(new ActionItemTarget(this, R.id.mood_history_select_attribute))
                        .setContentTitle("Tlt voit valita mit kuvaajassa nytetn")
                        .setContentText("Klikkaamalla tst voit vaihtaa mit kuvaajassa nytetn.")
                        .hideOnTouchOutside().setStyle(R.style.ShowcaseView).singleShot(SELECT_ATTRIBUTE_HELP_ID)
                        .build();
            }
        }
    }

    /**
     * Loading the action bar menu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.mood_history, menu);
        mMenu = menu;
        MenuItem selectAttribute = mMenu.findItem(R.id.mood_history_select_attribute);
        if (mRes.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            selectAttribute.setVisible(false);
        } else {
            selectAttribute.setVisible(true);
        }
        return super.onCreateOptionsMenu(menu);
    }

    /**
     * Actions for the menu items.
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        MoodHistoryWeekFragment weekFragment;
        switch (item.getItemId()) {
        // Search functionality for the search button
        case R.id.mood_history_search:
            // Spawn a dialog fragment so that the user can select a day.
            DialogFragment dialog = new DatePickerFragment();
            dialog.show(getFragmentManager(), "datePicker");
            return true;
        case R.id.mood_history_attribute_drinking:
            // Setting the selected attribute in landscape view.
            mSelectedAttribute = DayInHistory.AMOUNT_OF_DRINKS;
            weekFragment = mPagerAdapterWeek.getWeekFragment(mViewPager.getCurrentItem());
            weekFragment.updateSelectedAttribute(DayInHistory.AMOUNT_OF_DRINKS, false);
            return true;
        case R.id.mood_history_attribute_moods:
            mSelectedAttribute = DayInHistory.MOODS;
            weekFragment = mPagerAdapterWeek.getWeekFragment(mViewPager.getCurrentItem());
            weekFragment.updateSelectedAttribute(DayInHistory.MOODS, false);
            return true;
        case R.id.mood_history_change_sprint:
            // Spawn a dialog where the user can select the sprint depicted in this history.
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            final Sprint[] sprints = new SprintDataHandler(this).getSprints();

            // Get the string representations of the sprints.
            String[] sprintsAsString = new String[sprints.length];
            for (int i = 0; i < sprints.length; i++) {
                sprintsAsString[i] = sprints[i].getSprintTitle() + " " + sprints[i].getStartTimeInString(this);
            }
            builder.setTitle(getString(R.string.select_sprint)).setSingleChoiceItems(sprintsAsString,
                    sprints.length - 1, null);
            final AlertDialog alertDialog = builder.create();
            final Activity thisActivity = this;
            alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.ok),
                    new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // Start this activity again with the selected sprint as the passed Parcelable Sprint.
                            // This is done so that the activity can update itself to the selected sprint.
                            mCurrentSprint = sprints[alertDialog.getListView().getCheckedItemPosition()];
                            Intent intent = new Intent(thisActivity, MoodHistoryActivity.class);
                            intent.putExtra(Sprint.CURRENT_SPRINT, mCurrentSprint);
                            startActivity(intent);
                            alertDialog.dismiss();
                            thisActivity.finish();
                        }
                    });
            alertDialog.show();
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    /**
     * Represents the DatePicker fragment which is created when the user
     * clicks the search button. Implements on data set listener to get the
     * selection.
     * @author Toni Jrvinen
     *
     */
    public static class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {

        private Calendar mToday = Calendar.getInstance();

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Use the current date as the default date in the picker
            final Calendar c = Calendar.getInstance();
            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);
            int day = c.get(Calendar.DAY_OF_MONTH);
            DatePickerDialog pickerDialog = new DatePickerDialog(getActivity(), this, year, month, day);
            // Set the minimun and maximum dates based on the sprint and today.
            pickerDialog.getDatePicker().setMinDate(mSprintStartDateInMillis);
            if (mToday.getTimeInMillis() > mSprintEndDateInMillis) {
                pickerDialog.getDatePicker().setMaxDate(mSprintEndDateInMillis);
            } else {
                pickerDialog.getDatePicker().setMaxDate(mToday.getTimeInMillis());
            }
            pickerDialog.setTitle(getString(R.string.select_a_day));
            return pickerDialog;
        }

        /**
         * Jumps to the selected day when selected.
         */
        @Override
        public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
            final Calendar date = Calendar.getInstance();
            date.set(Calendar.YEAR, year);
            date.set(Calendar.MONTH, monthOfYear);
            date.set(Calendar.DAY_OF_MONTH, dayOfMonth);
            long dateInMillis = date.getTimeInMillis();
            // If the date is in the allowed limits, set the selected day in the activity
            if (dateInMillis >= mSprintStartDateInMillis
                    && dateInMillis < mToday.getTimeInMillis() + TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS)) {
                ((MoodHistoryActivity) getActivity()).setSelectedDay(dateInMillis);
            }
        }
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    public int getSelectedAttribute() {
        return mSelectedAttribute;
    }

    /**
     * Used to set the selected day and week of the sprint in the activity.
     * @param dayInMillis
     */
    public void setSelectedDay(long dayInMillis) {
        // Calculate the selected day position in the viewpager
        mSelectedDay = (int) TimeUnit.DAYS.convert(dayInMillis - mSprintStartDateInMillis, TimeUnit.MILLISECONDS);
        Calendar selectedDayAsCalendar = Calendar.getInstance();
        selectedDayAsCalendar.setFirstDayOfWeek(Calendar.MONDAY);
        selectedDayAsCalendar.setTimeInMillis(dayInMillis);
        // Calculate the selected day position as week in the viewpager
        mSelectedWeek = selectedDayAsCalendar.get(Calendar.WEEK_OF_YEAR) - mStartDate.get(Calendar.WEEK_OF_YEAR);
        // Take into account change of year.
        if (mSelectedWeek < 0) {
            mSelectedWeek = 52 - mStartDate.get(Calendar.WEEK_OF_YEAR)
                    + selectedDayAsCalendar.get(Calendar.WEEK_OF_YEAR);
        } else {
        }
        // Set the day or week depending on orientation.
        if (mRes.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            mViewPager.setCurrentItem(mSelectedDay);
        } else if (mRes.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mViewPager.setCurrentItem(mSelectedWeek);
        }
    }

    /**
     * Load the page titles to an array. This done so the Title Pager Indicator does not have to construct
     * them multiple times when changing pages.
     */
    public void setPageTitles() {
        mDayPageTitles = new String[mNumberOfTodayInSprint];
        mLocale = mRes.getConfiguration().locale;
        mDateFormat = new SimpleDateFormat("dd.MM.yyyy", mLocale);
        Calendar date = (Calendar) mStartDate.clone();
        for (int i = 0; i < mDayPageTitles.length; i++) {
            mDayPageTitles[i] = mDateFormat.format(date.getTime());
            date.add(Calendar.DATE, 1);
        }

        mWeekPageTitles = new String[mNumberOfWeeksInSprint];
        int size = mWeekPageTitles.length;
        for (int i = 0; i < size; i++) {
            long dateInMillis = mSprintStartDateInMillis + (TimeUnit.MILLISECONDS.convert(i, TimeUnit.DAYS) * 7);
            date.setTimeInMillis(dateInMillis);
            date.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            mWeekPageTitles[i] = mDateFormat.format(date.getTime());
            date.add(Calendar.DATE, 6);
            mWeekPageTitles[i] += " - " + mDateFormat.format(date.getTime());
        }
    }

    /**
     * Loads the views and funtionality for landscape orientation.
     */
    public void loadLandscapeView() {
        mPagerAdapterWeek = new FragmentWeekPagerAdapter(getFragmentManager(), this);
        mViewPager.setAdapter(mPagerAdapterWeek);
        titleIndicator.setViewPager(mViewPager);
        mViewPager.setCurrentItem(mSelectedWeek);
        mViewPager.setOffscreenPageLimit(1);

    }

    /**
     * Loads the views and functionality for portrait orientation.
     * In portrait view, days are represented in a horizontially scrollable carousel.
     */
    public void loadPortraitView() {
        mPagerAdapterDay = new FragmentDatePagerAdapter(getFragmentManager());
        mViewPager.setAdapter(mPagerAdapterDay);
        titleIndicator.setViewPager(mViewPager);
        mViewPager.setCurrentItem(mSelectedDay);
        mViewPager.setOffscreenPageLimit(5);

        new ShowcaseView.Builder(this, true).setTarget(new ViewTarget(titleIndicator))
                .setContentTitle("Tss on aikajana tmnhetkisest jaksostasi")
                .setContentText("Voit knt puhelimen vaakatasoon nhdksesi viikkonkymn.")
                .hideOnTouchOutside().setStyle(R.style.ShowcaseView).singleShot(TIMELINE_HELP_ID).build();
    }

    /**
     * Fragment adapter for the date view in the activity. Extends FragmentStatePagerAdapter so that it can destroy
     * fragments in memory more aggressively due to the large amount of fragments.
     * @author Toni Jrvinen
     *
     */
    private class FragmentDatePagerAdapter extends FragmentStatePagerAdapter {

        public FragmentDatePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getCount() {
            return mNumberOfTodayInSprint;
        }

        /**
         * Page title as the date
         */
        @Override
        public CharSequence getPageTitle(int position) {
            return mDayPageTitles[position];
        }

        /**
         * Instantiates the MoodHistoryDayFragment for the position.
         */
        @Override
        public MoodHistoryDayFragment getItem(int position) {
            MoodHistoryDayFragment fragment;
            // Get a DayInHistory for the date represented by this position. Calculated from the start date.
            DayInHistory date = mDayDataHandler.getDayInHistory(
                    mSprintStartDateInMillis + TimeUnit.MILLISECONDS.convert(position, TimeUnit.DAYS));
            fragment = new MoodHistoryDayFragment();
            Bundle b = new Bundle();
            // Send the DayInHistory for the fragment as parcelable.
            b.putParcelable(DayInHistory.DAY_IN_HISTORY, date);
            fragment.setArguments(b);

            fragment.setRetainInstance(true);
            return fragment;
        }

    }

    /**
     * Fragment adapter for the week view in the activity.
     * @author Toni Jrvinen
     *
     */
    private class FragmentWeekPagerAdapter extends FragmentPagerAdapter {

        private SparseArray<MoodHistoryWeekFragment> mWeekFragments = new SparseArray<MoodHistoryWeekFragment>();

        public FragmentWeekPagerAdapter(FragmentManager fm, Context context) {
            super(fm);
        }

        @Override
        public int getCount() {
            return mNumberOfWeeksInSprint;
        }

        /**
         * Page title with the week number.
         */
        @Override
        public CharSequence getPageTitle(int position) {
            return mWeekPageTitles[position];
        }

        /**
         * Instantiates the MoodHistoryWeekFragment for the position. Gives the current sprint start as milliseconds and
         * the fragments position as extras in a bundle.
         */
        @Override
        public Fragment getItem(int position) {
            Fragment fragment;
            fragment = new MoodHistoryWeekFragment();
            Bundle b = new Bundle();

            b.putLong(Sprint.CURRENT_SPRINT_STARTDATE, mSprintStartDateInMillis);
            b.putInt(FRAGMENT_POSITION, position);

            fragment.setArguments(b);

            fragment.setRetainInstance(true);
            return fragment;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            MoodHistoryWeekFragment weekFragment = (MoodHistoryWeekFragment) super.instantiateItem(container,
                    position);
            mWeekFragments.put(position, weekFragment);
            return weekFragment;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            super.destroyItem(container, position, object);
            mWeekFragments.remove(position);
        }

        public MoodHistoryWeekFragment getWeekFragment(int position) {
            return mWeekFragments.get(position);
        }

    }

}