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.ui;
18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.Animator;
20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.AnimatorListenerAdapter;
21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.ValueAnimator;
22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Context;
23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.res.Resources;
247d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.support.annotation.Nullable;
25816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.AttributeSet;
26816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log;
27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.KeyEvent;
28816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.LayoutInflater;
29816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.View;
30816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.ViewGroup;
31816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.animation.AnimationUtils;
32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.animation.Interpolator;
33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.widget.AdapterView;
34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.widget.BaseAdapter;
35816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.widget.LinearLayout;
36816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.widget.ListView;
37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.widget.TextView;
38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
39816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.MainActivity;
40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.R;
41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.TvApplication;
426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.util.DurationTimer;
43816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.analytics.Tracker;
442e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.android.tv.common.SoftPreconditions;
45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.Channel;
46816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ChannelNumber;
47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
48816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.ArrayList;
49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.List;
50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class KeypadChannelSwitchView extends LinearLayout implements
52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        TvTransitionManager.TransitionLayout {
53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String TAG = "KeypadChannelSwitchView";
54816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int MAX_CHANNEL_NUMBER_DIGIT = 4;
56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int MAX_MINOR_CHANNEL_NUMBER_DIGIT = 3;
57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int MAX_CHANNEL_ITEM = 8;
58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String CHANNEL_DELIMITERS_REGEX = "[-\\.\\s]";
59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static final String SCREEN_NAME = "Channel switch";
60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final MainActivity mMainActivity;
62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Tracker mTracker;
63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final DurationTimer mViewDurationTimer = new DurationTimer();
64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mNavigated = false;
657d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    @Nullable  //Once mChannels is set to null it should not be used again.
66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private List<Channel> mChannels;
67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private TextView mChannelNumberView;
68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private ListView mChannelItemListView;
69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ChannelNumber mTypedChannelNumber = new ChannelNumber();
70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ArrayList<Channel> mChannelCandidates = new ArrayList<>();
71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected final ChannelItemAdapter mAdapter = new ChannelItemAdapter();
72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final LayoutInflater mLayoutInflater;
73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Channel mSelectedChannel;
74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Runnable mHideRunnable = new Runnable() {
76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public void run() {
78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mCurrentHeight = 0;
79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mSelectedChannel != null) {
80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mMainActivity.tuneToChannel(mSelectedChannel);
81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mTracker.sendChannelNumberItemChosenByTimeout();
82816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } else {
8307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                mMainActivity.getOverlayManager().hideOverlays(
8407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
8507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
8607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                        | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
87ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
88ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    };
92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final long mShowDurationMillis;
93816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final long mRippleAnimDurationMillis;
94816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mBaseViewHeight;
95816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mItemHeight;
96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mResizeAnimDuration;
97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Animator mResizeAnimator;
98816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Interpolator mResizeInterpolator;
99816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // NOTE: getHeight() will be updated after layout() is called. mCurrentHeight is needed for
100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // getting the latest updated value of the view height before layout().
101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mCurrentHeight;
102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public KeypadChannelSwitchView(Context context) {
104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        this(context, null, 0);
105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public KeypadChannelSwitchView(Context context, AttributeSet attrs) {
108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        this(context, attrs, 0);
109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public KeypadChannelSwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        super(context, attrs, defStyleAttr);
113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mMainActivity = (MainActivity) context;
1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mTracker = TvApplication.getSingletons(context).getTracker();
116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Resources resources = getResources();
117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mLayoutInflater = LayoutInflater.from(context);
118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mShowDurationMillis = resources.getInteger(R.integer.keypad_channel_switch_show_duration);
119816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mRippleAnimDurationMillis = resources.getInteger(
120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.integer.keypad_channel_switch_ripple_anim_duration);
121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBaseViewHeight = resources.getDimensionPixelSize(
122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                R.dimen.keypad_channel_switch_base_height);
123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mItemHeight = resources.getDimensionPixelSize(R.dimen.keypad_channel_switch_item_height);
124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mResizeAnimDuration = resources.getInteger(R.integer.keypad_channel_switch_anim_duration);
125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mResizeInterpolator = AnimationUtils.loadInterpolator(context,
126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                android.R.interpolator.linear_out_slow_in);
127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void onFinishInflate(){
131ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        super.onFinishInflate();
132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelNumberView = (TextView) findViewById(R.id.channel_number);
133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelItemListView = (ListView) findViewById(R.id.channel_list);
134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelItemListView.setAdapter(mAdapter);
135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelItemListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (position >= mAdapter.getCount()) {
139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    // It can happen during closing.
140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    return;
141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mChannelItemListView.setFocusable(false);
143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                final Channel channel = ((Channel) mAdapter.getItem(position));
144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                postDelayed(new Runnable() {
145816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    @Override
146816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    public void run() {
147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mChannelItemListView.setFocusable(true);
148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mMainActivity.tuneToChannel(channel);
149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mTracker.sendChannelNumberItemClicked();
150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }, mRippleAnimDurationMillis);
152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelItemListView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (position >= mAdapter.getCount()) {
158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    // It can happen during closing.
159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mSelectedChannel = null;
160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                } else {
161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mSelectedChannel = (Channel) mAdapter.getItem(position);
162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
1637d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                if (position != 0 && !mNavigated) {
164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mNavigated = true;
165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mTracker.sendChannelInputNavigated();
166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onNothingSelected(AdapterView<?> parent) {
171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mSelectedChannel = null;
172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean dispatchKeyEvent(KeyEvent event) {
178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        scheduleHide();
179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return super.dispatchKeyEvent(event);
180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean onKeyUp(int keyCode, KeyEvent event) {
1847d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        SoftPreconditions.checkNotNull(mChannels, TAG, "mChannels");
185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (isChannelNumberKey(keyCode)) {
186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            onNumberKeyUp(keyCode - KeyEvent.KEYCODE_0);
187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return true;
188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (ChannelNumber.isChannelNumberDelimiterKey(keyCode)) {
190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            onDelimiterKeyUp();
191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return true;
192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return super.onKeyUp(keyCode, event);
194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onEnterAction(boolean fromEmptyScene) {
198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        reset();
199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (fromEmptyScene) {
200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            ViewUtils.setTransitionAlpha(mChannelItemListView, 1f);
201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mNavigated = false;
203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mViewDurationTimer.start();
204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendShowChannelSwitch();
205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendScreenView(SCREEN_NAME);
206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        updateView();
207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        scheduleHide();
208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onExitAction() {
212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mCurrentHeight = 0;
213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendHideChannelSwitch(mViewDurationTimer.reset());
214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        cancelHide();
215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void scheduleHide() {
218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        cancelHide();
219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        postDelayed(mHideRunnable, mShowDurationMillis);
220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void cancelHide() {
223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        removeCallbacks(mHideRunnable);
224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void reset() {
227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTypedChannelNumber.reset();
228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSelectedChannel = null;
229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelCandidates.clear();
230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mAdapter.notifyDataSetChanged();
231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
232816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
2337d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    public void setChannels(@Nullable List<Channel> channels) {
234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannels = channels;
235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
236816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static boolean isChannelNumberKey(int keyCode) {
238816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9;
239816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
240816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onNumberKeyUp(int num) {
242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // Reset typed channel number in some cases.
243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mTypedChannelNumber.majorNumber == null) {
244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTypedChannelNumber.reset();
245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else if (!mTypedChannelNumber.hasDelimiter
246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                && mTypedChannelNumber.majorNumber.length() >= MAX_CHANNEL_NUMBER_DIGIT) {
247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTypedChannelNumber.reset();
248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else if (mTypedChannelNumber.hasDelimiter
249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                && mTypedChannelNumber.minorNumber != null
250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                && mTypedChannelNumber.minorNumber.length() >= MAX_MINOR_CHANNEL_NUMBER_DIGIT) {
251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTypedChannelNumber.reset();
252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!mTypedChannelNumber.hasDelimiter) {
255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTypedChannelNumber.majorNumber += String.valueOf(num);
256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTypedChannelNumber.minorNumber += String.valueOf(num);
258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
259816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendChannelNumberInput();
260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        updateView();
261816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void onDelimiterKeyUp() {
264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mTypedChannelNumber.hasDelimiter || mTypedChannelNumber.majorNumber.length() == 0) {
265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTypedChannelNumber.hasDelimiter = true;
268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendChannelNumberInput();
269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        updateView();
270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void updateView() {
273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelNumberView.setText(mTypedChannelNumber.toString() + "_");
274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelCandidates.clear();
275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        ArrayList<Channel> secondaryChannelCandidates = new ArrayList<>();
276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (Channel channel : mChannels) {
277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            ChannelNumber chNumber = ChannelNumber.parseChannelNumber(channel.getDisplayNumber());
278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (chNumber == null) {
279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                Log.i(TAG, "Malformed channel number (name=" + channel.getDisplayName()
280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        + ", number=" + channel.getDisplayNumber() + ")");
281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                continue;
282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (matchChannelNumber(mTypedChannelNumber, chNumber)) {
284816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mChannelCandidates.add(channel);
285816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } else if (!mTypedChannelNumber.hasDelimiter) {
286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // Even if a user doesn't type '-', we need to match the typed number to not only
287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // the major number but also the minor number. For example, when a user types '111'
288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // without delimiter, it should be matched to '111', '1-11' and '11-1'.
289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (channel.getDisplayNumber().replaceAll(CHANNEL_DELIMITERS_REGEX, "")
290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        .startsWith(mTypedChannelNumber.majorNumber)) {
291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    secondaryChannelCandidates.add(channel);
292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelCandidates.addAll(secondaryChannelCandidates);
296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mAdapter.notifyDataSetChanged();
297816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mAdapter.getCount() > 0) {
298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mChannelItemListView.requestFocus();
299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mChannelItemListView.setSelection(0);
300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mSelectedChannel = mChannelCandidates.get(0);
301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
302816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        updateViewHeight();
304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void updateViewHeight() {
307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int itemListHeight = mItemHeight * Math.min(MAX_CHANNEL_ITEM, mAdapter.getCount());
308816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int targetHeight = mBaseViewHeight + itemListHeight;
309816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mResizeAnimator != null) {
310816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mResizeAnimator.cancel();
311816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mResizeAnimator = null;
312816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
313816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
314816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mCurrentHeight == 0) {
315816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // Do not add the resize animation when the banner has not been shown before.
316816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mCurrentHeight = targetHeight;
317816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            setViewHeight(this, targetHeight);
318816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else if (mCurrentHeight != targetHeight){
319816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mResizeAnimator = createResizeAnimator(targetHeight);
320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mResizeAnimator.start();
321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
324816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Animator createResizeAnimator(int targetHeight) {
325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        ValueAnimator animator = ValueAnimator.ofInt(mCurrentHeight, targetHeight);
326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationUpdate(ValueAnimator animation) {
329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                int value = (Integer) animation.getAnimatedValue();
330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                setViewHeight(KeypadChannelSwitchView.this, value);
331816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mCurrentHeight = value;
332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        animator.setDuration(mResizeAnimDuration);
335816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        animator.addListener(new AnimatorListenerAdapter() {
336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
337816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationEnd(Animator animator) {
338816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mResizeAnimator = null;
339816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        animator.setInterpolator(mResizeInterpolator);
342816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return animator;
343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void setViewHeight(View view, int height) {
346816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
347816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (height != layoutParams.height) {
348816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            layoutParams.height = height;
349816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            view.setLayoutParams(layoutParams);
350816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
351816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
352816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
353816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static boolean matchChannelNumber(ChannelNumber typedChNumber, ChannelNumber chNumber) {
354816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!chNumber.majorNumber.equals(typedChNumber.majorNumber)) {
355816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return false;
356816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
357816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (typedChNumber.hasDelimiter) {
358816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (!chNumber.hasDelimiter) {
359816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return false;
360816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
361816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (!chNumber.minorNumber.startsWith(typedChNumber.minorNumber)) {
362816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return false;
363816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
364816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
365816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return true;
366816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
367816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
368816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    class ChannelItemAdapter extends BaseAdapter {
369816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
370816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public int getCount() {
371816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return mChannelCandidates.size();
372816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
373816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
374816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
375816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public Object getItem(int position) {
376816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return mChannelCandidates.get(position);
377816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
378816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
379816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
380816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public long getItemId(int position) {
381816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return position;
382816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
383816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
384816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
385816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public View getView(int position, View convertView, ViewGroup parent) {
386816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            final Channel channel = mChannelCandidates.get(position);
387816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            View v = convertView;
388816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (v == null) {
389816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                v = mLayoutInflater.inflate(R.layout.keypad_channel_switch_item, parent, false);
390816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
391816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
392816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            TextView channelNumberView = (TextView) v.findViewById(R.id.number);
393816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            channelNumberView.setText(channel.getDisplayNumber());
394816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
395816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            TextView channelNameView = (TextView) v.findViewById(R.id.name);
396816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            channelNameView.setText(channel.getDisplayName());
397816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return v;
398816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko}
401