Home | History | Annotate | Download | only in month
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.calendar.month;
     18 
     19 import com.android.calendar.R;
     20 import com.android.calendar.Utils;
     21 
     22 import android.app.Activity;
     23 import android.app.ListFragment;
     24 import android.content.Context;
     25 import android.content.res.Resources;
     26 import android.database.DataSetObserver;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.text.TextUtils;
     30 import android.text.format.DateUtils;
     31 import android.text.format.Time;
     32 import android.util.Log;
     33 import android.view.LayoutInflater;
     34 import android.view.View;
     35 import android.view.ViewConfiguration;
     36 import android.view.ViewGroup;
     37 import android.view.accessibility.AccessibilityEvent;
     38 import android.widget.AbsListView;
     39 import android.widget.AbsListView.OnScrollListener;
     40 import android.widget.ListView;
     41 import android.widget.TextView;
     42 
     43 import java.util.Calendar;
     44 import java.util.HashMap;
     45 import java.util.Locale;
     46 
     47 /**
     48  * <p>
     49  * This displays a titled list of weeks with selectable days. It can be
     50  * configured to display the week number, start the week on a given day, show a
     51  * reduced number of days, or display an arbitrary number of weeks at a time. By
     52  * overriding methods and changing variables this fragment can be customized to
     53  * easily display a month selection component in a given style.
     54  * </p>
     55  */
     56 public class SimpleDayPickerFragment extends ListFragment implements OnScrollListener {
     57 
     58     private static final String TAG = "MonthFragment";
     59     private static final String KEY_CURRENT_TIME = "current_time";
     60 
     61     // Affects when the month selection will change while scrolling up
     62     protected static final int SCROLL_HYST_WEEKS = 2;
     63     // How long the GoTo fling animation should last
     64     protected static final int GOTO_SCROLL_DURATION = 1000;
     65     // How long to wait after receiving an onScrollStateChanged notification
     66     // before acting on it
     67     protected static final int SCROLL_CHANGE_DELAY = 40;
     68     // The number of days to display in each week
     69     protected static final int DAYS_PER_WEEK = 7;
     70     // The size of the month name displayed above the week list
     71     protected static final int MINI_MONTH_NAME_TEXT_SIZE = 18;
     72     protected static int LIST_TOP_OFFSET = -1;  // so that the top line will be under the separator
     73     protected int WEEK_MIN_VISIBLE_HEIGHT = 12;
     74     protected int BOTTOM_BUFFER = 20;
     75     protected int mSaturdayColor = 0;
     76     protected int mSundayColor = 0;
     77     protected int mDayNameColor = 0;
     78 
     79     // You can override these numbers to get a different appearance
     80     protected int mNumWeeks = 6;
     81     protected boolean mShowWeekNumber = false;
     82     protected int mDaysPerWeek = 7;
     83 
     84     // These affect the scroll speed and feel
     85     protected float mFriction = .05f;
     86     protected float mVelocityScale = 0.333f;
     87 
     88     protected Context mContext;
     89     protected Handler mHandler;
     90 
     91     protected float mMinimumFlingVelocity;
     92 
     93     // highlighted time
     94     protected Time mSelectedDay = new Time();
     95     protected SimpleWeeksAdapter mAdapter;
     96     protected ListView mListView;
     97     protected ViewGroup mDayNamesHeader;
     98     protected String[] mDayLabels;
     99 
    100     // disposable variable used for time calculations
    101     protected Time mTempTime = new Time();
    102 
    103     private static float mScale = 0;
    104     // When the week starts; numbered like Time.<WEEKDAY> (e.g. SUNDAY=0).
    105     protected int mFirstDayOfWeek;
    106     // The first day of the focus month
    107     protected Time mFirstDayOfMonth = new Time();
    108     // The first day that is visible in the view
    109     protected Time mFirstVisibleDay = new Time();
    110     // The name of the month to display
    111     protected TextView mMonthName;
    112     // The last name announced by accessibility
    113     protected CharSequence mPrevMonthName;
    114     // which month should be displayed/highlighted [0-11]
    115     protected int mCurrentMonthDisplayed;
    116     // used for tracking during a scroll
    117     protected long mPreviousScrollPosition;
    118     // used for tracking which direction the view is scrolling
    119     protected boolean mIsScrollingUp = false;
    120     // used for tracking what state listview is in
    121     protected int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
    122     // used for tracking what state listview is in
    123     protected int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
    124 
    125     // This causes an update of the view at midnight
    126     protected Runnable mTodayUpdater = new Runnable() {
    127         @Override
    128         public void run() {
    129             Time midnight = new Time(mFirstVisibleDay.timezone);
    130             midnight.setToNow();
    131             long currentMillis = midnight.toMillis(true);
    132 
    133             midnight.hour = 0;
    134             midnight.minute = 0;
    135             midnight.second = 0;
    136             midnight.monthDay++;
    137             long millisToMidnight = midnight.normalize(true) - currentMillis;
    138             mHandler.postDelayed(this, millisToMidnight);
    139 
    140             if (mAdapter != null) {
    141                 mAdapter.notifyDataSetChanged();
    142             }
    143         }
    144     };
    145 
    146     // This allows us to update our position when a day is tapped
    147     protected DataSetObserver mObserver = new DataSetObserver() {
    148         @Override
    149         public void onChanged() {
    150             Time day = mAdapter.getSelectedDay();
    151             if (day.year != mSelectedDay.year || day.yearDay != mSelectedDay.yearDay) {
    152                 goTo(day.toMillis(true), true, true, false);
    153             }
    154         }
    155     };
    156 
    157     public SimpleDayPickerFragment(long initialTime) {
    158         goTo(initialTime, false, true, true);
    159         mHandler = new Handler();
    160     }
    161 
    162     @Override
    163     public void onAttach(Activity activity) {
    164         super.onAttach(activity);
    165         mContext = activity;
    166         String tz = Time.getCurrentTimezone();
    167         ViewConfiguration viewConfig = ViewConfiguration.get(activity);
    168         mMinimumFlingVelocity = viewConfig.getScaledMinimumFlingVelocity();
    169 
    170         // Ensure we're in the correct time zone
    171         mSelectedDay.switchTimezone(tz);
    172         mSelectedDay.normalize(true);
    173         mFirstDayOfMonth.timezone = tz;
    174         mFirstDayOfMonth.normalize(true);
    175         mFirstVisibleDay.timezone = tz;
    176         mFirstVisibleDay.normalize(true);
    177         mTempTime.timezone = tz;
    178 
    179         Resources res = activity.getResources();
    180         mSaturdayColor = res.getColor(R.color.month_saturday);
    181         mSundayColor = res.getColor(R.color.month_sunday);
    182         mDayNameColor = res.getColor(R.color.month_day_names_color);
    183 
    184         // Adjust sizes for screen density
    185         if (mScale == 0) {
    186             mScale = activity.getResources().getDisplayMetrics().density;
    187             if (mScale != 1) {
    188                 WEEK_MIN_VISIBLE_HEIGHT *= mScale;
    189                 BOTTOM_BUFFER *= mScale;
    190                 LIST_TOP_OFFSET *= mScale;
    191             }
    192         }
    193         setUpAdapter();
    194         setListAdapter(mAdapter);
    195     }
    196 
    197     /**
    198      * Creates a new adapter if necessary and sets up its parameters. Override
    199      * this method to provide a custom adapter.
    200      */
    201     protected void setUpAdapter() {
    202         HashMap<String, Integer> weekParams = new HashMap<String, Integer>();
    203         weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks);
    204         weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0);
    205         weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek);
    206         weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY,
    207                 Time.getJulianDay(mSelectedDay.toMillis(false), mSelectedDay.gmtoff));
    208         if (mAdapter == null) {
    209             mAdapter = new SimpleWeeksAdapter(getActivity(), weekParams);
    210             mAdapter.registerDataSetObserver(mObserver);
    211         } else {
    212             mAdapter.updateParams(weekParams);
    213         }
    214         // refresh the view with the new parameters
    215         mAdapter.notifyDataSetChanged();
    216     }
    217 
    218     @Override
    219     public void onCreate(Bundle savedInstanceState) {
    220         super.onCreate(savedInstanceState);
    221         if (savedInstanceState != null && savedInstanceState.containsKey(KEY_CURRENT_TIME)) {
    222             goTo(savedInstanceState.getLong(KEY_CURRENT_TIME), false, true, true);
    223         }
    224     }
    225 
    226     @Override
    227     public void onActivityCreated(Bundle savedInstanceState) {
    228         super.onActivityCreated(savedInstanceState);
    229 
    230         setUpListView();
    231         setUpHeader();
    232 
    233         mMonthName = (TextView) getView().findViewById(R.id.month_name);
    234         SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0);
    235         if (child == null) {
    236             return;
    237         }
    238         int julianDay = child.getFirstJulianDay();
    239         mFirstVisibleDay.setJulianDay(julianDay);
    240         // set the title to the month of the second week
    241         mTempTime.setJulianDay(julianDay + DAYS_PER_WEEK);
    242         setMonthDisplayed(mTempTime, true);
    243     }
    244 
    245     /**
    246      * Sets up the strings to be used by the header. Override this method to use
    247      * different strings or modify the view params.
    248      */
    249     protected void setUpHeader() {
    250         mDayLabels = new String[7];
    251         for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) {
    252             mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i,
    253                     DateUtils.LENGTH_SHORTEST).toUpperCase();
    254         }
    255     }
    256 
    257     /**
    258      * Sets all the required fields for the list view. Override this method to
    259      * set a different list view behavior.
    260      */
    261     protected void setUpListView() {
    262         // Configure the listview
    263         mListView = getListView();
    264         // Transparent background on scroll
    265         mListView.setCacheColorHint(0);
    266         // No dividers
    267         mListView.setDivider(null);
    268         // Items are clickable
    269         mListView.setItemsCanFocus(true);
    270         // The thumb gets in the way, so disable it
    271         mListView.setFastScrollEnabled(false);
    272         mListView.setVerticalScrollBarEnabled(false);
    273         mListView.setOnScrollListener(this);
    274         mListView.setFadingEdgeLength(0);
    275         // Make the scrolling behavior nicer
    276         mListView.setFriction(mFriction);
    277         mListView.setVelocityScale(mVelocityScale);
    278     }
    279 
    280     @Override
    281     public void onResume() {
    282         super.onResume();
    283         setUpAdapter();
    284         doResumeUpdates();
    285     }
    286 
    287     @Override
    288     public void onPause() {
    289         super.onPause();
    290         mHandler.removeCallbacks(mTodayUpdater);
    291     }
    292 
    293     @Override
    294     public void onSaveInstanceState(Bundle outState) {
    295         outState.putLong(KEY_CURRENT_TIME, mSelectedDay.toMillis(true));
    296     }
    297 
    298     /**
    299      * Updates the user preference fields. Override this to use a different
    300      * preference space.
    301      */
    302     protected void doResumeUpdates() {
    303         // Get default week start based on locale, subtracting one for use with android Time.
    304         Calendar cal = Calendar.getInstance(Locale.getDefault());
    305         mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1;
    306 
    307         mShowWeekNumber = false;
    308 
    309         updateHeader();
    310         goTo(mSelectedDay.toMillis(true), false, false, false);
    311         mAdapter.setSelectedDay(mSelectedDay);
    312         mTodayUpdater.run();
    313     }
    314 
    315     /**
    316      * Fixes the day names header to provide correct spacing and updates the
    317      * label text. Override this to set up a custom header.
    318      */
    319     protected void updateHeader() {
    320         TextView label = (TextView) mDayNamesHeader.findViewById(R.id.wk_label);
    321         if (mShowWeekNumber) {
    322             label.setVisibility(View.VISIBLE);
    323         } else {
    324             label.setVisibility(View.GONE);
    325         }
    326         int offset = mFirstDayOfWeek - 1;
    327         for (int i = 1; i < 8; i++) {
    328             label = (TextView) mDayNamesHeader.getChildAt(i);
    329             if (i < mDaysPerWeek + 1) {
    330                 int position = (offset + i) % 7;
    331                 label.setText(mDayLabels[position]);
    332                 label.setVisibility(View.VISIBLE);
    333                 if (position == Time.SATURDAY) {
    334                     label.setTextColor(mSaturdayColor);
    335                 } else if (position == Time.SUNDAY) {
    336                     label.setTextColor(mSundayColor);
    337                 } else {
    338                     label.setTextColor(mDayNameColor);
    339                 }
    340             } else {
    341                 label.setVisibility(View.GONE);
    342             }
    343         }
    344         mDayNamesHeader.invalidate();
    345     }
    346 
    347     @Override
    348     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    349         View v = inflater.inflate(R.layout.month_by_week,
    350                 container, false);
    351         mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names);
    352         return v;
    353     }
    354 
    355     /**
    356      * Returns the UTC millis since epoch representation of the currently
    357      * selected time.
    358      *
    359      * @return
    360      */
    361     public long getSelectedTime() {
    362         return mSelectedDay.toMillis(true);
    363     }
    364 
    365     /**
    366      * This moves to the specified time in the view. If the time is not already
    367      * in range it will move the list so that the first of the month containing
    368      * the time is at the top of the view. If the new time is already in view
    369      * the list will not be scrolled unless forceScroll is true. This time may
    370      * optionally be highlighted as selected as well.
    371      *
    372      * @param time The time to move to
    373      * @param animate Whether to scroll to the given time or just redraw at the
    374      *            new location
    375      * @param setSelected Whether to set the given time as selected
    376      * @param forceScroll Whether to recenter even if the time is already
    377      *            visible
    378      * @return Whether or not the view animated to the new location
    379      */
    380     public boolean goTo(long time, boolean animate, boolean setSelected, boolean forceScroll) {
    381         if (time == -1) {
    382             Log.e(TAG, "time is invalid");
    383             return false;
    384         }
    385 
    386         // Set the selected day
    387         if (setSelected) {
    388             mSelectedDay.set(time);
    389             mSelectedDay.normalize(true);
    390         }
    391 
    392         // If this view isn't returned yet we won't be able to load the lists
    393         // current position, so return after setting the selected day.
    394         if (!isResumed()) {
    395             if (Log.isLoggable(TAG, Log.DEBUG)) {
    396                 Log.d(TAG, "We're not visible yet");
    397             }
    398             return false;
    399         }
    400 
    401         mTempTime.set(time);
    402         long millis = mTempTime.normalize(true);
    403         // Get the week we're going to
    404         // TODO push Util function into Calendar public api.
    405         int position = Utils.getWeeksSinceEpochFromJulianDay(
    406                 Time.getJulianDay(millis, mTempTime.gmtoff), mFirstDayOfWeek);
    407 
    408         View child;
    409         int i = 0;
    410         int top = 0;
    411         // Find a child that's completely in the view
    412         do {
    413             child = mListView.getChildAt(i++);
    414             if (child == null) {
    415                 break;
    416             }
    417             top = child.getTop();
    418             if (Log.isLoggable(TAG, Log.DEBUG)) {
    419                 Log.d(TAG, "child at " + (i-1) + " has top " + top);
    420             }
    421         } while (top < 0);
    422 
    423         // Compute the first and last position visible
    424         int firstPosition;
    425         if (child != null) {
    426             firstPosition = mListView.getPositionForView(child);
    427         } else {
    428             firstPosition = 0;
    429         }
    430         int lastPosition = firstPosition + mNumWeeks - 1;
    431         if (top > BOTTOM_BUFFER) {
    432             lastPosition--;
    433         }
    434 
    435         if (setSelected) {
    436             mAdapter.setSelectedDay(mSelectedDay);
    437         }
    438 
    439         if (Log.isLoggable(TAG, Log.DEBUG)) {
    440             Log.d(TAG, "GoTo position " + position);
    441         }
    442         // Check if the selected day is now outside of our visible range
    443         // and if so scroll to the month that contains it
    444         if (position < firstPosition || position > lastPosition || forceScroll) {
    445             mFirstDayOfMonth.set(mTempTime);
    446             mFirstDayOfMonth.monthDay = 1;
    447             millis = mFirstDayOfMonth.normalize(true);
    448             setMonthDisplayed(mFirstDayOfMonth, true);
    449             position = Utils.getWeeksSinceEpochFromJulianDay(
    450                     Time.getJulianDay(millis, mFirstDayOfMonth.gmtoff), mFirstDayOfWeek);
    451 
    452             mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
    453             if (animate) {
    454                 mListView.smoothScrollToPositionFromTop(
    455                         position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION);
    456                 return true;
    457             } else {
    458                 mListView.setSelectionFromTop(position, LIST_TOP_OFFSET);
    459                 // Perform any after scroll operations that are needed
    460                 onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE);
    461             }
    462         } else if (setSelected) {
    463             // Otherwise just set the selection
    464             setMonthDisplayed(mSelectedDay, true);
    465         }
    466         return false;
    467     }
    468 
    469      /**
    470      * Updates the title and selected month if the view has moved to a new
    471      * month.
    472      */
    473     @Override
    474     public void onScroll(
    475             AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    476         SimpleWeekView child = (SimpleWeekView)view.getChildAt(0);
    477         if (child == null) {
    478             return;
    479         }
    480 
    481         // Figure out where we are
    482         long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
    483         mFirstVisibleDay.setJulianDay(child.getFirstJulianDay());
    484 
    485         // If we have moved since our last call update the direction
    486         if (currScroll < mPreviousScrollPosition) {
    487             mIsScrollingUp = true;
    488         } else if (currScroll > mPreviousScrollPosition) {
    489             mIsScrollingUp = false;
    490         } else {
    491             return;
    492         }
    493 
    494         mPreviousScrollPosition = currScroll;
    495         mPreviousScrollState = mCurrentScrollState;
    496 
    497         updateMonthHighlight(mListView);
    498     }
    499 
    500     /**
    501      * Figures out if the month being shown has changed and updates the
    502      * highlight if needed
    503      *
    504      * @param view The ListView containing the weeks
    505      */
    506     private void updateMonthHighlight(AbsListView view) {
    507         SimpleWeekView child = (SimpleWeekView) view.getChildAt(0);
    508         if (child == null) {
    509             return;
    510         }
    511 
    512         // Figure out where we are
    513         int offset = child.getBottom() < WEEK_MIN_VISIBLE_HEIGHT ? 1 : 0;
    514         // Use some hysteresis for checking which month to highlight. This
    515         // causes the month to transition when two full weeks of a month are
    516         // visible.
    517         child = (SimpleWeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
    518 
    519         if (child == null) {
    520             return;
    521         }
    522 
    523         // Find out which month we're moving into
    524         int month;
    525         if (mIsScrollingUp) {
    526             month = child.getFirstMonth();
    527         } else {
    528             month = child.getLastMonth();
    529         }
    530 
    531         // And how it relates to our current highlighted month
    532         int monthDiff;
    533         if (mCurrentMonthDisplayed == 11 && month == 0) {
    534             monthDiff = 1;
    535         } else if (mCurrentMonthDisplayed == 0 && month == 11) {
    536             monthDiff = -1;
    537         } else {
    538             monthDiff = month - mCurrentMonthDisplayed;
    539         }
    540 
    541         // Only switch months if we're scrolling away from the currently
    542         // selected month
    543         if (monthDiff != 0) {
    544             int julianDay = child.getFirstJulianDay();
    545             if (mIsScrollingUp) {
    546                 // Takes the start of the week
    547             } else {
    548                 // Takes the start of the following week
    549                 julianDay += DAYS_PER_WEEK;
    550             }
    551             mTempTime.setJulianDay(julianDay);
    552             setMonthDisplayed(mTempTime, false);
    553         }
    554     }
    555 
    556     /**
    557      * Sets the month displayed at the top of this view based on time. Override
    558      * to add custom events when the title is changed.
    559      *
    560      * @param time A day in the new focus month.
    561      * @param updateHighlight TODO(epastern):
    562      */
    563     protected void setMonthDisplayed(Time time, boolean updateHighlight) {
    564         CharSequence oldMonth = mMonthName.getText();
    565         mMonthName.setText(Utils.formatMonthYear(mContext, time));
    566         mMonthName.invalidate();
    567         if (!TextUtils.equals(oldMonth, mMonthName.getText())) {
    568             mMonthName.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
    569         }
    570         mCurrentMonthDisplayed = time.month;
    571         if (updateHighlight) {
    572             mAdapter.updateFocusMonth(mCurrentMonthDisplayed);
    573         }
    574     }
    575 
    576     @Override
    577     public void onScrollStateChanged(AbsListView view, int scrollState) {
    578         // use a post to prevent re-entering onScrollStateChanged before it
    579         // exits
    580         mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
    581     }
    582 
    583     protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
    584 
    585     protected class ScrollStateRunnable implements Runnable {
    586         private int mNewState;
    587 
    588         /**
    589          * Sets up the runnable with a short delay in case the scroll state
    590          * immediately changes again.
    591          *
    592          * @param view The list view that changed state
    593          * @param scrollState The new state it changed to
    594          */
    595         public void doScrollStateChange(AbsListView view, int scrollState) {
    596             mHandler.removeCallbacks(this);
    597             mNewState = scrollState;
    598             mHandler.postDelayed(this, SCROLL_CHANGE_DELAY);
    599         }
    600 
    601         public void run() {
    602             mCurrentScrollState = mNewState;
    603             if (Log.isLoggable(TAG, Log.DEBUG)) {
    604                 Log.d(TAG,
    605                         "new scroll state: " + mNewState + " old state: " + mPreviousScrollState);
    606             }
    607             // Fix the position after a scroll or a fling ends
    608             if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
    609                     && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
    610                 mPreviousScrollState = mNewState;
    611                 // Uncomment the below to add snap to week back
    612 //                int i = 0;
    613 //                View child = mView.getChildAt(i);
    614 //                while (child != null && child.getBottom() <= 0) {
    615 //                    child = mView.getChildAt(++i);
    616 //                }
    617 //                if (child == null) {
    618 //                    // The view is no longer visible, just return
    619 //                    return;
    620 //                }
    621 //                int dist = child.getTop();
    622 //                if (dist < LIST_TOP_OFFSET) {
    623 //                    if (Log.isLoggable(TAG, Log.DEBUG)) {
    624 //                        Log.d(TAG, "scrolling by " + dist + " up? " + mIsScrollingUp);
    625 //                    }
    626 //                    int firstPosition = mView.getFirstVisiblePosition();
    627 //                    int lastPosition = mView.getLastVisiblePosition();
    628 //                    boolean scroll = firstPosition != 0 && lastPosition != mView.getCount() - 1;
    629 //                    if (mIsScrollingUp && scroll) {
    630 //                        mView.smoothScrollBy(dist, 500);
    631 //                    } else if (!mIsScrollingUp && scroll) {
    632 //                        mView.smoothScrollBy(child.getHeight() + dist, 500);
    633 //                    }
    634 //                }
    635                 mAdapter.updateFocusMonth(mCurrentMonthDisplayed);
    636             } else {
    637                 mPreviousScrollState = mNewState;
    638             }
    639         }
    640     }
    641 }
    642