107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko/*
207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * Copyright (C) 2015 The Android Open Source Project
307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko *
407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * you may not use this file except in compliance with the License.
607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * You may obtain a copy of the License at
707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko *
807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko *
1007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * Unless required by applicable law or agreed to in writing, software
1107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
1207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * See the License for the specific language governing permissions and
1407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * limitations under the License.
1507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko */
1607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
1707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkopackage com.android.tv.menu;
1807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
1907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.animation.Animator;
2007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.animation.AnimatorListenerAdapter;
2107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.animation.AnimatorSet;
2207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.animation.ObjectAnimator;
2307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.animation.TimeInterpolator;
2407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.content.Context;
2507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.content.res.Resources;
2607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.graphics.Rect;
2707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.support.annotation.UiThread;
2807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.support.v4.view.animation.FastOutLinearInInterpolator;
2907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.support.v4.view.animation.FastOutSlowInInterpolator;
3007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.support.v4.view.animation.LinearOutSlowInInterpolator;
3107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.util.Log;
3207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.util.Property;
3307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.view.View;
3407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.view.ViewGroup.MarginLayoutParams;
3507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.widget.TextView;
3607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
3707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport com.android.tv.R;
3848dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.common.SoftPreconditions;
3907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport com.android.tv.util.Utils;
4007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
4107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport java.util.ArrayList;
4207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport java.util.Collections;
4307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport java.util.HashMap;
4407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport java.util.List;
4507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport java.util.Map;
4607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport java.util.Map.Entry;
4707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport java.util.concurrent.TimeUnit;
4807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
4907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko/**
5007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko * A view that represents TV main menu.
5107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko */
5207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko@UiThread
5307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkopublic class MenuLayoutManager {
5407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    static final String TAG = "MenuLayoutManager";
5507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    static final boolean DEBUG = false;
5607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
5707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    // The visible duration of the title before it is hidden.
5807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private static final long TITLE_SHOW_DURATION_BEFORE_HIDDEN_MS = TimeUnit.SECONDS.toMillis(2);
5907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
6007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final MenuView mMenuView;
6107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final List<MenuRow> mMenuRows = new ArrayList<>();
6207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final List<MenuRowView> mMenuRowViews = new ArrayList<>();
6307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final List<Integer> mRemovingRowViews = new ArrayList<>();
6407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private int mSelectedPosition = -1;
6507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
6607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final int mRowAlignFromBottom;
6707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final int mRowContentsPaddingTop;
6807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final int mRowContentsPaddingBottomMax;
6907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final int mRowTitleTextDescenderHeight;
7007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final int mMenuMarginBottomMin;
7107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final int mRowTitleHeight;
7207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final int mRowScrollUpAnimationOffset;
7307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
7407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final long mRowAnimationDuration;
7507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final long mOldContentsFadeOutDuration;
7607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final long mCurrentContentsFadeInDuration;
7707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final TimeInterpolator mFastOutSlowIn = new FastOutSlowInInterpolator();
7807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final TimeInterpolator mFastOutLinearIn = new FastOutLinearInInterpolator();
7907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final TimeInterpolator mLinearOutSlowIn = new LinearOutSlowInInterpolator();
8007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private AnimatorSet mAnimatorSet;
8107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private ObjectAnimator mTitleFadeOutAnimator;
8207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final List<ViewPropertyValueHolder> mPropertyValuesAfterAnimation = new ArrayList<>();
8307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
8407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private TextView mTempTitleViewForOld;
8507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private TextView mTempTitleViewForCurrent;
8607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
8707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public MenuLayoutManager(Context context, MenuView menuView) {
8807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mMenuView = menuView;
8907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Load dimensions
9007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        Resources res = context.getResources();
9107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRowAlignFromBottom = res.getDimensionPixelOffset(R.dimen.menu_row_align_from_bottom);
9207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRowContentsPaddingTop = res.getDimensionPixelOffset(R.dimen.menu_row_contents_padding_top);
9307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRowContentsPaddingBottomMax = res.getDimensionPixelOffset(
9407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                R.dimen.menu_row_contents_padding_bottom_max);
9507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRowTitleTextDescenderHeight = res.getDimensionPixelOffset(
9607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                R.dimen.menu_row_title_text_descender_height);
9707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mMenuMarginBottomMin = res.getDimensionPixelOffset(R.dimen.menu_margin_bottom_min);
9807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRowTitleHeight = res.getDimensionPixelSize(R.dimen.menu_row_title_height);
9907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRowScrollUpAnimationOffset =
10007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                res.getDimensionPixelOffset(R.dimen.menu_row_scroll_up_anim_offset);
10107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRowAnimationDuration = res.getInteger(R.integer.menu_row_selection_anim_duration);
10207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mOldContentsFadeOutDuration = res.getInteger(
10307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                R.integer.menu_previous_contents_fade_out_duration);
10407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mCurrentContentsFadeInDuration = res.getInteger(
10507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                R.integer.menu_current_contents_fade_in_duration);
10607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
10707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
10807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
10907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Sets the menu rows and views.
11007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
11107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public void setMenuRowsAndViews(List<MenuRow> menuRows, List<MenuRowView> menuRowViews) {
11207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mMenuRows.clear();
11307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mMenuRows.addAll(menuRows);
11407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mMenuRowViews.clear();
11507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mMenuRowViews.addAll(menuRowViews);
11607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
11707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
11807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
11907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Layouts main menu view.
12007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     *
12107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * <p>Do not call this method directly. It's supposed to be called only by View.onLayout().
12207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
12307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public void layout(int left, int top, int right, int bottom) {
12407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mAnimatorSet != null) {
12507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Layout will be done after the animation ends.
12607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
12707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
12807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
12907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int count = mMenuRowViews.size();
13007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        MenuRowView currentView = mMenuRowViews.get(mSelectedPosition);
13107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (currentView.getVisibility() == View.GONE) {
13207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // If the selected row is not visible, select the first visible row.
13307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int firstVisiblePosition = findNextVisiblePosition(-1);
13407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (firstVisiblePosition != -1) {
13507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mSelectedPosition = firstVisiblePosition;
13607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else {
13707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // No rows are visible.
13807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                return;
13907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
14007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
14107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        List<Rect> layouts = getViewLayouts(left, top, right, bottom);
14207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (int i = 0; i < count; ++i) {
14307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            Rect rect = layouts.get(i);
14407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (rect != null) {
14507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                currentView = mMenuRowViews.get(i);
14607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                currentView.layout(rect.left, rect.top, rect.right, rect.bottom);
14707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                if (DEBUG) dumpChildren("layout()");
14807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
14907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
15007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
15107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // If the contents view is INVISIBLE initially, it should be changed to GONE after layout.
15207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // See MenuRowView.onFinishInflate() for more information
15307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // TODO: Find a better way to resolve this issue..
15407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (MenuRowView view : mMenuRowViews) {
15507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (view.getVisibility() == View.VISIBLE
15607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    && view.getContentsView().getVisibility() == View.INVISIBLE) {
15707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                view.onDeselected();
15807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
15907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
16007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
16107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
16207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private int findNextVisiblePosition(int start) {
16307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int count = mMenuRowViews.size();
16407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (int i = start + 1; i < count; ++i) {
16507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (mMenuRowViews.get(i).getVisibility() != View.GONE) {
16607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                return i;
16707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
16807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
16907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return -1;
17007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
17107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
17207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private void dumpChildren(String prefix) {
17307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int position = 0;
17407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (MenuRowView view : mMenuRowViews) {
17507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            View title = view.getChildAt(0);
17607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            View contents = view.getChildAt(1);
17707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            Log.d(TAG, prefix + " position=" + position++
17807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + " rowView={visiblility=" + view.getVisibility()
17907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", alpha=" + view.getAlpha()
18007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", translationY=" + view.getTranslationY()
18107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", left=" + view.getLeft() + ", top=" + view.getTop()
18207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", right=" + view.getRight() + ", bottom=" + view.getBottom()
18307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + "}, title={visiblility=" + title.getVisibility()
18407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", alpha=" + title.getAlpha()
18507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", translationY=" + title.getTranslationY()
18607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", left=" + title.getLeft() + ", top=" + title.getTop()
18707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", right=" + title.getRight() + ", bottom=" + title.getBottom()
18807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + "}, contents={visiblility=" + contents.getVisibility()
18907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", alpha=" + contents.getAlpha()
19007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", translationY=" + contents.getTranslationY()
19107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", left=" + contents.getLeft() + ", top=" + contents.getTop()
19207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + ", right=" + contents.getRight() + ", bottom=" + contents.getBottom()+ "}");
19307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
19407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
19507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
19607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
19707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Checks if the view will take up space for the layout not.
19807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     *
19907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param position The index of the menu row view in the list. This is not the index of the view
20007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * in the screen.
20107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param view The menu row view.
20207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param rowsToAdd The menu row views to be added in the next layout process.
20307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param rowsToRemove The menu row views to be removed in the next layout process.
20407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @return {@code true} if the view will take up space for the layout, otherwise {@code false}.
20507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
20607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private boolean isVisibleInLayout(int position, MenuRowView view, List<Integer> rowsToAdd,
20707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            List<Integer> rowsToRemove) {
20807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Checks if the view will be visible or not.
20907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return (view.getVisibility() != View.GONE && !rowsToRemove.contains(position))
21007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                || rowsToAdd.contains(position);
21107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
21207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
21307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
21407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Calculates and returns a list of the layout bounds of the menu row views for the layout.
21507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     *
21607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param left The left coordinate of the menu view.
21707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param top The top coordinate of the menu view.
21807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param right The right coordinate of the menu view.
21907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param bottom The bottom coordinate of the menu view.
22007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
22107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private List<Rect> getViewLayouts(int left, int top, int right, int bottom) {
22207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return getViewLayouts(left, top, right, bottom, Collections.<Integer>emptyList(),
22307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                Collections.<Integer>emptyList());
22407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
22507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
22607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
22707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Calculates and returns a list of the layout bounds of the menu row views for the layout. The
22807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * order of the bounds is the same as that of the menu row views. e.g. the second rectangle in
22907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * the list is for the second menu row view in the view list (not the second view in the
23007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * screen).
23107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     *
23207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * <p>It predicts the layout bounds for the next layout process. Some views will be added or
23307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * removed in the layout, so they need to be considered here.
23407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     *
23507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param left The left coordinate of the menu view.
23607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param top The top coordinate of the menu view.
23707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param right The right coordinate of the menu view.
23807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param bottom The bottom coordinate of the menu view.
23907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param rowsToAdd The menu row views to be added in the next layout process.
24007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @param rowsToRemove The menu row views to be removed in the next layout process.
24107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * @return the layout bounds of the menu row views.
24207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
24307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private List<Rect> getViewLayouts(int left, int top, int right, int bottom,
24407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            List<Integer> rowsToAdd, List<Integer> rowsToRemove) {
24507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // The coordinates should be relative to the parent.
24607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int relativeLeft = 0;
24707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int relateiveRight = right - left;
24807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int relativeBottom = bottom - top;
24907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
25007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        List<Rect> layouts = new ArrayList<>();
25107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int count = mMenuRowViews.size();
25207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        MenuRowView selectedView = mMenuRowViews.get(mSelectedPosition);
25307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int rowTitleHeight = selectedView.getTitleView().getMeasuredHeight();
25407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int rowContentsHeight = selectedView.getPreferredContentsHeight();
25507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Calculate for the selected row first.
25607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // The distance between the bottom of the screen and the vertical center of the contents
25707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // should be kept fixed. For more information, please see the redlines.
25807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int childTop = relativeBottom - mRowAlignFromBottom - rowContentsHeight / 2
25907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                - mRowContentsPaddingTop - rowTitleHeight;
26007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int childBottom = relativeBottom;
26107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int position = mSelectedPosition + 1;
26207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (; position < count; ++position) {
26307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Find and layout the next row to calculate the bottom line of the selected row.
26407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView nextView = mMenuRowViews.get(position);
26507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (isVisibleInLayout(position, nextView, rowsToAdd, rowsToRemove)) {
26607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                int nextTitleTopMax = relativeBottom - mMenuMarginBottomMin - rowTitleHeight
26707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        + mRowTitleTextDescenderHeight;
26807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                int childBottomMax = relativeBottom - mRowAlignFromBottom + rowContentsHeight / 2
26907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        + mRowContentsPaddingBottomMax - rowTitleHeight;
27007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                childBottom = Math.min(nextTitleTopMax, childBottomMax);
27107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                layouts.add(new Rect(relativeLeft, childBottom, relateiveRight, relativeBottom));
27207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                break;
27307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else {
27407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // null means that the row is GONE.
27507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                layouts.add(null);
27607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
27707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
27807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        layouts.add(0, new Rect(relativeLeft, childTop, relateiveRight, childBottom));
27907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Layout the previous rows.
28007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (int i = mSelectedPosition - 1; i >= 0; --i) {
28107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView view = mMenuRowViews.get(i);
28207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (isVisibleInLayout(i, view, rowsToAdd, rowsToRemove)) {
28307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                childTop -= mRowTitleHeight;
28407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                childBottom = childTop + rowTitleHeight;
28507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                layouts.add(0, new Rect(relativeLeft, childTop, relateiveRight, childBottom));
28607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else {
28707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                layouts.add(0, null);
28807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
28907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
29007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Move all the next rows to the below of the screen.
29107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        childTop = relativeBottom;
29207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (++position; position < count; ++position) {
29307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView view = mMenuRowViews.get(position);
29407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (isVisibleInLayout(position, view, rowsToAdd, rowsToRemove)) {
29507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                childBottom = childTop + rowTitleHeight;
29607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                layouts.add(new Rect(relativeLeft, childTop, relateiveRight, childBottom));
29707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                childTop += mRowTitleHeight;
29807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else {
29907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                layouts.add(null);
30007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
30107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
30207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return layouts;
30307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
30407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
30507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
30607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Move the current selection to the given {@code position}.
30707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
30807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public void setSelectedPosition(int position) {
30907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (DEBUG) {
31007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            Log.d(TAG, "setSelectedPosition(position=" + position + ") {previousPosition="
31107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + mSelectedPosition + "}");
31207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
31307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mSelectedPosition == position) {
31407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
31507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
3167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        boolean indexValid = Utils.isIndexValid(mMenuRowViews, position);
3177d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        SoftPreconditions.checkArgument(indexValid, TAG, "position " + position);
3187d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (!indexValid) {
31907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
32007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
32148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        MenuRow row = mMenuRows.get(position);
32248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (!row.isVisible()) {
32348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            Log.e(TAG, "Selecting invisible row: " + position);
32448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return;
32548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
3267d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (Utils.isIndexValid(mMenuRowViews, mSelectedPosition)) {
32707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mMenuRowViews.get(mSelectedPosition).onDeselected();
32807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
32907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mSelectedPosition = position;
3307d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (Utils.isIndexValid(mMenuRowViews, mSelectedPosition)) {
33107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mMenuRowViews.get(mSelectedPosition).onSelected(false);
33207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
33307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mMenuView.getVisibility() == View.VISIBLE) {
33407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Request focus after the new contents view shows up.
33507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mMenuView.requestFocus();
33607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Adjust the position of the selected row.
33707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mMenuView.requestLayout();
33807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
33907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
34007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
34107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
34207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Move the current selection to the given {@code position} with animation.
34307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * The animation specification is included in http://b/21069476
34407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
34507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public void setSelectedPositionSmooth(final int position) {
34607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (DEBUG) {
34707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            Log.d(TAG, "setSelectedPositionSmooth(position=" + position + ") {previousPosition="
34807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    + mSelectedPosition + "}");
34907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
35007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mMenuView.getVisibility() != View.VISIBLE) {
35107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            setSelectedPosition(position);
35207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
35307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
35407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mSelectedPosition == position) {
35507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
35607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
3577d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        boolean oldIndexValid = Utils.isIndexValid(mMenuRowViews, mSelectedPosition);
3587d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        SoftPreconditions
3597d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                .checkState(oldIndexValid, TAG, "No previous selection: " + mSelectedPosition);
3607d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (!oldIndexValid) {
36107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
36207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
3637d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        boolean newIndexValid = Utils.isIndexValid(mMenuRowViews, position);
3647d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        SoftPreconditions.checkArgument(newIndexValid, TAG, "position " + position);
3657d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (!newIndexValid) {
36607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
36707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
36848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        MenuRow row = mMenuRows.get(position);
36948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (!row.isVisible()) {
37048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            Log.e(TAG, "Moving to the invisible row: " + position);
37148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return;
37248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
37307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mAnimatorSet != null) {
37407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Do not cancel the animation here. The property values should be set to the end values
37507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // when the animation finishes.
37607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mAnimatorSet.end();
37707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
37807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mTitleFadeOutAnimator != null) {
37907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Cancel the animation instead of ending it in order that the title animation starts
38007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // again from the intermediate state.
38107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mTitleFadeOutAnimator.cancel();
38207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
38307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        final int oldPosition = mSelectedPosition;
38407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mSelectedPosition = position;
38507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (DEBUG) dumpChildren("startRowAnimation()");
38607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
38707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        MenuRowView currentView = mMenuRowViews.get(position);
38807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Show the children of the next row.
38907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        currentView.getTitleView().setVisibility(View.VISIBLE);
39007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        currentView.getContentsView().setVisibility(View.VISIBLE);
39107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Request focus after the new contents view shows up.
39207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mMenuView.requestFocus();
39307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mTempTitleViewForOld == null) {
39407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Initialize here because we don't know when the views are inflated.
39507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mTempTitleViewForOld =
39607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    (TextView) mMenuView.findViewById(R.id.temp_title_for_old);
39707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mTempTitleViewForCurrent =
39807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    (TextView) mMenuView.findViewById(R.id.temp_title_for_current);
39907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
40007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
40107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Animations.
40207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mPropertyValuesAfterAnimation.clear();
40307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        List<Animator> animators = new ArrayList<>();
40407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        boolean scrollDown = position > oldPosition;
40507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        List<Rect> layouts = getViewLayouts(mMenuView.getLeft(), mMenuView.getTop(),
40607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mMenuView.getRight(), mMenuView.getBottom());
40707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
40807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Old row.
40907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        MenuRow oldRow = mMenuRows.get(oldPosition);
41007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        MenuRowView oldView = mMenuRowViews.get(oldPosition);
41107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        View oldContentsView = oldView.getContentsView();
41207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Old contents view.
41307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animators.add(createAlphaAnimator(oldContentsView, 1.0f, 0.0f, 1.0f, mLinearOutSlowIn)
41407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                .setDuration(mOldContentsFadeOutDuration));
41507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        final TextView oldTitleView = oldView.getTitleView();
41607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        setTempTitleView(mTempTitleViewForOld, oldTitleView);
41707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        Rect oldLayoutRect = layouts.get(oldPosition);
41807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (scrollDown) {
41907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Old title view.
42007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (oldRow.hideTitleWhenSelected() && oldTitleView.getVisibility() != View.VISIBLE) {
42107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // This case is not included in the animation specification.
42207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mTempTitleViewForOld.setScaleX(1.0f);
42307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mTempTitleViewForOld.setScaleY(1.0f);
42407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createAlphaAnimator(mTempTitleViewForOld, 0.0f,
42507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        oldView.getTitleViewAlphaDeselected(), mFastOutLinearIn));
42607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                int offset = oldLayoutRect.top - mTempTitleViewForOld.getTop();
42707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createTranslationYAnimator(mTempTitleViewForOld,
42807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        offset + mRowScrollUpAnimationOffset, offset));
42907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else {
43007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createScaleXAnimator(mTempTitleViewForOld,
43107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        oldView.getTitleViewScaleSelected(), 1.0f));
43207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createScaleYAnimator(mTempTitleViewForOld,
43307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        oldView.getTitleViewScaleSelected(), 1.0f));
43407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createAlphaAnimator(mTempTitleViewForOld, oldTitleView.getAlpha(),
43507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        oldView.getTitleViewAlphaDeselected(), mLinearOutSlowIn));
43607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createTranslationYAnimator(mTempTitleViewForOld, 0,
43707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        oldLayoutRect.top - mTempTitleViewForOld.getTop()));
43807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
43907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            oldTitleView.setAlpha(oldView.getTitleViewAlphaDeselected());
44007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            oldTitleView.setVisibility(View.INVISIBLE);
44107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        } else {
44207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            Rect currentLayoutRect = new Rect(layouts.get(position));
44307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Old title view.
44407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // The move distance in the specification is 32dp(mRowScrollUpAnimationOffset).
44507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // But if the height of the upper row is small, the upper row will move down a lot. In
44607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // this case, this row needs to move more than the specification to avoid the overlap of
44707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // the two titles.
44807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // The maximum is to the top of the start position of mTempTitleViewForOld.
44907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int distanceCurrentTitle = currentLayoutRect.top - currentView.getTop();
45007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int distance = Math.max(mRowScrollUpAnimationOffset, distanceCurrentTitle);
45107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int distanceToTopOfSecondTitle = oldLayoutRect.top - mRowScrollUpAnimationOffset
45207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    - oldView.getTop();
45307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createTranslationYAnimator(oldTitleView, 0.0f,
45407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    Math.min(distance, distanceToTopOfSecondTitle)));
45507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createAlphaAnimator(oldTitleView, 1.0f, 0.0f, 1.0f, mLinearOutSlowIn)
45607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    .setDuration(mOldContentsFadeOutDuration));
45707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createScaleXAnimator(oldTitleView,
45807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    oldView.getTitleViewScaleSelected(), 1.0f));
45907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createScaleYAnimator(oldTitleView,
46007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    oldView.getTitleViewScaleSelected(), 1.0f));
46107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mTempTitleViewForOld.setScaleX(1.0f);
46207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mTempTitleViewForOld.setScaleY(1.0f);
46307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createAlphaAnimator(mTempTitleViewForOld, 0.0f,
46407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    oldView.getTitleViewAlphaDeselected(), mFastOutLinearIn));
46507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int offset = oldLayoutRect.top - mTempTitleViewForOld.getTop();
46607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createTranslationYAnimator(mTempTitleViewForOld,
46707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    offset - mRowScrollUpAnimationOffset, offset));
46807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
46907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Current row.
47007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        Rect currentLayoutRect = new Rect(layouts.get(position));
47107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        TextView currentTitleView = currentView.getTitleView();
47207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        View currentContentsView = currentView.getContentsView();
47307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        currentContentsView.setAlpha(0.0f);
47407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (scrollDown) {
47507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Current title view.
47607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            setTempTitleView(mTempTitleViewForCurrent, currentTitleView);
47707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // The move distance in the specification is 32dp(mRowScrollUpAnimationOffset).
47807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // But if the height of the upper row is small, the upper row will move up a lot. In
47907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // this case, this row needs to start the move from more than the specification to avoid
48007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // the overlap of the two titles.
48107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // The maximum is to the top of the end position of mTempTitleViewForCurrent.
48207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int distanceOldTitle = oldView.getTop() - oldLayoutRect.top;
48307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int distance = Math.max(mRowScrollUpAnimationOffset, distanceOldTitle);
48407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int distanceTopOfSecondTitle = currentView.getTop() - mRowScrollUpAnimationOffset
48507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    - currentLayoutRect.top;
48607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createTranslationYAnimator(currentTitleView,
48707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    Math.min(distance, distanceTopOfSecondTitle), 0.0f));
48807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            currentView.setTop(currentLayoutRect.top);
48907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            ObjectAnimator animator = createAlphaAnimator(currentTitleView, 0.0f, 1.0f,
49007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mFastOutLinearIn).setDuration(mCurrentContentsFadeInDuration);
49107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animator.setStartDelay(mOldContentsFadeOutDuration);
49207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            currentTitleView.setAlpha(0.0f);
49307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(animator);
49407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createScaleXAnimator(currentTitleView, 1.0f,
49507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    currentView.getTitleViewScaleSelected()));
49607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createScaleYAnimator(currentTitleView, 1.0f,
49707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    currentView.getTitleViewScaleSelected()));
49807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createTranslationYAnimator(mTempTitleViewForCurrent, 0.0f,
49907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    -mRowScrollUpAnimationOffset));
50007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createAlphaAnimator(mTempTitleViewForCurrent,
50107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    currentView.getTitleViewAlphaDeselected(), 0, mLinearOutSlowIn));
50207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Current contents view.
50307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createTranslationYAnimator(currentContentsView,
50407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mRowScrollUpAnimationOffset, 0.0f));
50507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animator = createAlphaAnimator(currentContentsView, 0.0f, 1.0f, mFastOutLinearIn)
50607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    .setDuration(mCurrentContentsFadeInDuration);
50707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animator.setStartDelay(mOldContentsFadeOutDuration);
50807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(animator);
50907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        } else {
51007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            currentView.setBottom(currentLayoutRect.bottom);
51107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Current title view.
51207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int currentViewOffset = currentLayoutRect.top - currentView.getTop();
51307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createTranslationYAnimator(currentTitleView, 0, currentViewOffset));
51407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createAlphaAnimator(currentTitleView,
51507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    currentView.getTitleViewAlphaDeselected(), 1.0f, mFastOutSlowIn));
51607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createScaleXAnimator(currentTitleView, 1.0f,
51707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    currentView.getTitleViewScaleSelected()));
51807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createScaleYAnimator(currentTitleView, 1.0f,
51907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    currentView.getTitleViewScaleSelected()));
52007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Current contents view.
52107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createTranslationYAnimator(currentContentsView,
52207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    currentViewOffset - mRowScrollUpAnimationOffset, currentViewOffset));
52307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            ObjectAnimator animator = createAlphaAnimator(currentContentsView, 0.0f, 1.0f,
52407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mFastOutLinearIn).setDuration(mCurrentContentsFadeInDuration);
52507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animator.setStartDelay(mOldContentsFadeOutDuration);
52607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(animator);
52707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
52807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Next row.
52907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int nextPosition;
53007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (scrollDown) {
53107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            nextPosition = findNextVisiblePosition(position);
53207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (nextPosition != -1) {
53307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                MenuRowView nextView = mMenuRowViews.get(nextPosition);
53407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                Rect nextLayoutRect = layouts.get(nextPosition);
53507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createTranslationYAnimator(nextView,
53607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        nextLayoutRect.top + mRowScrollUpAnimationOffset - nextView.getTop(),
53707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        nextLayoutRect.top - nextView.getTop()));
53807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createAlphaAnimator(nextView, 0.0f, 1.0f, mFastOutLinearIn));
53907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
54007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        } else {
54107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            nextPosition = findNextVisiblePosition(oldPosition);
54207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (nextPosition != -1) {
54307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                MenuRowView nextView = mMenuRowViews.get(nextPosition);
54407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createTranslationYAnimator(nextView, 0, mRowScrollUpAnimationOffset));
54507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createAlphaAnimator(nextView,
54607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        nextView.getTitleViewAlphaDeselected(), 0.0f, 1.0f, mLinearOutSlowIn));
54707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
54807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
54907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Other rows.
55007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int count = mMenuRowViews.size();
55107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (int i = 0; i < count; ++i) {
55207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView view = mMenuRowViews.get(i);
55307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (view.getVisibility() == View.VISIBLE && i != oldPosition && i != position
55407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    && i != nextPosition) {
55507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                Rect rect = layouts.get(i);
55607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                animators.add(createTranslationYAnimator(view, 0, rect.top - view.getTop()));
55707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
55807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
55907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Run animation.
56007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        final List<ViewPropertyValueHolder> propertyValuesAfterAnimation = new ArrayList<>();
56107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        propertyValuesAfterAnimation.addAll(mPropertyValuesAfterAnimation);
56207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mAnimatorSet = new AnimatorSet();
56307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mAnimatorSet.playTogether(animators);
56407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
56507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            @Override
56607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            public void onAnimationEnd(Animator animator) {
56707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                if (DEBUG) dumpChildren("onRowAnimationEndBefore");
56807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mAnimatorSet = null;
56907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // The property values which are different from the end values and need to be
57007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // changed after the animation are set here.
57107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // e.g. setting translationY to 0, alpha of the contents view to 1.
57207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                for (ViewPropertyValueHolder holder : propertyValuesAfterAnimation) {
57307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    holder.property.set(holder.view, holder.value);
57407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                }
57507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                oldTitleView.setVisibility(View.VISIBLE);
57607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mMenuRowViews.get(oldPosition).onDeselected();
57707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mMenuRowViews.get(position).onSelected(true);
57807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mTempTitleViewForOld.setVisibility(View.GONE);
57907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mTempTitleViewForCurrent.setVisibility(View.GONE);
58007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                layout(mMenuView.getLeft(), mMenuView.getTop(), mMenuView.getRight(),
58107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        mMenuView.getBottom());
58207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                if (DEBUG) dumpChildren("onRowAnimationEndAfter");
58307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
58407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                MenuRow currentRow = mMenuRows.get(position);
58507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                if (currentRow.hideTitleWhenSelected()) {
58607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    View titleView = mMenuRowViews.get(position).getTitleView();
58707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mTitleFadeOutAnimator = createAlphaAnimator(titleView, titleView.getAlpha(),
58807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                            0.0f, mLinearOutSlowIn);
58907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mTitleFadeOutAnimator.setStartDelay(TITLE_SHOW_DURATION_BEFORE_HIDDEN_MS);
59007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mTitleFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
59107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        private boolean mCanceled;
59207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
59307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        @Override
59407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        public void onAnimationCancel(Animator animator) {
59507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                            mCanceled = true;
59607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        }
59707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
59807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        @Override
59907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        public void onAnimationEnd(Animator animator) {
60007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                            mTitleFadeOutAnimator = null;
60107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                            if (!mCanceled) {
60207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                                mMenuRowViews.get(position).onSelected(false);
60307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                            }
60407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        }
60507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    });
60607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mTitleFadeOutAnimator.start();
60707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                }
60807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
60907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        });
61007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mAnimatorSet.start();
61107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (DEBUG) dumpChildren("startedRowAnimation()");
61207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
61307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
61407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private void setTempTitleView(TextView dest, TextView src) {
61507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        dest.setVisibility(View.VISIBLE);
61607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        dest.setText(src.getText());
61707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        dest.setTranslationY(0.0f);
61807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (src.getVisibility() == View.VISIBLE) {
61907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            dest.setAlpha(src.getAlpha());
62007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            dest.setScaleX(src.getScaleX());
62107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            dest.setScaleY(src.getScaleY());
62207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        } else {
62307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            dest.setAlpha(0.0f);
62407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            dest.setScaleX(1.0f);
62507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            dest.setScaleY(1.0f);
62607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
62707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        View parent = (View) src.getParent();
62807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        dest.setLeft(src.getLeft() + parent.getLeft());
62907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        dest.setRight(src.getRight() + parent.getLeft());
63007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        dest.setTop(src.getTop() + parent.getTop());
63107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        dest.setBottom(src.getBottom() + parent.getTop());
63207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
63307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
63407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
63507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Called when the menu row information is updated. The add/remove animation of the row views
63607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * will be started.
63707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     *
63807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * <p>Note that the current row should not be removed.
63907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
64007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public void onMenuRowUpdated() {
64107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mMenuView.getVisibility() != View.VISIBLE) {
64207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int count = mMenuRowViews.size();
64307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            for (int i = 0; i < count; ++i) {
6447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mMenuRowViews.get(i)
6457d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        .setVisibility(mMenuRows.get(i).isVisible() ? View.VISIBLE : View.GONE);
64607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
64707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
64807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
64907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
65007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        List<Integer> addedRowViews = new ArrayList<>();
65107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        List<Integer> removedRowViews = new ArrayList<>();
65207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        Map<Integer, Integer> offsetsToMove = new HashMap<>();
65307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int added = 0;
65407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (int i = mSelectedPosition - 1; i >= 0; --i) {
65507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRow row = mMenuRows.get(i);
65607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView view = mMenuRowViews.get(i);
65707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (row.isVisible() && (view.getVisibility() == View.GONE
65807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    || mRemovingRowViews.contains(i))) {
65907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // Removing rows are still VISIBLE.
66007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                addedRowViews.add(i);
66107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                ++added;
66207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else if (!row.isVisible() && view.getVisibility() == View.VISIBLE) {
66307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                removedRowViews.add(i);
66407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                --added;
66507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else if (added != 0) {
66607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                offsetsToMove.put(i, -added);
66707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
66807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
66907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        added = 0;
67007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int count = mMenuRowViews.size();
67107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (int i = mSelectedPosition + 1; i < count; ++i) {
67207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRow row = mMenuRows.get(i);
67307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView view = mMenuRowViews.get(i);
67407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (row.isVisible() && (view.getVisibility() == View.GONE
67507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    || mRemovingRowViews.contains(i))) {
67607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // Removing rows are still VISIBLE.
67707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                addedRowViews.add(i);
67807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                ++added;
67907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else if (!row.isVisible() && view.getVisibility() == View.VISIBLE) {
68007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                removedRowViews.add(i);
68107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                --added;
68207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            } else if (added != 0) {
68307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                offsetsToMove.put(i, added);
68407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
68507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
68607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (addedRowViews.size() == 0 && removedRowViews.size() == 0) {
68707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            return;
68807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
68907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
69007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mAnimatorSet != null) {
69107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // Do not cancel the animation here. The property values should be set to the end values
69207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // when the animation finishes.
69307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mAnimatorSet.end();
69407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
69507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mTitleFadeOutAnimator != null) {
69607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mTitleFadeOutAnimator.end();
69707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
69807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mPropertyValuesAfterAnimation.clear();
69907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        List<Animator> animators = new ArrayList<>();
70007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        List<Rect> layouts = getViewLayouts(mMenuView.getLeft(), mMenuView.getTop(),
70107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mMenuView.getRight(), mMenuView.getBottom(), addedRowViews, removedRowViews);
70207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (int position : addedRowViews) {
70307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView view = mMenuRowViews.get(position);
70407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            view.setVisibility(View.VISIBLE);
70507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            Rect rect = layouts.get(position);
70607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // TODO: The animation is not visible when it is shown for the first time. Need to find
70707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            // a better way to resolve this issue.
70807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            view.layout(rect.left, rect.top, rect.right, rect.bottom);
70907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            View titleView = view.getTitleView();
71007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MarginLayoutParams params = (MarginLayoutParams) titleView.getLayoutParams();
71107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            titleView.layout(view.getPaddingLeft() + params.leftMargin,
71207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    view.getPaddingTop() + params.topMargin,
71307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    rect.right - rect.left - view.getPaddingRight() - params.rightMargin,
71407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    rect.bottom - rect.top - view.getPaddingBottom() - params.bottomMargin);
71507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createAlphaAnimator(view, 0.0f, 1.0f, mFastOutLinearIn));
71607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
71707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (int position : removedRowViews) {
71807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView view = mMenuRowViews.get(position);
71907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createAlphaAnimator(view, 1.0f, 0.0f, 1.0f, mLinearOutSlowIn));
72007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
72107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        for (Entry<Integer, Integer> entry : offsetsToMove.entrySet()) {
72207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            MenuRowView view = mMenuRowViews.get(entry.getKey());
72307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            animators.add(createTranslationYAnimator(view, 0, entry.getValue() * mRowTitleHeight));
72407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
72507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Run animation.
72607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        final List<ViewPropertyValueHolder> propertyValuesAfterAnimation = new ArrayList<>();
72707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        propertyValuesAfterAnimation.addAll(mPropertyValuesAfterAnimation);
72807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRemovingRowViews.clear();
72907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mRemovingRowViews.addAll(removedRowViews);
73007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mAnimatorSet = new AnimatorSet();
73107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mAnimatorSet.playTogether(animators);
73207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
73307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            @Override
73407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            public void onAnimationEnd(Animator animation) {
73507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mAnimatorSet = null;
73607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // The property values which are different from the end values and need to be
73707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // changed after the animation are set here.
73807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // e.g. setting translationY to 0, alpha of the contents view to 1.
73907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                for (ViewPropertyValueHolder holder : propertyValuesAfterAnimation) {
74007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    holder.property.set(holder.view, holder.value);
74107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                }
74207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                for (int position : mRemovingRowViews) {
74307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mMenuRowViews.get(position).setVisibility(View.GONE);
74407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                }
74507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                layout(mMenuView.getLeft(), mMenuView.getTop(), mMenuView.getRight(),
74607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        mMenuView.getBottom());
74707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
74807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        });
74907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mAnimatorSet.start();
75007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (DEBUG) dumpChildren("onMenuRowUpdated()");
75107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
75207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
75307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private ObjectAnimator createTranslationYAnimator(View view, float from, float to) {
75407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, from, to);
75507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setDuration(mRowAnimationDuration);
75607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setInterpolator(mFastOutSlowIn);
75707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mPropertyValuesAfterAnimation.add(new ViewPropertyValueHolder(View.TRANSLATION_Y, view, 0));
75807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return animator;
75907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
76007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
76107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private ObjectAnimator createAlphaAnimator(View view, float from, float to,
76207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            TimeInterpolator interpolator) {
76307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.ALPHA, from, to);
76407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setDuration(mRowAnimationDuration);
76507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setInterpolator(interpolator);
76607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return animator;
76707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
76807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
76907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private ObjectAnimator createAlphaAnimator(View view, float from, float to, float end,
77007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            TimeInterpolator interpolator) {
77107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.ALPHA, from, to);
77207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setDuration(mRowAnimationDuration);
77307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setInterpolator(interpolator);
77407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mPropertyValuesAfterAnimation.add(new ViewPropertyValueHolder(View.ALPHA, view, end));
77507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return animator;
77607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
77707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
77807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private ObjectAnimator createScaleXAnimator(View view, float from, float to) {
77907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.SCALE_X, from, to);
78007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setDuration(mRowAnimationDuration);
78107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setInterpolator(mFastOutSlowIn);
78207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return animator;
78307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
78407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
78507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private ObjectAnimator createScaleYAnimator(View view, float from, float to) {
78607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.SCALE_Y, from, to);
78707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setDuration(mRowAnimationDuration);
78807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        animator.setInterpolator(mFastOutSlowIn);
78907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return animator;
79007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
79107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
79207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
79307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Returns the current position.
79407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
79507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public int getSelectedPosition() {
79607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return mSelectedPosition;
79707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
79807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
79907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private static final class ViewPropertyValueHolder {
80048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public final Property<View, Float> property;
80148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public final View view;
80248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public final float value;
80307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
80407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        public ViewPropertyValueHolder(Property<View, Float> property, View view, float value) {
80507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            this.property = property;
80607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            this.view = view;
80707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            this.value = value;
80807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
80907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
81007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
81107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
81207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Called when the menu becomes visible.
81307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
81407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public void onMenuShow() {
81507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
81607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
81707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    /**
81807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * Called when the menu becomes hidden.
81907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     */
82007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public void onMenuHide() {
82107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mAnimatorSet != null) {
82207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mAnimatorSet.end();
82307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mAnimatorSet = null;
82407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
82507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        // Should be finished after the animator set.
82607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mTitleFadeOutAnimator != null) {
82707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mTitleFadeOutAnimator.end();
82807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mTitleFadeOutAnimator = null;
82907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
83007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
83107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko}
832