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
17ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenpackage com.android.tv.ui.sidepanel;
18ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
19ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport android.app.Fragment;
20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Context;
21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.graphics.drawable.RippleDrawable;
22ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport android.os.Bundle;
23ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport android.support.v17.leanback.widget.VerticalGridView;
24ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport android.support.v7.widget.RecyclerView;
25816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.KeyEvent;
26ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport android.view.LayoutInflater;
27ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport android.view.View;
28ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport android.view.ViewGroup;
296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.widget.FrameLayout;
30ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport android.widget.TextView;
31ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.MainActivity;
33ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport com.android.tv.R;
34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.TvApplication;
356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.util.DurationTimer;
36816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.analytics.HasTrackerLabel;
37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.analytics.Tracker;
38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ChannelDataManager;
39816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ProgramDataManager;
40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.SystemProperties;
416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.util.ViewCache;
42ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
43ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkerstenimport java.util.List;
44ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkopublic abstract class SideFragment<T extends Item> extends Fragment implements HasTrackerLabel {
46816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static final int INVALID_POSITION = -1;
47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
486ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static final int PRELOAD_VIEW_SIZE = 7;
496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static final int[] PRELOAD_VIEW_IDS = {
50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        R.layout.option_item_radio_button,
51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        R.layout.option_item_channel_lock,
52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        R.layout.option_item_check_box,
536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        R.layout.option_item_channel_check,
546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        R.layout.option_item_action
55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    };
56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static RecyclerView.RecycledViewPool sRecycledViewPool =
586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            new RecyclerView.RecycledViewPool();
59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private VerticalGridView mListView;
61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private ItemAdapter mAdapter;
62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private SideFragmentListener mListener;
63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private ChannelDataManager mChannelDataManager;
64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private ProgramDataManager mProgramDataManager;
65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Tracker mTracker;
66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final DurationTimer mSidePanelDurationTimer = new DurationTimer();
67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mHideKey;
69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final int mDebugHideKey;
70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public SideFragment() {
72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        this(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.KEYCODE_UNKNOWN);
73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param hideKey the KeyCode used to hide the fragment
77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param debugHideKey the KeyCode used to hide the fragment if
78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *            {@link SystemProperties#USE_DEBUG_KEYS}.
79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public SideFragment(int hideKey, int debugHideKey) {
81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHideKey = hideKey;
82816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDebugHideKey = debugHideKey;
83816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
8665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void onAttach(Context context) {
8765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        super.onAttach(context);
88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelDataManager = getMainActivity().getChannelDataManager();
89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramDataManager = getMainActivity().getProgramDataManager();
9065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mTracker = TvApplication.getSingletons(context).getTracker();
91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
92ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
93ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    @Override
94ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    public View onCreateView(LayoutInflater inflater, ViewGroup container,
95ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            Bundle savedInstanceState) {
966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        View view = ViewCache.getInstance().getOrCreateView(
976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                inflater, getFragmentLayoutResourceId(), container);
98ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
99ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        TextView textView = (TextView) view.findViewById(R.id.side_panel_title);
100ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        textView.setText(getTitle());
101ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListView = (VerticalGridView) view.findViewById(R.id.side_panel_list);
103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListView.setRecycledViewPool(sRecycledViewPool);
104ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mAdapter = new ItemAdapter(inflater, getItemList());
106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListView.setAdapter(mAdapter);
107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListView.requestFocus();
108ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
109ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        return view;
110ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    }
111ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
112ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    @Override
113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onResume() {
114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        super.onResume();
115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendShowSidePanel(this);
116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendScreenView(this.getTrackerLabel());
117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSidePanelDurationTimer.start();
118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
119816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onPause() {
122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker.sendHideSidePanel(this, mSidePanelDurationTimer.reset());
123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        super.onPause();
124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
127ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    public void onDetach() {
128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTracker = null;
129ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        super.onDetach();
130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public final boolean isHideKeyForThisPanel(int keyCode) {
133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        boolean debugKeysEnabled = SystemProperties.USE_DEBUG_KEYS.getValue();
134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mHideKey != KeyEvent.KEYCODE_UNKNOWN &&
135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                (mHideKey == keyCode || (debugKeysEnabled && mDebugHideKey == keyCode));
136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
137ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onDestroyView() {
140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        super.onDestroyView();
141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListView.swapAdapter(null, true);
142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mListener != null) {
143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mListener.onSideFragmentViewDestroyed();
144ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
145ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    }
146ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public final void setListener(SideFragmentListener listener) {
148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListener = listener;
149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void setSelectedPosition(int position) {
152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListView.setSelectedPosition(position);
153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected int getSelectedPosition() {
156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mListView.getSelectedPosition();
157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public void setItems(List<T> items) {
160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mAdapter.reset(items);
161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void closeFragment() {
164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        getMainActivity().getOverlayManager().getSideFragmentManager().popSideFragment();
165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected MainActivity getMainActivity() {
168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return (MainActivity) getActivity();
169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected ChannelDataManager getChannelDataManager() {
172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mChannelDataManager;
173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected ProgramDataManager getProgramDataManager() {
176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mProgramDataManager;
177ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    }
178ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
179ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    protected void notifyDataSetChanged() {
180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mAdapter.notifyDataSetChanged();
181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /*
184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * HACK: The following methods bypass the updating mechanism of RecyclerView.Adapter and
185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * directly updates each item. This works around a bug in the base libraries where calling
186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Adapter.notifyItemsChanged() causes the VerticalGridView to lose track of displayed item
187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * position.
188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void notifyItemChanged(int position) {
191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        notifyItemChanged(mAdapter.getItem(position));
192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void notifyItemChanged(Item item) {
195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        item.notifyUpdated();
196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Notifies all items of ItemAdapter has changed without structural changes.
200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void notifyItemsChanged() {
202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        notifyItemsChanged(0, mAdapter.getItemCount());
203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Notifies some items of ItemAdapter has changed starting from position
207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * <code>positionStart</code> to the end without structural changes.
208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void notifyItemsChanged(int positionStart) {
210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        notifyItemsChanged(positionStart, mAdapter.getItemCount() - positionStart);
211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void notifyItemsChanged(int positionStart, int itemCount) {
214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        while (itemCount-- != 0) {
215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            notifyItemChanged(positionStart++);
216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /*
220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * END HACK
221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected int getFragmentLayoutResourceId() {
224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return R.layout.option_fragment;
225ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    }
226ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
227ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    protected abstract String getTitle();
228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public abstract String getTrackerLabel();
2306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    protected abstract List<T> getItemList();
231ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
232816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public interface SideFragmentListener {
233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onSideFragmentViewDestroyed();
234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
236d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    /**
2376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Preloads the item views.
238d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     */
2396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public static void preloadItemViews(Context context) {
2406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        ViewCache.getInstance().putView(
2416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                context, R.layout.option_fragment, new FrameLayout(context), 1);
2426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        VerticalGridView fakeParent = new VerticalGridView(context);
2436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        for (int id : PRELOAD_VIEW_IDS) {
2446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            sRecycledViewPool.setMaxRecycledViews(id, PRELOAD_VIEW_SIZE);
2456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            ViewCache.getInstance().putView(context, id, fakeParent, PRELOAD_VIEW_SIZE);
246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
249d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    /**
2506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Releases the recycled view pool.
251d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     */
2526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public static void releaseRecycledViewPool() {
2536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        sRecycledViewPool.clear();
254d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
255d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
2566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static class ItemAdapter<T extends Item> extends RecyclerView.Adapter<ViewHolder> {
257ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        private final LayoutInflater mLayoutInflater;
2586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private List<T> mItems;
259ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
2606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private ItemAdapter(LayoutInflater layoutInflater, List<T> items) {
261ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            mLayoutInflater = layoutInflater;
262ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            mItems = items;
263ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
264ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
2656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private void reset(List<T> items) {
266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mItems = items;
267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            notifyDataSetChanged();
268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
270ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        @Override
271ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
2726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            View view = ViewCache.getInstance().getOrCreateView(mLayoutInflater, viewType, parent);
2736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return new ViewHolder(view);
274ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
275ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
276ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        @Override
277ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        public void onBindViewHolder(ViewHolder holder, int position) {
278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            holder.onBind(this, getItem(position));
279ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
280ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
281ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        @Override
282ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        public void onViewRecycled(ViewHolder holder) {
283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            holder.onUnbind();
284ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
285ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
286ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        @Override
287ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        public int getItemViewType(int position) {
288ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            return getItem(position).getResourceId();
289ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
290ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
291ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        @Override
292ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        public int getItemCount() {
293ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            return mItems == null ? 0 : mItems.size();
294ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
295ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
2966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private T getItem(int position) {
297ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            return mItems.get(position);
298ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
299ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
3006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private void clearRadioGroup(T item) {
301ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            int position = mItems.indexOf(item);
302ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            for (int i = position - 1; i >= 0; --i) {
303ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                if ((item = mItems.get(i)) instanceof RadioButtonItem) {
304ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                    ((RadioButtonItem) item).setChecked(false);
305ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                } else {
306ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                    break;
307ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                }
308ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            }
309ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            for (int i = position + 1; i < mItems.size(); ++i) {
310ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                if ((item = mItems.get(i)) instanceof RadioButtonItem) {
311ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                    ((RadioButtonItem) item).setChecked(false);
312ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                } else {
313ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                    break;
314ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten                }
315ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            }
316ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
3176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
318ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
3196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static class ViewHolder extends RecyclerView.ViewHolder
3206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            implements View.OnClickListener, View.OnFocusChangeListener {
3216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private ItemAdapter mAdapter;
3226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        public Item mItem;
323ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten
3246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private ViewHolder(View view) {
3256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            super(view);
3266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            itemView.setOnClickListener(this);
3276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            itemView.setOnFocusChangeListener(this);
3286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
3306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        public void onBind(ItemAdapter adapter, Item item) {
3316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mAdapter = adapter;
3326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mItem = item;
3336ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mItem.onBind(itemView);
3346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mItem.onUpdate();
3356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
3376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        public void onUnbind() {
3386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mItem.onUnbind();
3396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mItem = null;
3406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mAdapter = null;
3416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
342816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
3436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        @Override
3446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        public void onClick(View view) {
3456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (mItem instanceof RadioButtonItem) {
3466ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mAdapter.clearRadioGroup(mItem);
3476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
3486ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (view.getBackground() instanceof RippleDrawable) {
3496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                view.postDelayed(
3506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        new Runnable() {
3516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                            @Override
3526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                            public void run() {
3536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                if (mItem != null) {
3546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                    mItem.onSelected();
3556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                }
356816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                            }
3576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        },
3586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        view.getResources().getInteger(R.integer.side_panel_ripple_anim_duration));
3596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            } else {
3606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mItem.onSelected();
361816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
3626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
363816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
3646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        @Override
3656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        public void onFocusChange(View view, boolean focusGained) {
3666ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (focusGained) {
3676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mItem.onFocused();
368ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten            }
369ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten        }
370ad8bb145f00812459b96e54fdaad36f3a7a3612cChristofer Åkersten    }
371816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko}
372