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