• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.example.android.leanback;
2 
3 import static com.example.android.leanback.CardPresenter.CONTENT;
4 import static com.example.android.leanback.CardPresenter.IMAGE;
5 import static com.example.android.leanback.CardPresenter.TITLE;
6 
7 import android.content.Context;
8 import android.content.Intent;
9 import android.os.Bundle;
10 import android.os.Handler;
11 import android.text.TextUtils;
12 import android.util.Log;
13 
14 import androidx.annotation.Nullable;
15 import androidx.core.app.ActivityOptionsCompat;
16 import androidx.core.content.res.ResourcesCompat;
17 import androidx.leanback.widget.ArrayObjectAdapter;
18 import androidx.leanback.widget.DiffCallback;
19 import androidx.leanback.widget.HeaderItem;
20 import androidx.leanback.widget.ImageCardView;
21 import androidx.leanback.widget.ListRow;
22 import androidx.leanback.widget.ListRowPresenter;
23 import androidx.leanback.widget.ObjectAdapter;
24 import androidx.leanback.widget.OnItemViewClickedListener;
25 import androidx.leanback.widget.Presenter;
26 import androidx.leanback.widget.Row;
27 import androidx.leanback.widget.RowPresenter;
28 
29 import java.util.ArrayList;
30 
31 public class SearchFragment extends androidx.leanback.app.SearchFragment
32         implements androidx.leanback.app.SearchFragment.SearchResultProvider {
33     private static final String TAG = "leanback.SearchFragment";
34     private static final int NUM_ROWS = 3;
35     private static final int SEARCH_DELAY_MS = 1000;
36 
37     private ArrayObjectAdapter mRowsAdapter;
38     private Handler mHandler = new Handler();
39     private String mQuery;
40 
41     // Flag to represent if data set one is presented in the fragment
42     private boolean mIsDataSetOnePresented;
43 
44     // Adapter for first row
45     private ArrayObjectAdapter mFirstRowAdapter;
46 
47     // The diff callback which defines the standard to judge if two items are the same or if
48     // two items have the same content.
49     private DiffCallback<PhotoItem> mDiffCallback = new DiffCallback<PhotoItem>() {
50 
51         // when two photo items have the same id, they are the same from adapter's
52         // perspective
53         @Override
54         public boolean areItemsTheSame(PhotoItem oldItem, PhotoItem newItem) {
55             return oldItem.getId() == newItem.getId();
56         }
57 
58         // when two photo items is equal to each other (based on the equal method defined in
59         // PhotoItem), they have the same content.
60         @Override
61         public boolean areContentsTheSame(PhotoItem oldItem, PhotoItem newItem) {
62             return oldItem.equals(newItem);
63         }
64 
65         @Nullable
66         @Override
67         public Object getChangePayload(PhotoItem oldItem, PhotoItem newItem) {
68             Bundle diff = new Bundle();
69             if (oldItem.getImageResourceId()
70                     != newItem.getImageResourceId()) {
71                 diff.putLong(IMAGE, newItem.getImageResourceId());
72             }
73 
74             if (oldItem.getTitle() != null && newItem.getTitle() != null
75                     && !oldItem.getTitle().equals(newItem.getTitle())) {
76                 diff.putString(TITLE, newItem.getTitle());
77             }
78 
79             if (oldItem.getContent() != null && newItem.getContent() != null
80                     && !oldItem.getContent().equals(newItem.getContent())) {
81                 diff.putString(CONTENT, newItem.getContent());
82             }
83             return diff;
84         }
85     };
86 
87     @Override
onCreate(Bundle savedInstanceState)88     public void onCreate(Bundle savedInstanceState) {
89         super.onCreate(savedInstanceState);
90 
91         mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
92 
93         final Context context = getActivity();
94         setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
95                 R.drawable.ic_title, context.getTheme()));
96         setTitle("Leanback Sample App");
97         setSearchResultProvider(this);
98         setOnItemViewClickedListener(new ItemViewClickedListener());
99     }
100 
101     @Override
getResultsAdapter()102     public ObjectAdapter getResultsAdapter() {
103         return mRowsAdapter;
104     }
105 
106     @Override
onQueryTextChange(String newQuery)107     public boolean onQueryTextChange(String newQuery) {
108         Log.i(TAG, String.format("Search Query Text Change %s", newQuery));
109         mRowsAdapter.clear();
110         loadQuery(newQuery);
111         return true;
112     }
113 
114     @Override
onQueryTextSubmit(String query)115     public boolean onQueryTextSubmit(String query) {
116         Log.i(TAG, String.format("Search Query Text Submit %s", query));
117         mRowsAdapter.clear();
118         loadQuery(query);
119         return true;
120     }
121 
loadQuery(String query)122     private void loadQuery(String query) {
123         mQuery = query;
124         mHandler.removeCallbacks(mDelayedLoad);
125         if (!TextUtils.isEmpty(query) && !query.equals("nil")) {
126             mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
127         }
128     }
129 
loadRows()130     private void loadRows() {
131         HeaderItem header = new HeaderItem(0, mQuery + " results row " + 0);
132 
133         // Every time when the query event is fired, we will update the fake search result in the
134         // first row based on the flag mIsDataSetOnePresented flag.
135         // Also the first row adapter will only be created once so the animation will be triggered
136         // when the items in the adapter changed.
137         if (!mIsDataSetOnePresented) {
138             if (mFirstRowAdapter == null) {
139                 mFirstRowAdapter = createFirstListRowAdapter();
140             } else {
141                 mFirstRowAdapter.setItems(createDataSetOneDebug(), mDiffCallback);
142             }
143             mIsDataSetOnePresented = true;
144         } else {
145             mFirstRowAdapter.setItems(createDataSetTwoDebug(), mDiffCallback);
146             mIsDataSetOnePresented = false;
147         }
148         mRowsAdapter.add(new ListRow(header, mFirstRowAdapter));
149         for (int i = 1; i < NUM_ROWS + 1; ++i) {
150             ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
151             listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1));
152             listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2));
153             header = new HeaderItem(i, mQuery + " results row " + i);
154             mRowsAdapter.add(new ListRow(header, listRowAdapter));
155         }
156     }
157 
158     private Runnable mDelayedLoad = new Runnable() {
159         @Override
160         public void run() {
161             loadRows();
162         }
163     };
164 
165     private final class ItemViewClickedListener implements OnItemViewClickedListener {
166         @Override
onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row)167         public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
168                 RowPresenter.ViewHolder rowViewHolder, Row row) {
169             Intent intent = new Intent(getActivity(), DetailsActivity.class);
170             intent.putExtra(DetailsActivity.EXTRA_ITEM, (PhotoItem) item);
171 
172             Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
173                     getActivity(),
174                     ((ImageCardView) itemViewHolder.view).getMainImageView(),
175                     DetailsActivity.SHARED_ELEMENT_NAME).toBundle();
176             getActivity().startActivity(intent, bundle);
177         }
178     }
179 
180 
createFirstListRowAdapter()181     private ArrayObjectAdapter createFirstListRowAdapter() {
182         ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
183         listRowAdapter.setItems(createDataSetOneDebug(), mDiffCallback);
184         mIsDataSetOnePresented = true;
185         return listRowAdapter;
186     }
187 
188     /**
189      * Create a data set (data set one) for the last row of this browse fragment. It will be
190      * changed by another set of data when user click one of the photo items in the list.
191      * Different with other rows in the browsing fragment, the photo item in last row all have been
192      * allocated with a unique id. And the id will be used to jduge if two photo items are the same
193      * or not.
194      *
195      * @return List of photoItem
196      */
createDataSetOne()197     private ArrayList<PhotoItem> createDataSetOne() {
198         ArrayList<PhotoItem> photoItems = new ArrayList<>();
199         photoItems.add(new PhotoItem(
200                 "Hello world",
201                 R.drawable.gallery_photo_1,
202                 1));
203         photoItems.add(new PhotoItem(
204                 "This is a test",
205                 "Only a test",
206                 R.drawable.gallery_photo_2,
207                 2));
208         photoItems.add(new PhotoItem(
209                 "Android TV",
210                 "by Google",
211                 R.drawable.gallery_photo_3,
212                 3));
213         photoItems.add(new PhotoItem(
214                 "Leanback",
215                 R.drawable.gallery_photo_4,
216                 4));
217         photoItems.add(new PhotoItem(
218                 "GuidedStep (Slide left/right)",
219                 R.drawable.gallery_photo_5,
220                 5));
221         photoItems.add(new PhotoItem(
222                 "GuidedStep (Slide bottom up)",
223                 "Open GuidedStepFragment",
224                 R.drawable.gallery_photo_6,
225                 6));
226         photoItems.add(new PhotoItem(
227                 "Android TV",
228                 "open RowsActivity",
229                 R.drawable.gallery_photo_7,
230                 7));
231         photoItems.add(new PhotoItem(
232                 "Leanback",
233                 "open BrowseActivity",
234                 R.drawable.gallery_photo_8,
235                 8));
236         photoItems.add(new PhotoItem(
237                 "Hello world",
238                 R.drawable.gallery_photo_1,
239                 1));
240         photoItems.add(new PhotoItem(
241                 "This is a test",
242                 "Only a test",
243                 R.drawable.gallery_photo_2,
244                 2));
245         photoItems.add(new PhotoItem(
246                 "Android TV",
247                 "by Google",
248                 R.drawable.gallery_photo_3,
249                 3));
250         photoItems.add(new PhotoItem(
251                 "Leanback",
252                 R.drawable.gallery_photo_4,
253                 4));
254         return photoItems;
255     }
256 
257     /**
258      * Create a new data set (data set one) for the last row of this browse fragment. It will be
259      * changed by another set of data when user click one of the photo items in the list.
260      * Different with other rows in the browsing fragment, the photo item in last row all have been
261      * allocated with a unique id. And the id will be used to jduge if two photo items are the same
262      * or not.
263      *
264      * @return List of photoItem
265      */
createDataSetTwo()266     private ArrayList<PhotoItem> createDataSetTwo() {
267         ArrayList<PhotoItem> photoItems = new ArrayList<>();
268         photoItems.add(new PhotoItem(
269                 "This is a test",
270                 "Only a test",
271                 R.drawable.gallery_photo_2,
272                 2));
273         photoItems.add(new PhotoItem(
274                 "Hello world",
275                 R.drawable.gallery_photo_1,
276                 1));
277         photoItems.add(new PhotoItem(
278                 "Leanback",
279                 R.drawable.gallery_photo_4,
280                 4));
281         photoItems.add(new PhotoItem(
282                 "Android TV",
283                 "by Google",
284                 R.drawable.gallery_photo_3,
285                 3));
286         photoItems.add(new PhotoItem(
287                 "change title",
288                 R.drawable.gallery_photo_5,
289                 5));
290         photoItems.add(new PhotoItem(
291                 "GuidedStep (Slide bottom up)",
292                 "change comment",
293                 R.drawable.gallery_photo_6,
294                 6));
295         photoItems.add(new PhotoItem(
296                 "Android TV",
297                 R.drawable.gallery_photo_7,
298                 7));
299         photoItems.add(new PhotoItem(
300                 "Leanback",
301                 "open BrowseActivity",
302                 R.drawable.gallery_photo_7,
303                 8));
304         photoItems.add(new PhotoItem(
305                 "Hello world",
306                 R.drawable.gallery_photo_1,
307                 10));
308         photoItems.add(new PhotoItem(
309                 "This is a test",
310                 "Only a test",
311                 R.drawable.gallery_photo_2,
312                 20));
313         photoItems.add(new PhotoItem(
314                 "Android TV",
315                 "by Google",
316                 R.drawable.gallery_photo_3,
317                 30));
318         photoItems.add(new PhotoItem(
319                 "Leanback",
320                 R.drawable.gallery_photo_4,
321                 40));
322         return photoItems;
323     }
324 
325 
createDataSetOneDebug()326     private ArrayList<PhotoItem> createDataSetOneDebug() {
327         ArrayList<PhotoItem> photoItems = new ArrayList<>();
328         photoItems.add(new PhotoItem(
329                 "Hello world",
330                 R.drawable.gallery_photo_1,
331                 1));
332         return photoItems;
333     }
334 
335     /**
336      * Create a new data set (data set one) for the last row of this browse fragment. It will be
337      * changed by another set of data when user click one of the photo items in the list.
338      * Different with other rows in the browsing fragment, the photo item in last row all have been
339      * allocated with a unique id. And the id will be used to jduge if two photo items are the same
340      * or not.
341      *
342      * @return List of photoItem
343      */
createDataSetTwoDebug()344     private ArrayList<PhotoItem> createDataSetTwoDebug() {
345         ArrayList<PhotoItem> photoItems = new ArrayList<>();
346         photoItems.add(new PhotoItem(
347                 "Hello world Hello world",
348                 R.drawable.gallery_photo_1,
349                 1));
350         return photoItems;
351     }
352 }
353