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
1965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.annotation.SuppressLint;
20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Context;
21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.res.ColorStateList;
22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.res.Resources;
23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.graphics.drawable.Drawable;
24816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.graphics.drawable.LayerDrawable;
25816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.graphics.drawable.StateListDrawable;
26816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Handler;
27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.SystemClock;
28816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.text.SpannableStringBuilder;
29816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.text.Spanned;
30816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.text.TextUtils;
31816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.text.style.TextAppearanceSpan;
32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.AttributeSet;
33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log;
34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.View;
35816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.ViewGroup;
36816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.widget.TextView;
3765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.widget.Toast;
38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
391abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.ApplicationSingletons;
40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.MainActivity;
41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.R;
42816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.TvApplication;
43816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.analytics.Tracker;
44ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.feature.CommonFeatures;
45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.Channel;
461abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.dvr.DvrManager;
476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.data.ScheduledRecording;
486ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.ui.DvrUiHelper;
49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.guide.ProgramManager.TableEntry;
50d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.util.ToastUtils;
51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.Utils;
52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.lang.reflect.InvocationTargetException;
54816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.concurrent.TimeUnit;
55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class ProgramItemView extends TextView {
57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String TAG = "ProgramItemView";
58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final long FOCUS_UPDATE_FREQUENCY = TimeUnit.SECONDS.toMillis(1);
60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int MAX_PROGRESS = 10000; // From android.widget.ProgressBar.MAX_VALUE
61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // State indicating the focused program is the current program
63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int[] STATE_CURRENT_PROGRAM = { R.attr.state_current_program };
64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // Workaround state in order to not use too much texture memory for RippleDrawable
66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int[] STATE_TOO_WIDE = { R.attr.state_program_too_wide };
67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static int sVisibleThreshold;
69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static int sItemPadding;
7065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private static int sCompoundDrawablePadding;
71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static TextAppearanceSpan sProgramTitleStyle;
72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static TextAppearanceSpan sGrayedOutProgramTitleStyle;
73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static TextAppearanceSpan sEpisodeTitleStyle;
74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static TextAppearanceSpan sGrayedOutEpisodeTitleStyle;
75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
766ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private ProgramGuide mProgramGuide;
7765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private DvrManager mDvrManager;
78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private TableEntry mTableEntry;
79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mMaxWidthForRipple;
8007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private int mTextWidth;
81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
82816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // If set this flag disables requests to re-layout the parent view as a result of changing
83816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // this view, improving performance. This also prevents the parent view to lose child focus
84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // as a result of the re-layout (see b/21378855).
85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mPreventParentRelayout;
86816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
87816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final View.OnClickListener ON_CLICKED = new View.OnClickListener() {
88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
89ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        public void onClick(final View view) {
90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            TableEntry entry = ((ProgramItemView) view).mTableEntry;
912e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            if (entry == null) {
922e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                //do nothing
932e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                return;
942e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            }
951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ApplicationSingletons singletons = TvApplication.getSingletons(view.getContext());
961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Tracker tracker = singletons.getTracker();
97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            tracker.sendEpgItemClicked();
98d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            final MainActivity tvActivity = (MainActivity) view.getContext();
99d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            final Channel channel = tvActivity.getChannelDataManager().getChannel(entry.channelId);
100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (entry.isCurrentProgram()) {
101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                view.postDelayed(new Runnable() {
102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    @Override
103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    public void run() {
104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        tvActivity.tuneToChannel(channel);
105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        tvActivity.hideOverlaysForTune();
106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
107ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                }, entry.getWidth() > ((ProgramItemView) view).mMaxWidthForRipple ? 0
108ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        : view.getResources()
109ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                .getInteger(R.integer.program_guide_ripple_anim_duration));
1106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            } else if (entry.program != null && CommonFeatures.DVR.isEnabled(view.getContext())) {
11165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                DvrManager dvrManager = singletons.getDvrManager();
11265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                if (entry.entryStartUtcMillis > System.currentTimeMillis()
11365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        && dvrManager.isProgramRecordable(entry.program)) {
1142e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                    if (entry.scheduledRecording == null) {
1156ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        DvrUiHelper.checkStorageStatusAndShowErrorMessage(tvActivity,
1166ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                channel.getInputId(), new Runnable() {
1176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                    @Override
1186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                    public void run() {
1196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                        DvrUiHelper.requestRecordingFutureProgram(tvActivity,
1206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                                entry.program, false);
1216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                    }
1226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                });
1232e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                    } else {
12465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        dvrManager.removeScheduledRecording(entry.scheduledRecording);
12565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        String msg = view.getResources().getString(
12665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                                R.string.dvr_schedules_deletion_info, entry.program.getTitle());
127d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        ToastUtils.show(view.getContext(), msg, Toast.LENGTH_SHORT);
1282e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                    }
12965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                } else {
130d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    ToastUtils.show(view.getContext(), view.getResources()
131d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            .getString(R.string.dvr_msg_cannot_record_program), Toast.LENGTH_SHORT);
132ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                }
133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1352e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    };
136ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final View.OnFocusChangeListener ON_FOCUS_CHANGED =
138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            new View.OnFocusChangeListener() {
139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public void onFocusChange(View view, boolean hasFocus) {
141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (hasFocus) {
142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                ((ProgramItemView) view).mUpdateFocus.run();
143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } else {
144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                Handler handler = view.getHandler();
145816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (handler != null) {
146816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    handler.removeCallbacks(((ProgramItemView) view).mUpdateFocus);
147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    };
151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Runnable mUpdateFocus = new Runnable() {
153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public void run() {
155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            refreshDrawableState();
156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            TableEntry entry = mTableEntry;
1572e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            if (entry == null) {
1582e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                //do nothing
1592e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                return;
1602e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            }
161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (entry.isCurrentProgram()) {
162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                Drawable background = getBackground();
1636ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                if (!mProgramGuide.isActive() || mProgramGuide.isRunningAnimation()) {
1646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    // If program guide is not active or is during showing/hiding,
1656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    // the animation is unnecessary, skip it.
1666ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    background.jumpToCurrentState();
1676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                int progress = getProgress(entry.entryStartUtcMillis, entry.entryEndUtcMillis);
169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                setProgress(background, R.id.reverse_progress, MAX_PROGRESS - progress);
170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (getHandler() != null) {
172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                getHandler().postAtTime(this,
173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        Utils.ceilTime(SystemClock.uptimeMillis(), FOCUS_UPDATE_FREQUENCY));
174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    };
177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public ProgramItemView(Context context) {
179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        this(context, null);
180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public ProgramItemView(Context context, AttributeSet attrs) {
183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        this(context, attrs, 0);
184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public ProgramItemView(Context context, AttributeSet attrs, int defStyle) {
187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        super(context, attrs, defStyle);
1882e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        setOnClickListener(ON_CLICKED);
1892e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        setOnFocusChangeListener(ON_FOCUS_CHANGED);
19065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mDvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void initIfNeeded() {
194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (sVisibleThreshold != 0) {
195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Resources res = getContext().getResources();
198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        sVisibleThreshold = res.getDimensionPixelOffset(
200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.dimen.program_guide_table_item_visible_threshold);
201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        sItemPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_item_padding);
20365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        sCompoundDrawablePadding = res.getDimensionPixelOffset(
20465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                R.dimen.program_guide_table_item_compound_drawable_padding);
20565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
20665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ColorStateList programTitleColor = ColorStateList.valueOf(res.getColor(
20765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                R.color.program_guide_table_item_program_title_text_color, null));
20865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ColorStateList grayedOutProgramTitleColor = res.getColorStateList(
20965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                R.color.program_guide_table_item_grayed_out_program_text_color, null);
21065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ColorStateList episodeTitleColor = ColorStateList.valueOf(res.getColor(
21165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                R.color.program_guide_table_item_program_episode_title_text_color, null));
21265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ColorStateList grayedOutEpisodeTitleColor = ColorStateList.valueOf(res.getColor(
21365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                R.color.program_guide_table_item_grayed_out_program_episode_title_text_color,
21465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                null));
215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int programTitleSize = res.getDimensionPixelSize(
216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.dimen.program_guide_table_item_program_title_font_size);
217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int episodeTitleSize = res.getDimensionPixelSize(
218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.dimen.program_guide_table_item_program_episode_title_font_size);
219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        sProgramTitleStyle = new TextAppearanceSpan(null, 0, programTitleSize, programTitleColor,
221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                null);
222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        sGrayedOutProgramTitleStyle = new TextAppearanceSpan(null, 0, programTitleSize,
223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                grayedOutProgramTitleColor, null);
224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        sEpisodeTitleStyle = new TextAppearanceSpan(null, 0, episodeTitleSize, episodeTitleColor,
225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                null);
226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        sGrayedOutEpisodeTitleStyle = new TextAppearanceSpan(null, 0, episodeTitleSize,
227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                grayedOutEpisodeTitleColor, null);
228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void onFinishInflate() {
232ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        super.onFinishInflate();
233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        initIfNeeded();
234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
236816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected int[] onCreateDrawableState(int extraSpace) {
238816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mTableEntry != null) {
239816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            int states[] = super.onCreateDrawableState(extraSpace
240816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    + STATE_CURRENT_PROGRAM.length + STATE_TOO_WIDE.length);
241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mTableEntry.isCurrentProgram()) {
242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mergeDrawableStates(states, STATE_CURRENT_PROGRAM);
243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mTableEntry.getWidth() > mMaxWidthForRipple) {
245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mergeDrawableStates(states, STATE_TOO_WIDE);
246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return states;
248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return super.onCreateDrawableState(extraSpace);
250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public TableEntry getTableEntry() {
253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mTableEntry;
254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
25665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    @SuppressLint("SwitchIntDef")
2576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public void setValues(ProgramGuide programGuide, TableEntry entry, int selectedGenreId,
2586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            long fromUtcMillis, long toUtcMillis, String gapTitle) {
2596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mProgramGuide = programGuide;
260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTableEntry = entry;
261816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        ViewGroup.LayoutParams layoutParams = getLayoutParams();
263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        layoutParams.width = entry.getWidth();
264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        setLayoutParams(layoutParams);
265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        String title = entry.program != null ? entry.program.getTitle() : null;
267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        String episode = entry.program != null ?
268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                entry.program.getEpisodeDisplayTitle(getContext()) : null;
269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        TextAppearanceSpan titleStyle = sGrayedOutProgramTitleStyle;
271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        TextAppearanceSpan episodeStyle = sGrayedOutEpisodeTitleStyle;
272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (entry.getWidth() < sVisibleThreshold) {
274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            setText(null);
275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (entry.isGap()) {
2772e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                title = gapTitle;
278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                episode = null;
2792e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            } else if (entry.hasGenre(selectedGenreId)) {
280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                titleStyle = sProgramTitleStyle;
281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                episodeStyle = sEpisodeTitleStyle;
282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
2832e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            if (TextUtils.isEmpty(title)) {
2842e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                title = getResources().getString(R.string.program_title_for_no_information);
2852e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            }
286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            SpannableStringBuilder description = new SpannableStringBuilder();
287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            description.append(title);
288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (!TextUtils.isEmpty(episode)) {
289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                description.append('\n');
290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // Add a 'zero-width joiner'/ZWJ in order to ensure we have the same line height for
292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // all lines. This is a non-printing character so it will not change the horizontal
293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // spacing however it will affect the line height. As we ensure the ZWJ has the same
294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // text style as the title it will make sure the line height is consistent.
295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                description.append('\u200D');
296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
297816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                int middle = description.length();
298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                description.append(episode);
299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                description.setSpan(titleStyle, 0, middle, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                description.setSpan(episodeStyle, middle, description.length(),
302816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } else {
304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                description.setSpan(titleStyle, 0, description.length(),
305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            setText(description);
30865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
30965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            // Sets recording icons if needed.
31065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            int iconResId = 0;
31165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (mTableEntry.scheduledRecording != null) {
31265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                if (mDvrManager.isConflicting(mTableEntry.scheduledRecording)) {
313d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    iconResId = R.drawable.ic_warning_white_18dp;
31465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                } else {
31565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    switch (mTableEntry.scheduledRecording.getState()) {
31665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
31765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            iconResId = R.drawable.ic_scheduled_recording;
31865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            break;
31965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
32065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            iconResId = R.drawable.ic_recording_program;
32165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            break;
32265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    }
32365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
32465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
32565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            setCompoundDrawablePadding(iconResId != 0 ? sCompoundDrawablePadding : 0);
32665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconResId, 0);
327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
32807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
32907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        mTextWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // Maximum width for us to use a ripple
3312e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        mMaxWidthForRipple = GuideUtils.convertMillisToPixel(fromUtcMillis, toUtcMillis);
332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
33565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Update programItemView to handle alignments of text.
33665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
33765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void updateVisibleArea() {
33865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        View parentView = ((View) getParent());
33965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (parentView == null) {
34065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
34165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
34265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
34365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            layoutVisibleArea(parentView.getLeft() - getLeft(), getRight() - parentView.getRight());
34465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        } else  {
34565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            layoutVisibleArea(getRight() - parentView.getRight(), parentView.getLeft() - getLeft());
34665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
34765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
34865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
34965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
350816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Layout title and episode according to visible area.
351816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
352816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Here's the spec.
353816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *   1. Don't show text if it's shorter than 48dp.
354816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *   2. Try showing whole text in visible area by placing and wrapping text,
355816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *      but do not wrap text less than 30min.
356816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *   3. Episode title is visible only if title isn't multi-line.
357816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
35865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param startOffset Offset of the start position from the enclosing view's start position.
35965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param endOffset Offset of the end position from the enclosing view's end position.
360816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
36165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     private void layoutVisibleArea(int startOffset, int endOffset) {
362816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int width = mTableEntry.getWidth();
36365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        int startPadding = Math.max(0, startOffset);
36465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        int endPadding = Math.max(0, endOffset);
36507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int minWidth = Math.min(width, mTextWidth + 2 * sItemPadding);
36607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        if (startPadding > 0 && width - startPadding < minWidth) {
36707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            startPadding = Math.max(0, width - minWidth);
368816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
36965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (endPadding > 0 && width - endPadding < minWidth) {
37065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            endPadding = Math.max(0, width - minWidth);
37165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
372816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
37365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (startPadding + sItemPadding != getPaddingStart()
37465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                || endPadding + sItemPadding != getPaddingEnd()) {
375816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mPreventParentRelayout = true; // The size of this view is kept, no need to tell parent.
37665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            setPaddingRelative(startPadding + sItemPadding, 0, endPadding + sItemPadding, 0);
377816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mPreventParentRelayout = false;
378816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
379816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
380816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
3812e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    public void clearValues() {
382816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (getHandler() != null) {
383816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            getHandler().removeCallbacks(mUpdateFocus);
384816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
385816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
386816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        setTag(null);
3876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mProgramGuide = null;
3882e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        mTableEntry = null;
389816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
390816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
391816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static int getProgress(long start, long end) {
392816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        long currentTime = System.currentTimeMillis();
393816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (currentTime <= start) {
394816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return 0;
395816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else if (currentTime >= end) {
396816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return MAX_PROGRESS;
397816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
398816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return (int) (((currentTime - start) * MAX_PROGRESS) / (end - start));
399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
401816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static void setProgress(Drawable drawable, int id, int progress) {
402816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (drawable instanceof StateListDrawable) {
403816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            StateListDrawable stateDrawable = (StateListDrawable) drawable;
404816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            for (int i = 0; i < getStateCount(stateDrawable); ++i) {
405816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                setProgress(getStateDrawable(stateDrawable, i), id, progress);
406816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
407816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else if (drawable instanceof LayerDrawable) {
408816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            LayerDrawable layerDrawable = (LayerDrawable) drawable;
409816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            for (int i = 0; i < layerDrawable.getNumberOfLayers(); ++i) {
410816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                setProgress(layerDrawable.getDrawable(i), id, progress);
411816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (layerDrawable.getId(i) == id) {
412816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    layerDrawable.getDrawable(i).setLevel(progress);
413816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
414816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
415816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
416816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
417816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
418816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static int getStateCount(StateListDrawable stateListDrawable) {
419816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        try {
420816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Object stateCount = StateListDrawable.class.getDeclaredMethod("getStateCount")
421816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    .invoke(stateListDrawable);
422816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return (int) stateCount;
423816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } catch (NoSuchMethodException|IllegalAccessException|IllegalArgumentException
424816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                |InvocationTargetException e) {
425816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.e(TAG, "Failed to call StateListDrawable.getStateCount()", e);
426816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return 0;
427816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
428816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
429816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
430816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static Drawable getStateDrawable(StateListDrawable stateListDrawable, int index) {
431816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        try {
432816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Object drawable = StateListDrawable.class
433816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    .getDeclaredMethod("getStateDrawable", Integer.TYPE)
434816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    .invoke(stateListDrawable, index);
435816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return (Drawable) drawable;
436816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } catch (NoSuchMethodException|IllegalAccessException|IllegalArgumentException
437816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                |InvocationTargetException e) {
438816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.e(TAG, "Failed to call StateListDrawable.getStateDrawable(" + index + ")", e);
439816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return null;
440816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
441816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
442816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
443816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
444816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void requestLayout() {
445816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mPreventParentRelayout) {
446816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // Trivial layout, no need to tell parent.
447816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            forceLayout();
448816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
449816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            super.requestLayout();
450816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
451816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
452816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko}
453