1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.search; 18 19 import android.content.res.Resources; 20 import android.graphics.Bitmap; 21 import android.graphics.drawable.BitmapDrawable; 22 import android.os.AsyncTask; 23 import android.os.Bundle; 24 import android.support.v17.leanback.app.SearchFragment; 25 import android.support.v17.leanback.widget.ArrayObjectAdapter; 26 import android.support.v17.leanback.widget.HeaderItem; 27 import android.support.v17.leanback.widget.ImageCardView; 28 import android.support.v17.leanback.widget.ListRow; 29 import android.support.v17.leanback.widget.ListRowPresenter; 30 import android.support.v17.leanback.widget.ObjectAdapter; 31 import android.support.v17.leanback.widget.OnItemViewClickedListener; 32 import android.support.v17.leanback.widget.Presenter; 33 import android.support.v17.leanback.widget.Row; 34 import android.support.v17.leanback.widget.RowPresenter; 35 import android.support.v17.leanback.widget.SearchBar; 36 import android.text.TextUtils; 37 import android.util.Log; 38 import android.view.LayoutInflater; 39 import android.view.View; 40 import android.view.ViewGroup; 41 42 import com.android.tv.MainActivity; 43 import com.android.tv.R; 44 import com.android.tv.util.ImageLoader; 45 import com.android.tv.util.PermissionUtils; 46 47 import java.util.List; 48 49 public class ProgramGuideSearchFragment extends SearchFragment { 50 private static final String TAG = "ProgramGuideSearch"; 51 private static final boolean DEBUG = false; 52 private static final int SEARCH_RESULT_MAX = 10; 53 54 private final Presenter mPresenter = new Presenter() { 55 @Override 56 public Presenter.ViewHolder onCreateViewHolder(ViewGroup viewGroup) { 57 if (DEBUG) Log.d(TAG, "onCreateViewHolder"); 58 59 ImageCardView cardView = new ImageCardView(mMainActivity); 60 cardView.setFocusable(true); 61 cardView.setFocusableInTouchMode(true); 62 cardView.setMainImageAdjustViewBounds(false); 63 64 Resources res = mMainActivity.getResources(); 65 cardView.setMainImageDimensions( 66 res.getDimensionPixelSize(R.dimen.card_image_layout_width), 67 res.getDimensionPixelSize(R.dimen.card_image_layout_height)); 68 69 return new Presenter.ViewHolder(cardView); 70 } 71 72 @Override 73 public void onBindViewHolder(ViewHolder viewHolder, Object o) { 74 ImageCardView cardView = (ImageCardView) viewHolder.view; 75 LocalSearchProvider.SearchResult result = (LocalSearchProvider.SearchResult) o; 76 if (DEBUG) Log.d(TAG, "onBindViewHolder result:" + result); 77 78 cardView.setTitleText(result.title); 79 if (!TextUtils.isEmpty(result.imageUri)) { 80 ImageLoader.loadBitmap(mMainActivity, result.imageUri, mMainCardWidth, 81 mMainCardHeight, createImageLoaderCallback(cardView)); 82 } else { 83 cardView.setMainImage(mMainActivity.getDrawable(R.drawable.ic_launcher)); 84 } 85 } 86 87 @Override 88 public void onUnbindViewHolder(ViewHolder viewHolder) { 89 // Do nothing here. 90 } 91 }; 92 93 private static ImageLoader.ImageLoaderCallback<ImageCardView> createImageLoaderCallback( 94 ImageCardView cardView) { 95 return new ImageLoader.ImageLoaderCallback<ImageCardView>(cardView) { 96 @Override 97 public void onBitmapLoaded(ImageCardView cardView, Bitmap bitmap) { 98 cardView.setMainImage( 99 new BitmapDrawable(cardView.getContext().getResources(), bitmap)); 100 } 101 }; 102 } 103 104 private final SearchResultProvider mSearchResultProvider = new SearchResultProvider() { 105 @Override 106 public ObjectAdapter getResultsAdapter() { 107 return mResultAdapter; 108 } 109 110 @Override 111 public boolean onQueryTextChange(String query) { 112 searchAndRefresh(query); 113 return true; 114 } 115 116 @Override 117 public boolean onQueryTextSubmit(String query) { 118 searchAndRefresh(query); 119 return true; 120 } 121 }; 122 123 private final OnItemViewClickedListener mItemClickedListener = new OnItemViewClickedListener() { 124 @Override 125 public void onItemClicked(Presenter.ViewHolder viewHolder, Object o, RowPresenter 126 .ViewHolder viewHolder1, Row row) { 127 LocalSearchProvider.SearchResult result = (LocalSearchProvider.SearchResult) o; 128 mMainActivity.getFragmentManager().popBackStack(); 129 mMainActivity.tuneToChannel( 130 mMainActivity.getChannelDataManager().getChannel(result.channelId)); 131 } 132 }; 133 134 private final ArrayObjectAdapter mResultAdapter = 135 new ArrayObjectAdapter(new ListRowPresenter()); 136 private MainActivity mMainActivity; 137 private SearchInterface mSearch; 138 private int mMainCardWidth; 139 private int mMainCardHeight; 140 private SearchTask mSearchTask; 141 142 @Override 143 public void onCreate(Bundle savedInstanceState) { 144 super.onCreate(savedInstanceState); 145 146 mMainActivity = (MainActivity) getActivity(); 147 if (PermissionUtils.hasAccessAllEpg(mMainActivity)) { 148 mSearch = new TvProviderSearch(mMainActivity); 149 } else { 150 mSearch = new DataManagerSearch(mMainActivity); 151 } 152 Resources res = getResources(); 153 mMainCardWidth = res.getDimensionPixelSize(R.dimen.card_image_layout_width); 154 mMainCardHeight = res.getDimensionPixelSize(R.dimen.card_image_layout_height); 155 } 156 157 @Override 158 public View onCreateView( 159 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 160 View v = super.onCreateView(inflater, container, savedInstanceState); 161 v.setBackgroundResource(R.color.program_guide_scrim); 162 163 setBadgeDrawable(mMainActivity.getDrawable(R.drawable.ic_launcher)); 164 setSearchResultProvider(mSearchResultProvider); 165 setOnItemViewClickedListener(mItemClickedListener); 166 return v; 167 } 168 169 @Override 170 public void onResume() { 171 super.onResume(); 172 173 SearchBar searchBar = (SearchBar) getView().findViewById(R.id.lb_search_bar); 174 searchBar.setSearchQuery(""); 175 mResultAdapter.clear(); 176 } 177 178 private void searchAndRefresh(String query) { 179 // TODO: Search directly from program data manager for performance. 180 // TODO: Search upcoming programs. 181 if (mSearchTask != null) { 182 mSearchTask.cancel(true); 183 } 184 mSearchTask = new SearchTask(query); 185 mSearchTask.execute(); 186 } 187 188 private class SearchTask extends 189 AsyncTask<Void, Void, List<LocalSearchProvider.SearchResult>> { 190 private final String mQuery; 191 192 public SearchTask(String query) { 193 mQuery = query; 194 } 195 196 @Override 197 protected List<LocalSearchProvider.SearchResult> doInBackground(Void... params) { 198 return mSearch.search(mQuery, SEARCH_RESULT_MAX, 199 TvProviderSearch.ACTION_TYPE_AMBIGUOUS); 200 } 201 202 @Override 203 protected void onPostExecute(List<LocalSearchProvider.SearchResult> results) { 204 super.onPostExecute(results); 205 mResultAdapter.clear(); 206 207 if (DEBUG) { 208 Log.d(TAG, "searchAndRefresh query=" + mQuery 209 + " results=" + ((results == null) ? 0 : results.size())); 210 } 211 212 if (results == null || results.size() == 0) { 213 HeaderItem header = 214 new HeaderItem(0, mMainActivity.getString(R.string 215 .search_result_no_result)); 216 ArrayObjectAdapter resultsAdapter = new ArrayObjectAdapter(mPresenter); 217 mResultAdapter.add(new ListRow(header, resultsAdapter)); 218 } else { 219 HeaderItem header = 220 new HeaderItem(0, mMainActivity.getString(R.string 221 .search_result_title)); 222 ArrayObjectAdapter resultsAdapter = new ArrayObjectAdapter(mPresenter); 223 resultsAdapter.addAll(0, results); 224 mResultAdapter.add(new ListRow(header, resultsAdapter)); 225 } 226 mSearchTask = null; 227 } 228 } 229 } 230