1816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/*
2816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Copyright (C) 2015 The Android Open Source Project
3816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
4816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
5816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * you may not use this file except in compliance with the License.
6816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * You may obtain a copy of the License at
7816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
8816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
9816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
10816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Unless required by applicable law or agreed to in writing, software
11816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
12816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * See the License for the specific language governing permissions and
14816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * limitations under the License.
15816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
16816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
17816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopackage com.android.tv.guide;
18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.Animator;
20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.AnimatorInflater;
21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.AnimatorListenerAdapter;
22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.AnimatorSet;
23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.ObjectAnimator;
24816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.PropertyValuesHolder;
25d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.Context;
26816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.SharedPreferences;
27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.res.Resources;
28816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.graphics.Point;
29816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Handler;
30816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Message;
31816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.SystemClock;
32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.preference.PreferenceManager;
3307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.support.annotation.NonNull;
342e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.support.annotation.Nullable;
35816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.v17.leanback.widget.OnChildSelectedListener;
36816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.v17.leanback.widget.SearchOrbView;
37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.v17.leanback.widget.VerticalGridView;
38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.v7.widget.RecyclerView;
39816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log;
40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.View;
41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.View.MeasureSpec;
42816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.ViewGroup;
43816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.ViewGroup.LayoutParams;
44816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.ViewTreeObserver;
45d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.view.accessibility.AccessibilityManager;
46816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.ChannelTuner;
487d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.Features;
49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.MainActivity;
50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.R;
516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.util.DurationTimer;
52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.analytics.Tracker;
5307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport com.android.tv.common.WeakHandler;
54816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ChannelDataManager;
55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.GenreItems;
56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ProgramDataManager;
572e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.android.tv.dvr.DvrDataManager;
5865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.dvr.DvrScheduleManager;
59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.ui.HardwareLayerAnimatorListenerAdapter;
60d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.ui.ViewUtils;
61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.TvInputManagerHelper;
62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.Utils;
63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.ArrayList;
65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.List;
66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.concurrent.TimeUnit;
67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/**
69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The program guide.
70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class ProgramGuide implements ProgramGrid.ChildFocusListener {
72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String TAG = "ProgramGuide";
73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final boolean DEBUG = false;
74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // Whether we should show the guide partially. The first time the user enters the program guide,
76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // we show the grid partially together with the genre side panel on the left. Next time
77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // the program guide is entered, we recover the previous state (partial or full).
78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String KEY_SHOW_GUIDE_PARTIAL = "show_guide_partial";
79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final long TIME_INDICATOR_UPDATE_FREQUENCY = TimeUnit.SECONDS.toMillis(1);
80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final long HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final long HALF_HOUR_IN_MILLIS = HOUR_IN_MILLIS / 2;
82816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // We keep the duration between mStartTime and the current time larger than this value.
83816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // We clip out the first program entry in ProgramManager, if it does not have enough width.
84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // In order to prevent from clipping out the current program, this value need be larger than
85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // or equal to ProgramManager.FIRST_ENTRY_MIN_DURATION.
86816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final long MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME
87816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            = ProgramManager.FIRST_ENTRY_MIN_DURATION;
88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int MSG_PROGRAM_TABLE_FADE_IN_ANIM = 1000;
90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String SCREEN_NAME = "EPG";
92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
93816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final MainActivity mActivity;
94816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ProgramManager mProgramManager;
95d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    private final AccessibilityManager mAccessibilityManager;
96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ChannelTuner mChannelTuner;
97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Tracker mTracker;
98816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final DurationTimer mVisibleDuration = new DurationTimer();
99816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Runnable mPreShowRunnable;
100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Runnable mPostHideRunnable;
101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mWidthPerHour;
103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final long mViewPortMillis;
104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mRowHeight;
105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mDetailHeight;
10607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final int mSelectionRow;  // Row that is focused
107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mTableFadeAnimDuration;
108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mAnimationDuration;
109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mDetailPadding;
110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final SearchOrbView mSearchOrb;
111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mCurrentTimeIndicatorWidth;
112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final View mContainer;
114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final View mSidePanel;
115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final VerticalGridView mSidePanelGridView;
116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final View mTable;
117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final TimelineRow mTimelineRow;
118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ProgramGrid mGrid;
119816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final TimeListAdapter mTimeListAdapter;
120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final View mCurrentTimeIndicator;
121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Animator mShowAnimatorFull;
123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Animator mShowAnimatorPartial;
124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // mHideAnimatorFull and mHideAnimatorPartial are created from the same animation xmls.
125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // When we share the one animator for two different animations, the starting value
126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // is broken, even though the starting value is not defined in XML.
127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Animator mHideAnimatorFull;
128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Animator mHideAnimatorPartial;
129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Animator mPartialToFullAnimator;
130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Animator mFullToPartialAnimator;
131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Animator mProgramTableFadeOutAnimator;
132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Animator mProgramTableFadeInAnimator;
133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // When the program guide is popped up, we keep the previous state of the guide.
135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mShowGuidePartial;
136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final SharedPreferences mSharedPreference;
137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private View mSelectedRow;
138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Animator mDetailOutAnimator;
139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Animator mDetailInAnimator;
140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private long mStartUtcTime;
142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mTimelineAnimation;
143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mLastRequestedGenreId = GenreItems.ID_ALL_CHANNELS;
144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mIsDuringResetRowSelection;
14507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private final Handler mHandler = new ProgramGuideHandler(this);
1466ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private boolean mActive;
147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Runnable mHideRunnable = new Runnable() {
149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public void run() {
151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            hide();
152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    };
154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final long mShowDurationMillis;
15507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private ViewTreeObserver.OnGlobalLayoutListener mOnLayoutListenerForShow;
156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ProgramManagerListener mProgramManagerListener = new ProgramManagerListener();
158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Runnable mUpdateTimeIndicator = new Runnable() {
160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public void run() {
162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            positionCurrentTimeIndicator();
163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mHandler.postAtTime(this,
164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    Utils.ceilTime(SystemClock.uptimeMillis(), TIME_INDICATOR_UPDATE_FREQUENCY));
165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    };
167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public ProgramGuide(MainActivity activity, ChannelTuner channelTuner,
169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            TvInputManagerHelper tvInputManagerHelper, ChannelDataManager channelDataManager,
1702e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            ProgramDataManager programDataManager, @Nullable DvrDataManager dvrDataManager,
17165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            @Nullable DvrScheduleManager dvrScheduleManager, Tracker tracker,
17265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            Runnable preShowRunnable, Runnable postHideRunnable) {
173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mActivity = activity;
1742e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        mProgramManager = new ProgramManager(tvInputManagerHelper, channelDataManager,
17565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                programDataManager, dvrDataManager, dvrScheduleManager);
176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelTuner = channelTuner;
177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker = tracker;
178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mPreShowRunnable = preShowRunnable;
179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mPostHideRunnable = postHideRunnable;
180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Resources res = activity.getResources();
182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mWidthPerHour = res.getDimensionPixelSize(R.dimen.program_guide_table_width_per_hour);
184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        GuideUtils.setWidthPerHour(mWidthPerHour);
185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Point displaySize = new Point();
187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mActivity.getWindowManager().getDefaultDisplay().getSize(displaySize);
188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int gridWidth = displaySize.x
189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                - res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_start)
190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                - res.getDimensionPixelSize(R.dimen.program_guide_table_header_column_width);
191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mViewPortMillis = (gridWidth * HOUR_IN_MILLIS) / mWidthPerHour;
192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mRowHeight = res.getDimensionPixelSize(R.dimen.program_guide_table_item_row_height);
194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDetailHeight = res.getDimensionPixelSize(R.dimen.program_guide_table_detail_height);
19507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mSelectionRow = res.getInteger(R.integer.program_guide_selection_row);
196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTableFadeAnimDuration =
197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                res.getInteger(R.integer.program_guide_table_detail_fade_anim_duration);
198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mShowDurationMillis = res.getInteger(R.integer.program_guide_show_duration);
199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mAnimationDuration =
200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                res.getInteger(R.integer.program_guide_table_detail_toggle_anim_duration);
201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDetailPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_detail_padding);
202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mContainer = mActivity.findViewById(R.id.program_guide);
204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        ViewTreeObserver.OnGlobalFocusChangeListener globalFocusChangeListener
205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                = new GlobalFocusChangeListener();
206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mContainer.getViewTreeObserver().addOnGlobalFocusChangeListener(globalFocusChangeListener);
207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        GenreListAdapter genreListAdapter = new GenreListAdapter(mActivity, mProgramManager, this);
209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanel = mContainer.findViewById(R.id.program_guide_side_panel);
210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanelGridView = (VerticalGridView) mContainer.findViewById(
211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.id.program_guide_side_panel_grid_view);
212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanelGridView.getRecycledViewPool().setMaxRecycledViews(
213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.layout.program_guide_side_panel_row,
214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                res.getInteger(R.integer.max_recycled_view_pool_epg_side_panel_row));
215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanelGridView.setAdapter(genreListAdapter);
216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanelGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanelGridView.setWindowAlignmentOffset(mActivity.getResources()
218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .getDimensionPixelOffset(R.dimen.program_guide_side_panel_alignment_y));
219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanelGridView.setWindowAlignmentOffsetPercent(
220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
2216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
2227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (Features.EPG_SEARCH.isEnabled(mActivity)) {
223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mSearchOrb = (SearchOrbView) mContainer.findViewById(
224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    R.id.program_guide_side_panel_search_orb);
225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mSearchOrb.setVisibility(View.VISIBLE);
226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mSearchOrb.setOnOrbClickedListener(new View.OnClickListener() {
228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onClick(View view) {
230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    hide();
231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mActivity.showProgramGuideSearchFragment();
232816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mSidePanelGridView.setOnChildSelectedListener(
235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    new android.support.v17.leanback.widget.OnChildSelectedListener() {
236816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onChildSelected(ViewGroup viewGroup, View view, int i, long l) {
238816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mSearchOrb.animate().alpha(i == 0 ? 1.0f : 0.0f);
239816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
240816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mSearchOrb = null;
243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTable = mContainer.findViewById(R.id.program_guide_table);
246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimelineRow = (TimelineRow) mTable.findViewById(R.id.time_row);
248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimeListAdapter = new TimeListAdapter(res);
249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimelineRow.getRecycledViewPool().setMaxRecycledViews(
250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.layout.program_guide_table_header_row_item,
251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                res.getInteger(R.integer.max_recycled_view_pool_epg_header_row_item));
252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimelineRow.setAdapter(mTimeListAdapter);
253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
2546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        ProgramTableAdapter programTableAdapter = new ProgramTableAdapter(mActivity, this);
255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        programTableAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onChanged() {
258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // It is usually called when Genre is changed.
259816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // Reset selection of ProgramGrid
260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                resetRowSelection();
261ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                updateGuidePosition();
262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid = (ProgramGrid) mTable.findViewById(R.id.grid);
266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.initialize(mProgramManager);
267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.getRecycledViewPool().setMaxRecycledViews(
268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.layout.program_guide_table_row,
269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                res.getInteger(R.integer.max_recycled_view_pool_epg_table_row));
270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.setAdapter(programTableAdapter);
271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.setChildFocusListener(this);
273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.setOnChildSelectedListener(new OnChildSelectedListener() {
274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (mIsDuringResetRowSelection) {
277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    // Ignore if it's during the first resetRowSelection, because onChildSelected
278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    // will be called again when rows are bound to the program table. if selectRow
279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    // is called here, mSelectedRow is set and the second selectRow call doesn't
280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    // work as intended.
281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mIsDuringResetRowSelection = false;
282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    return;
283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
284816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                selectRow(view);
285816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.setFocusScrollStrategy(ProgramGrid.FOCUS_SCROLL_ALIGNED);
28807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mGrid.setWindowAlignmentOffset(mSelectionRow * mRowHeight);
289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.setWindowAlignmentOffsetPercent(ProgramGrid.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.setItemAlignmentOffset(0);
291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.setItemAlignmentOffsetPercent(ProgramGrid.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                onHorizontalScrolled(dx);
297816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        };
299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimelineRow.addOnScrollListener(onScrollListener);
300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mCurrentTimeIndicator = mTable.findViewById(R.id.current_time_indicator);
302816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mShowAnimatorFull = createAnimator(
304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_side_panel_enter_full,
305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                0,
306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_table_enter_full);
307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
308816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mShowAnimatorPartial = createAnimator(
309816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_side_panel_enter_partial,
310816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                0,
311816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_table_enter_partial);
312816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mShowAnimatorPartial.addListener(new AnimatorListenerAdapter() {
313816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
314816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationStart(Animator animation) {
315816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mSidePanelGridView.setVisibility(View.VISIBLE);
316816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mSidePanelGridView.setAlpha(1.0f);
317816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
318816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
319816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHideAnimatorFull = createAnimator(
321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_side_panel_exit,
322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                0,
323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_table_exit);
324816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHideAnimatorFull.addListener(new AnimatorListenerAdapter() {
325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationEnd(Animator animation) {
32707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mContainer.setVisibility(View.GONE);
328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHideAnimatorPartial = createAnimator(
331816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_side_panel_exit,
332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                0,
333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_table_exit);
334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHideAnimatorPartial.addListener(new AnimatorListenerAdapter() {
335816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationEnd(Animator animation) {
33707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mContainer.setVisibility(View.GONE);
338816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
339816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mPartialToFullAnimator = createAnimator(
342816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_side_panel_hide,
343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_side_panel_grid_fade_out,
344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_table_partial_to_full);
345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mFullToPartialAnimator = createAnimator(
346816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_side_panel_reveal,
347816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_side_panel_grid_fade_in,
348816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_table_full_to_partial);
349816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
350816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramTableFadeOutAnimator = AnimatorInflater.loadAnimator(mActivity,
351816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_table_fade_out);
352816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramTableFadeOutAnimator.setTarget(mTable);
353816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramTableFadeOutAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(mTable) {
354816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
355816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationEnd(Animator animation) {
356816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                super.onAnimationEnd(animation);
357816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
358816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (!isActive()) {
359816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    return;
360816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
361816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mProgramManager.resetChannelListWithGenre(mLastRequestedGenreId);
362816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                resetTimelineScroll();
363816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (!mHandler.hasMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM)) {
364816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mHandler.sendEmptyMessage(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
365816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
366816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
367816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
368816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramTableFadeInAnimator = AnimatorInflater.loadAnimator(mActivity,
369816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.animator.program_guide_table_fade_in);
370816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramTableFadeInAnimator.setTarget(mTable);
371816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramTableFadeInAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(mTable));
372816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSharedPreference = PreferenceManager.getDefaultSharedPreferences(mActivity);
373d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        mAccessibilityManager =
374d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                (AccessibilityManager) mActivity.getSystemService(Context.ACCESSIBILITY_SERVICE);
375d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        mShowGuidePartial = mAccessibilityManager.isEnabled()
376d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                || mSharedPreference.getBoolean(KEY_SHOW_GUIDE_PARTIAL, true);
377816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
378816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
379816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
380816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onRequestChildFocus(View oldFocus, View newFocus) {
381816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (oldFocus != null && newFocus != null) {
38207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            int selectionRowOffset = mSelectionRow * mRowHeight;
383816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (oldFocus.getTop() < newFocus.getTop()) {
384816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // Selection moves downwards
385816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // Adjust scroll offset to be at the bottom of the target row and to expand up. This
386816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // will set the scroll target to be one row height up from its current position.
387816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mGrid.setWindowAlignmentOffset(selectionRowOffset + mRowHeight + mDetailHeight);
388816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mGrid.setItemAlignmentOffsetPercent(100);
389816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } else if (oldFocus.getTop() > newFocus.getTop()) {
390816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // Selection moves upwards
391816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // Adjust scroll offset to be at the top of the target row and to expand down. This
392816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // will set the scroll target to be one row height down from its current position.
393816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mGrid.setWindowAlignmentOffset(selectionRowOffset);
394816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mGrid.setItemAlignmentOffsetPercent(0);
395816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
396816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
397816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
398816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Show the program guide.  This reveals the side panel, and the program guide table is shown
401816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * partially.
40207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     *
40307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * <p>Note: the animation which starts together with ProgramGuide showing animation needs to
40407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * be initiated in {@code runnableAfterAnimatorReady}. If the animation starts together
40507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko     * with show(), the animation may drop some frames.
406816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
40707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    public void show(final Runnable runnableAfterAnimatorReady) {
408816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mContainer.getVisibility() == View.VISIBLE) {
409816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
410816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
411816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendShowEpg();
412816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendScreenView(SCREEN_NAME);
413816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mPreShowRunnable != null) {
414816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mPreShowRunnable.run();
415816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
416816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mVisibleDuration.start();
417816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
418816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramManager.programGuideVisibilityChanged(true);
419816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mStartUtcTime = Utils.floorTime(
420816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                System.currentTimeMillis() - MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME,
421816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                HALF_HOUR_IN_MILLIS);
42207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mProgramManager.updateInitialTimeRange(mStartUtcTime, mStartUtcTime + mViewPortMillis);
423816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramManager.addListener(mProgramManagerListener);
424816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mLastRequestedGenreId = GenreItems.ID_ALL_CHANNELS;
425816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimeListAdapter.update(mStartUtcTime);
426816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimelineRow.resetScroll();
427816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
4286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mContainer.setVisibility(View.VISIBLE);
4296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mActive = true;
430816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!mShowGuidePartial) {
4316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mTable.requestFocus();
432816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
43307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        positionCurrentTimeIndicator();
434816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanelGridView.setSelectedPosition(0);
43507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (DEBUG) {
43607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            Log.d(TAG, "show()");
43707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
43807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mOnLayoutListenerForShow = new ViewTreeObserver.OnGlobalLayoutListener() {
439816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
44007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            public void onGlobalLayout() {
44107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
44207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mTable.setLayerType(View.LAYER_TYPE_HARDWARE, null);
44307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mSidePanelGridView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
44407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mTable.buildLayer();
44507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mSidePanelGridView.buildLayer();
44607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mOnLayoutListenerForShow = null;
44707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mTimelineAnimation = true;
44807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                // Make sure that time indicator update starts after animation is finished.
44907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                startCurrentTimeIndicator(TIME_INDICATOR_UPDATE_FREQUENCY);
45007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                if (DEBUG) {
45107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                    mContainer.getViewTreeObserver().addOnDrawListener(
45207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                            new ViewTreeObserver.OnDrawListener() {
453ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                long time = System.currentTimeMillis();
454ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                int count = 0;
455ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
456ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                @Override
457ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                public void onDraw() {
458ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                    long curtime = System.currentTimeMillis();
459ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                    Log.d(TAG, "onDraw " + count++ + " " + (curtime - time) + "ms");
460ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                    time = curtime;
461ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                    if (count > 10) {
462ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                        mContainer.getViewTreeObserver().removeOnDrawListener(this);
463ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                    }
464ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                }
465ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                            });
46607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                }
4676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                updateGuidePosition();
46807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                runnableAfterAnimatorReady.run();
469816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (mShowGuidePartial) {
470816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mShowAnimatorPartial.start();
471816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                } else {
472816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mShowAnimatorFull.start();
473816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
474816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
47507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        };
47607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mContainer.getViewTreeObserver().addOnGlobalLayoutListener(mOnLayoutListenerForShow);
477816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        scheduleHide();
478816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
479816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
480816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
481816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Hide the program guide.
482816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
483816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void hide() {
484816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!isActive()) {
485816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
486816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
48707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (mOnLayoutListenerForShow != null) {
48807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mContainer.getViewTreeObserver().removeOnGlobalLayoutListener(mOnLayoutListenerForShow);
48907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            mOnLayoutListenerForShow = null;
49007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
491816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendHideEpg(mVisibleDuration.reset());
492816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        cancelHide();
493816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramManager.programGuideVisibilityChanged(false);
494816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramManager.removeListener(mProgramManagerListener);
4956ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mActive = false;
4966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (!mShowGuidePartial) {
497816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mHideAnimatorFull.start();
498816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
499816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mHideAnimatorPartial.start();
500816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
501816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
502816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // Clears fade-out/in animation for genre change
503816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mProgramTableFadeOutAnimator.isRunning()) {
504816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mProgramTableFadeOutAnimator.cancel();
505816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
506816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mProgramTableFadeInAnimator.isRunning()) {
507816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mProgramTableFadeInAnimator.cancel();
508816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
509816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHandler.removeMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
510816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTable.setAlpha(1.0f);
511816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
512816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimelineAnimation = false;
513816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        stopCurrentTimeIndicator();
514816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mPostHideRunnable != null) {
515816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mPostHideRunnable.run();
516816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
517816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
518816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
5196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
5206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Schedules hiding the program guide.
5216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
522816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void scheduleHide() {
523816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        cancelHide();
524816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHandler.postDelayed(mHideRunnable, mShowDurationMillis);
525816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
526816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
527816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
5286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Cancels hiding the program guide.
5292e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko     */
5302e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    public void cancelHide() {
531816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHandler.removeCallbacks(mHideRunnable);
532816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
533816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
534816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
535816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Process the {@code KEYCODE_BACK} key event.
536816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
537816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onBackPressed() {
538816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        hide();
539816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
540816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
541816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
5426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Returns {@code true} if the program guide should process the input events.
543816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
5446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public boolean isActive() {
5456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return mActive;
5466ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
5476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
5486ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
5496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Returns {@code true} if the program guide is shown, i.e. showing animation is done and
5506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * hiding animation is not started yet.
5516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
5526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public boolean isRunningAnimation() {
5536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return mShowAnimatorPartial.isStarted() || mShowAnimatorFull.isStarted()
5546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                || mHideAnimatorPartial.isStarted() || mHideAnimatorFull.isStarted();
5556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
5566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
5576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /** Returns if program table is in full screen mode. **/
5586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    boolean isFull() {
5596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return !mShowGuidePartial;
560816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
561816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
562816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
563816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Requests change genre to {@code genreId}.
564816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
5656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    void requestGenreChange(int genreId) {
566816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mLastRequestedGenreId == genreId) {
567816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // When Recycler.onLayout() removes its children to recycle,
568816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // View tries to find next focus candidate immediately
569816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // so GenreListAdapter can take focus back while it's hiding.
570816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // Returns early here to prevent re-entrance.
571816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
572816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
573816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mLastRequestedGenreId = genreId;
574816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mProgramTableFadeOutAnimator.isStarted()) {
575816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // When requestGenreChange is called repeatedly in short time, we keep the fade-out
576816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // state for mTableFadeAnimDuration from now. Without it, we'll see blinks.
577816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mHandler.removeMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
578816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mHandler.sendEmptyMessageDelayed(MSG_PROGRAM_TABLE_FADE_IN_ANIM,
579816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mTableFadeAnimDuration);
580816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
581816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
582816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mHandler.hasMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM)) {
583816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mProgramManager.resetChannelListWithGenre(mLastRequestedGenreId);
584816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mHandler.removeMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
585816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mHandler.sendEmptyMessageDelayed(MSG_PROGRAM_TABLE_FADE_IN_ANIM,
586816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mTableFadeAnimDuration);
587816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
588816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
589816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mProgramTableFadeInAnimator.isStarted()) {
590816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mProgramTableFadeInAnimator.cancel();
591816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
592816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
593816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramTableFadeOutAnimator.start();
594816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
595816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
5966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
5976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Returns the scroll offset of the time line row in pixels.
5986ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
5996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    int getTimelineRowScrollOffset() {
6006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return mTimelineRow.getScrollOffset();
6016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
6026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6036ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /** Returns the program grid view that hold all component views. */
6046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    ProgramGrid getProgramGrid() {
6056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return mGrid;
6066ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
6076ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6086ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
6096ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Gets {@link VerticalGridView} for "genre select" side panel.
6106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
6116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    VerticalGridView getSidePanel() {
6126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return mSidePanelGridView;
6136ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
6146ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6156ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /** Returns the program manager the program guide is using to provide program information. */
6166ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    ProgramManager getProgramManager() {
6176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return mProgramManager;
6186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
6196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private void updateGuidePosition() {
6216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        // Align EPG at vertical center, if EPG table height is less than the screen size.
6226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        Resources res = mActivity.getResources();
6236ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int screenHeight = mContainer.getHeight();
6246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (screenHeight <= 0) {
6256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // mContainer is not initialized yet.
6266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return;
6276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
6286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int startPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_start);
6296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int topPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_top);
6306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int bottomPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_bottom);
6316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int tableHeight = res.getDimensionPixelOffset(R.dimen.program_guide_table_header_row_height)
6326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                + mDetailHeight + mRowHeight * mGrid.getAdapter().getItemCount() + topPadding
6336ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                + bottomPadding;
6346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (tableHeight > screenHeight) {
6356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // EPG height is longer that the screen height.
6366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mTable.setPaddingRelative(startPadding, topPadding, 0, 0);
6376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            LayoutParams layoutParams = mTable.getLayoutParams();
6386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            layoutParams.height = LayoutParams.WRAP_CONTENT;
6396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mTable.setLayoutParams(layoutParams);
6406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        } else {
6416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mTable.setPaddingRelative(startPadding, topPadding, 0, bottomPadding);
6426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            LayoutParams layoutParams = mTable.getLayoutParams();
6436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            layoutParams.height = tableHeight;
6446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mTable.setLayoutParams(layoutParams);
6456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
6466ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
6476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6486ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private Animator createAnimator(int sidePanelAnimResId, int sidePanelGridAnimResId,
6496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            int tableAnimResId) {
6506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        List<Animator> animatorList = new ArrayList<>();
6516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        Animator sidePanelAnimator = AnimatorInflater.loadAnimator(mActivity, sidePanelAnimResId);
6536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        sidePanelAnimator.setTarget(mSidePanel);
6546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        animatorList.add(sidePanelAnimator);
6556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (sidePanelGridAnimResId != 0) {
6576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            Animator sidePanelGridAnimator = AnimatorInflater.loadAnimator(mActivity,
6586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    sidePanelGridAnimResId);
6596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            sidePanelGridAnimator.setTarget(mSidePanelGridView);
6606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            sidePanelGridAnimator.addListener(
6616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    new HardwareLayerAnimatorListenerAdapter(mSidePanelGridView));
6626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            animatorList.add(sidePanelGridAnimator);
6636ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
6646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        Animator tableAnimator = AnimatorInflater.loadAnimator(mActivity, tableAnimResId);
6656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        tableAnimator.setTarget(mTable);
6666ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        tableAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(mTable));
6676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        animatorList.add(tableAnimator);
6686ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6696ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        AnimatorSet set = new AnimatorSet();
6706ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        set.playTogether(animatorList);
6716ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return set;
6726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
6736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private void startFull() {
6756ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (!mShowGuidePartial || mAccessibilityManager.isEnabled()) {
6766ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // If accessibility service is enabled, focus cannot be moved to side panel due to it's
6776ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // hidden. Therefore, we don't hide side panel when accessibility service is enabled.
6786ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return;
6796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
6806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mShowGuidePartial = false;
6816ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mSharedPreference.edit().putBoolean(KEY_SHOW_GUIDE_PARTIAL, mShowGuidePartial).apply();
6826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mPartialToFullAnimator.start();
6836ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
6846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
6856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private void startPartial() {
6866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (mShowGuidePartial) {
6876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return;
6886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
6896ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mShowGuidePartial = true;
6906ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mSharedPreference.edit().putBoolean(KEY_SHOW_GUIDE_PARTIAL, mShowGuidePartial).apply();
6916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mFullToPartialAnimator.start();
6926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
6936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
69407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private void startCurrentTimeIndicator(long initialDelay) {
69507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mHandler.postDelayed(mUpdateTimeIndicator, initialDelay);
696816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
697816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
698816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void stopCurrentTimeIndicator() {
699816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHandler.removeCallbacks(mUpdateTimeIndicator);
700816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
701816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
702816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void positionCurrentTimeIndicator() {
703816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int offset = GuideUtils.convertMillisToPixel(mStartUtcTime, System.currentTimeMillis())
704816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                - mTimelineRow.getScrollOffset();
705816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (offset < 0) {
706816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mCurrentTimeIndicator.setVisibility(View.GONE);
707816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
708816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mCurrentTimeIndicatorWidth == 0) {
709816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mCurrentTimeIndicator.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
710816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mCurrentTimeIndicatorWidth = mCurrentTimeIndicator.getMeasuredWidth();
711816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
712816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mCurrentTimeIndicator.setPaddingRelative(
713816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    offset - mCurrentTimeIndicatorWidth / 2, 0, 0, 0);
714816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mCurrentTimeIndicator.setVisibility(View.VISIBLE);
715816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
716816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
717816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
718816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void resetTimelineScroll() {
719816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mProgramManager.getFromUtcMillis() != mStartUtcTime) {
720816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            boolean timelineAnimation = mTimelineAnimation;
721816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTimelineAnimation = false;
722816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // mProgramManagerListener.onTimeRangeUpdated() will be called by shiftTime().
723816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mProgramManager.shiftTime(mStartUtcTime - mProgramManager.getFromUtcMillis());
724816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTimelineAnimation = timelineAnimation;
725816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
726816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
727816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
728816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void onHorizontalScrolled(int dx) {
729816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (DEBUG) Log.d(TAG, "onHorizontalScrolled(dx=" + dx + ")");
730816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        positionCurrentTimeIndicator();
731816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (int i = 0, n = mGrid.getChildCount(); i < n; ++i) {
732816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mGrid.getChildAt(i).findViewById(R.id.row).scrollBy(dx, 0);
733816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
734816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
735816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
736816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void resetRowSelection() {
737816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mDetailOutAnimator != null) {
738816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mDetailOutAnimator.end();
739816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
740816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mDetailInAnimator != null) {
741816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mDetailInAnimator.cancel();
742816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
743816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSelectedRow = null;
744816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mIsDuringResetRowSelection = true;
745816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.setSelectedPosition(
746816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                Math.max(mProgramManager.getChannelIndex(mChannelTuner.getCurrentChannel()),
747816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        0));
748816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mGrid.resetFocusState();
7492e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        mGrid.onItemSelectionReset();
750816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mIsDuringResetRowSelection = false;
751816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
752816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
753816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void selectRow(View row) {
754816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (row == null || row == mSelectedRow) {
755816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
756816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
757816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mSelectedRow == null
758816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                || mGrid.getChildAdapterPosition(mSelectedRow) == RecyclerView.NO_POSITION) {
759816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mSelectedRow != null) {
760816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                View oldDetailView = mSelectedRow.findViewById(R.id.detail);
761816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                oldDetailView.setVisibility(View.GONE);
762816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
763816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            View detailView = row.findViewById(R.id.detail);
764816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            detailView.findViewById(R.id.detail_content_full).setAlpha(1);
765816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            detailView.findViewById(R.id.detail_content_full).setTranslationY(0);
766d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            ViewUtils.setLayoutHeight(detailView, mDetailHeight);
767816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            detailView.setVisibility(View.VISIBLE);
768816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
769816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            final ProgramRow programRow = (ProgramRow) row.findViewById(R.id.row);
770816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            programRow.post(new Runnable() {
771816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
772816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void run() {
773816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    programRow.focusCurrentProgram();
774816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
775816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
776816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
777816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            animateRowChange(mSelectedRow, row);
778816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
779816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSelectedRow = row;
780816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
781816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
782816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void animateRowChange(View outRow, View inRow) {
783816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mDetailOutAnimator != null) {
784816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mDetailOutAnimator.end();
785816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
786816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mDetailInAnimator != null) {
787816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mDetailInAnimator.cancel();
788816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
789816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
7906ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int operationDirection = mGrid.getLastUpDownDirection();
7916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int animationPadding = 0;
7926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (operationDirection == View.FOCUS_UP) {
7936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            animationPadding = mDetailPadding;
7946ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        } else if (operationDirection == View.FOCUS_DOWN) {
7956ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            animationPadding = -mDetailPadding;
796816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
797816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
798816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        View outDetail = outRow != null ? outRow.findViewById(R.id.detail) : null;
799816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (outDetail != null && outDetail.isShown()) {
800816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            final View outDetailContent = outDetail.findViewById(R.id.detail_content_full);
801816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
802816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Animator fadeOutAnimator = ObjectAnimator.ofPropertyValuesHolder(outDetailContent,
803816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    PropertyValuesHolder.ofFloat(View.ALPHA, outDetail.getAlpha(), 0f),
804816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    PropertyValuesHolder.ofFloat(View.TRANSLATION_Y,
8056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                            outDetailContent.getTranslationY(), animationPadding));
806816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            fadeOutAnimator.setStartDelay(0);
807816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            fadeOutAnimator.setDuration(mAnimationDuration);
808816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            fadeOutAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(outDetailContent));
809816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
810d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            Animator collapseAnimator = ViewUtils
811d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    .createHeightAnimator(outDetail, ViewUtils.getLayoutHeight(outDetail), 0);
812816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            collapseAnimator.setStartDelay(mAnimationDuration);
813816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            collapseAnimator.setDuration(mTableFadeAnimDuration);
814816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            collapseAnimator.addListener(new AnimatorListenerAdapter() {
815816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
816816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onAnimationStart(Animator animator) {
817816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    outDetailContent.setVisibility(View.GONE);
818816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
819816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
820816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
821816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onAnimationEnd(Animator animator) {
822816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    outDetailContent.setVisibility(View.VISIBLE);
823816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
824816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
825816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
826816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            AnimatorSet outAnimator = new AnimatorSet();
827816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            outAnimator.playTogether(fadeOutAnimator, collapseAnimator);
828816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            outAnimator.addListener(new AnimatorListenerAdapter() {
829816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
830816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onAnimationEnd(Animator animator) {
831816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mDetailOutAnimator = null;
832816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
833816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
834816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mDetailOutAnimator = outAnimator;
835816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            outAnimator.start();
836816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
837816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
838816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        View inDetail = inRow != null ? inRow.findViewById(R.id.detail) : null;
839816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (inDetail != null) {
840816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            final View inDetailContent = inDetail.findViewById(R.id.detail_content_full);
841816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
842d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            Animator expandAnimator = ViewUtils.createHeightAnimator(inDetail, 0, mDetailHeight);
843816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            expandAnimator.setStartDelay(mAnimationDuration);
844816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            expandAnimator.setDuration(mTableFadeAnimDuration);
845816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            expandAnimator.addListener(new AnimatorListenerAdapter() {
846816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
847816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onAnimationStart(Animator animator) {
848816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    inDetailContent.setVisibility(View.GONE);
849816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
850816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
851816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
852816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onAnimationEnd(Animator animator) {
853816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    inDetailContent.setVisibility(View.VISIBLE);
854816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    inDetailContent.setAlpha(0);
855816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
856816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
857816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Animator fadeInAnimator = ObjectAnimator.ofPropertyValuesHolder(inDetailContent,
858816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
8596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, -animationPadding, 0f));
860816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            fadeInAnimator.setDuration(mAnimationDuration);
861816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            fadeInAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(inDetailContent));
862816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
863816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            AnimatorSet inAnimator = new AnimatorSet();
864d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            inAnimator.playSequentially(expandAnimator, fadeInAnimator);
865816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            inAnimator.addListener(new AnimatorListenerAdapter() {
866816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
867816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onAnimationEnd(Animator animator) {
868816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mDetailInAnimator = null;
869816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
870816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
871816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mDetailInAnimator = inAnimator;
872816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            inAnimator.start();
873816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
874816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
875816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
876816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private class GlobalFocusChangeListener implements
877816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            ViewTreeObserver.OnGlobalFocusChangeListener {
878816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        private static final int UNKNOWN = 0;
879816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        private static final int SIDE_PANEL = 1;
880816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        private static final int PROGRAM_TABLE = 2;
881816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
882816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
883816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
884816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (DEBUG) Log.d(TAG, "onGlobalFocusChanged " + oldFocus + " -> " + newFocus);
885816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (!isActive()) {
886816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return;
887816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
888816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            int fromLocation = getLocation(oldFocus);
889816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            int toLocation = getLocation(newFocus);
890816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (fromLocation == SIDE_PANEL && toLocation == PROGRAM_TABLE) {
891816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                startFull();
892816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } else if (fromLocation == PROGRAM_TABLE && toLocation == SIDE_PANEL) {
893816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                startPartial();
894816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
895816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
896816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
897816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        private int getLocation(View view) {
898816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (view == null) {
899816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return UNKNOWN;
900816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
901816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            for (Object obj = view; obj instanceof View; obj = ((View) obj).getParent()) {
902816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (obj == mSidePanel) {
903816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    return SIDE_PANEL;
904816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                } else if (obj == mGrid) {
905816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    return PROGRAM_TABLE;
906816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
907816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
908816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return UNKNOWN;
909816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
910816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
911816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
912816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private class ProgramManagerListener extends ProgramManager.ListenerAdapter {
913816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
914816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public void onTimeRangeUpdated() {
915816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            int scrollOffset = (int) (mWidthPerHour * mProgramManager.getShiftedTime()
916816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    / HOUR_IN_MILLIS);
917816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (DEBUG) {
918816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                Log.d(TAG, "Horizontal scroll to " + scrollOffset + " pixels ("
919816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        + mProgramManager.getShiftedTime() + " millis)");
920816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
921816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTimelineRow.scrollTo(scrollOffset, mTimelineAnimation);
922816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
923816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
92407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
92507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private static class ProgramGuideHandler extends WeakHandler<ProgramGuide> {
9266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        ProgramGuideHandler(ProgramGuide ref) {
92707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            super(ref);
92807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
92907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
93007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        @Override
93107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        public void handleMessage(Message msg, @NonNull ProgramGuide programGuide) {
93207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (msg.what == MSG_PROGRAM_TABLE_FADE_IN_ANIM) {
93307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                programGuide.mProgramTableFadeInAnimator.start();
93407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
93507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        }
93607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
937816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko}
938