1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package android.support.v17.leanback.widget;
15 
16 import android.support.v7.widget.RecyclerView;
17 import android.util.Log;
18 import android.view.View;
19 import android.view.ViewGroup;
20 
21 import java.util.ArrayList;
22 
23 /**
24  * Bridge from {@link Presenter} to {@link RecyclerView.Adapter}. Public to allow use by third
25  * party Presenters.
26  */
27 public class ItemBridgeAdapter extends RecyclerView.Adapter implements FacetProviderAdapter {
28     static final String TAG = "ItemBridgeAdapter";
29     static final boolean DEBUG = false;
30 
31     /**
32      * Interface for listening to ViewHolder operations.
33      */
34     public static class AdapterListener {
onAddPresenter(Presenter presenter, int type)35         public void onAddPresenter(Presenter presenter, int type) {
36         }
onCreate(ViewHolder viewHolder)37         public void onCreate(ViewHolder viewHolder) {
38         }
onBind(ViewHolder viewHolder)39         public void onBind(ViewHolder viewHolder) {
40         }
onUnbind(ViewHolder viewHolder)41         public void onUnbind(ViewHolder viewHolder) {
42         }
onAttachedToWindow(ViewHolder viewHolder)43         public void onAttachedToWindow(ViewHolder viewHolder) {
44         }
onDetachedFromWindow(ViewHolder viewHolder)45         public void onDetachedFromWindow(ViewHolder viewHolder) {
46         }
47     }
48 
49     /**
50      * Interface for wrapping a view created by a Presenter into another view.
51      * The wrapper must be the immediate parent of the wrapped view.
52      */
53     public static abstract class Wrapper {
createWrapper(View root)54         public abstract View createWrapper(View root);
wrap(View wrapper, View wrapped)55         public abstract void wrap(View wrapper, View wrapped);
56     }
57 
58     private ObjectAdapter mAdapter;
59     Wrapper mWrapper;
60     private PresenterSelector mPresenterSelector;
61     FocusHighlightHandler mFocusHighlight;
62     private AdapterListener mAdapterListener;
63     private ArrayList<Presenter> mPresenters = new ArrayList<Presenter>();
64 
65     final class OnFocusChangeListener implements View.OnFocusChangeListener {
66         View.OnFocusChangeListener mChainedListener;
67 
68         @Override
onFocusChange(View view, boolean hasFocus)69         public void onFocusChange(View view, boolean hasFocus) {
70             if (DEBUG) Log.v(TAG, "onFocusChange " + hasFocus + " " + view
71                     + " mFocusHighlight" + mFocusHighlight);
72             if (mWrapper != null) {
73                 view = (View) view.getParent();
74             }
75             if (mFocusHighlight != null) {
76                 mFocusHighlight.onItemFocused(view, hasFocus);
77             }
78             if (mChainedListener != null) {
79                 mChainedListener.onFocusChange(view, hasFocus);
80             }
81         }
82     }
83 
84     /**
85      * ViewHolder for the ItemBridgeAdapter.
86      */
87     public class ViewHolder extends RecyclerView.ViewHolder implements FacetProvider {
88         final Presenter mPresenter;
89         final Presenter.ViewHolder mHolder;
90         final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener();
91         Object mItem;
92         Object mExtraObject;
93 
94         /**
95          * Get {@link Presenter}.
96          */
getPresenter()97         public final Presenter getPresenter() {
98             return mPresenter;
99         }
100 
101         /**
102          * Get {@link Presenter.ViewHolder}.
103          */
getViewHolder()104         public final Presenter.ViewHolder getViewHolder() {
105             return mHolder;
106         }
107 
108         /**
109          * Get currently bound object.
110          */
getItem()111         public final Object getItem() {
112             return mItem;
113         }
114 
115         /**
116          * Get extra object associated with the view.  Developer can attach
117          * any customized UI object in addition to {@link Presenter.ViewHolder}.
118          * A typical use case is attaching an animator object.
119          */
getExtraObject()120         public final Object getExtraObject() {
121             return mExtraObject;
122         }
123 
124         /**
125          * Set extra object associated with the view.  Developer can attach
126          * any customized UI object in addition to {@link Presenter.ViewHolder}.
127          * A typical use case is attaching an animator object.
128          */
setExtraObject(Object object)129         public void setExtraObject(Object object) {
130             mExtraObject = object;
131         }
132 
133         @Override
getFacet(Class<?> facetClass)134         public Object getFacet(Class<?> facetClass) {
135             return mHolder.getFacet(facetClass);
136         }
137 
ViewHolder(Presenter presenter, View view, Presenter.ViewHolder holder)138         ViewHolder(Presenter presenter, View view, Presenter.ViewHolder holder) {
139             super(view);
140             mPresenter = presenter;
141             mHolder = holder;
142         }
143     }
144 
145     private ObjectAdapter.DataObserver mDataObserver = new ObjectAdapter.DataObserver() {
146         @Override
147         public void onChanged() {
148             ItemBridgeAdapter.this.notifyDataSetChanged();
149         }
150         @Override
151         public void onItemRangeChanged(int positionStart, int itemCount) {
152             ItemBridgeAdapter.this.notifyItemRangeChanged(positionStart, itemCount);
153         }
154         @Override
155         public void onItemRangeInserted(int positionStart, int itemCount) {
156             ItemBridgeAdapter.this.notifyItemRangeInserted(positionStart, itemCount);
157         }
158         @Override
159         public void onItemRangeRemoved(int positionStart, int itemCount) {
160             ItemBridgeAdapter.this.notifyItemRangeRemoved(positionStart, itemCount);
161         }
162     };
163 
ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector)164     public ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector) {
165         setAdapter(adapter);
166         mPresenterSelector = presenterSelector;
167     }
168 
ItemBridgeAdapter(ObjectAdapter adapter)169     public ItemBridgeAdapter(ObjectAdapter adapter) {
170         this(adapter, null);
171     }
172 
ItemBridgeAdapter()173     public ItemBridgeAdapter() {
174     }
175 
176     /**
177      * Sets the {@link ObjectAdapter}.
178      */
setAdapter(ObjectAdapter adapter)179     public void setAdapter(ObjectAdapter adapter) {
180         if (adapter == mAdapter) {
181             return;
182         }
183         if (mAdapter != null) {
184             mAdapter.unregisterObserver(mDataObserver);
185         }
186         mAdapter = adapter;
187         if (mAdapter == null) {
188             notifyDataSetChanged();
189             return;
190         }
191 
192         mAdapter.registerObserver(mDataObserver);
193         if (hasStableIds() != mAdapter.hasStableIds()) {
194             setHasStableIds(mAdapter.hasStableIds());
195         }
196         notifyDataSetChanged();
197     }
198 
199     /**
200      * Changes Presenter that creates and binds the view.
201      * @param presenterSelector Presenter that creates and binds the view.
202      */
setPresenter(PresenterSelector presenterSelector)203     public void setPresenter(PresenterSelector presenterSelector) {
204         mPresenterSelector = presenterSelector;
205         notifyDataSetChanged();
206     }
207 
208     /**
209      * Sets the {@link Wrapper}.
210      */
setWrapper(Wrapper wrapper)211     public void setWrapper(Wrapper wrapper) {
212         mWrapper = wrapper;
213     }
214 
215     /**
216      * Returns the {@link Wrapper}.
217      */
getWrapper()218     public Wrapper getWrapper() {
219         return mWrapper;
220     }
221 
setFocusHighlight(FocusHighlightHandler listener)222     void setFocusHighlight(FocusHighlightHandler listener) {
223         mFocusHighlight = listener;
224         if (DEBUG) Log.v(TAG, "setFocusHighlight " + mFocusHighlight);
225     }
226 
227     /**
228      * Clears the adapter.
229      */
clear()230     public void clear() {
231         setAdapter(null);
232     }
233 
234     /**
235      * Sets the presenter mapper array.
236      */
setPresenterMapper(ArrayList<Presenter> presenters)237     public void setPresenterMapper(ArrayList<Presenter> presenters) {
238         mPresenters = presenters;
239     }
240 
241     /**
242      * Returns the presenter mapper array.
243      */
getPresenterMapper()244     public ArrayList<Presenter> getPresenterMapper() {
245         return mPresenters;
246     }
247 
248     @Override
getItemCount()249     public int getItemCount() {
250         return mAdapter != null ? mAdapter.size() : 0;
251     }
252 
253     @Override
getItemViewType(int position)254     public int getItemViewType(int position) {
255         PresenterSelector presenterSelector = mPresenterSelector != null
256                 ? mPresenterSelector : mAdapter.getPresenterSelector();
257         Object item = mAdapter.get(position);
258         Presenter presenter = presenterSelector.getPresenter(item);
259         int type = mPresenters.indexOf(presenter);
260         if (type < 0) {
261             mPresenters.add(presenter);
262             type = mPresenters.indexOf(presenter);
263             if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
264             onAddPresenter(presenter, type);
265             if (mAdapterListener != null) {
266                 mAdapterListener.onAddPresenter(presenter, type);
267             }
268         }
269         return type;
270     }
271 
272     /**
273      * Called when presenter is added to Adapter.
274      */
onAddPresenter(Presenter presenter, int type)275     protected void onAddPresenter(Presenter presenter, int type) {
276     }
277 
278     /**
279      * Called when ViewHolder is created.
280      */
onCreate(ViewHolder viewHolder)281     protected void onCreate(ViewHolder viewHolder) {
282     }
283 
284     /**
285      * Called when ViewHolder has been bound to data.
286      */
onBind(ViewHolder viewHolder)287     protected void onBind(ViewHolder viewHolder) {
288     }
289 
290     /**
291      * Called when ViewHolder has been unbound from data.
292      */
onUnbind(ViewHolder viewHolder)293     protected void onUnbind(ViewHolder viewHolder) {
294     }
295 
296     /**
297      * Called when ViewHolder has been attached to window.
298      */
onAttachedToWindow(ViewHolder viewHolder)299     protected void onAttachedToWindow(ViewHolder viewHolder) {
300     }
301 
302     /**
303      * Called when ViewHolder has been detached from window.
304      */
onDetachedFromWindow(ViewHolder viewHolder)305     protected void onDetachedFromWindow(ViewHolder viewHolder) {
306     }
307 
308     /**
309      * {@link View.OnFocusChangeListener} that assigned in
310      * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
311      * {@link View.OnFocusChangeListener} after that.
312      */
313     @Override
onCreateViewHolder(ViewGroup parent, int viewType)314     public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
315         if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
316         Presenter presenter = mPresenters.get(viewType);
317         Presenter.ViewHolder presenterVh;
318         View view;
319         if (mWrapper != null) {
320             view = mWrapper.createWrapper(parent);
321             presenterVh = presenter.onCreateViewHolder(parent);
322             mWrapper.wrap(view, presenterVh.view);
323         } else {
324             presenterVh = presenter.onCreateViewHolder(parent);
325             view = presenterVh.view;
326         }
327         ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
328         onCreate(viewHolder);
329         if (mAdapterListener != null) {
330             mAdapterListener.onCreate(viewHolder);
331         }
332         View presenterView = viewHolder.mHolder.view;
333         if (presenterView != null) {
334             viewHolder.mFocusChangeListener.mChainedListener = presenterView.getOnFocusChangeListener();
335             presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
336         }
337         if (mFocusHighlight != null) {
338             mFocusHighlight.onInitializeView(view);
339         }
340         return viewHolder;
341     }
342 
343     /**
344      * Sets the AdapterListener.
345      */
setAdapterListener(AdapterListener listener)346     public void setAdapterListener(AdapterListener listener) {
347         mAdapterListener = listener;
348     }
349 
350     @Override
onBindViewHolder(RecyclerView.ViewHolder holder, int position)351     public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
352         if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
353         ViewHolder viewHolder = (ViewHolder) holder;
354         viewHolder.mItem = mAdapter.get(position);
355 
356         viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
357 
358         onBind(viewHolder);
359         if (mAdapterListener != null) {
360             mAdapterListener.onBind(viewHolder);
361         }
362     }
363 
364     @Override
onViewRecycled(RecyclerView.ViewHolder holder)365     public final void onViewRecycled(RecyclerView.ViewHolder holder) {
366         ViewHolder viewHolder = (ViewHolder) holder;
367         viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder);
368         onUnbind(viewHolder);
369         if (mAdapterListener != null) {
370             mAdapterListener.onUnbind(viewHolder);
371         }
372         viewHolder.mItem = null;
373     }
374 
375     @Override
onViewAttachedToWindow(RecyclerView.ViewHolder holder)376     public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
377         ViewHolder viewHolder = (ViewHolder) holder;
378         onAttachedToWindow(viewHolder);
379         if (mAdapterListener != null) {
380             mAdapterListener.onAttachedToWindow(viewHolder);
381         }
382         viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder);
383     }
384 
385     @Override
onViewDetachedFromWindow(RecyclerView.ViewHolder holder)386     public final void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
387         ViewHolder viewHolder = (ViewHolder) holder;
388         viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder);
389         onDetachedFromWindow(viewHolder);
390         if (mAdapterListener != null) {
391             mAdapterListener.onDetachedFromWindow(viewHolder);
392         }
393     }
394 
395     @Override
getItemId(int position)396     public long getItemId(int position) {
397         return mAdapter.getId(position);
398     }
399 
400     @Override
getFacetProvider(int type)401     public FacetProvider getFacetProvider(int type) {
402         return mPresenters.get(type);
403     }
404 }
405