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 androidx.leanback.app.SearchFragment; 25 import androidx.leanback.widget.ArrayObjectAdapter; 26 import androidx.leanback.widget.HeaderItem; 27 import androidx.leanback.widget.ImageCardView; 28 import androidx.leanback.widget.ListRow; 29 import androidx.leanback.widget.ListRowPresenter; 30 import androidx.leanback.widget.ObjectAdapter; 31 import androidx.leanback.widget.OnItemViewClickedListener; 32 import androidx.leanback.widget.Presenter; 33 import androidx.leanback.widget.Row; 34 import androidx.leanback.widget.RowPresenter; 35 import androidx.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 import com.android.tv.MainActivity; 42 import com.android.tv.R; 43 import com.android.tv.common.util.PermissionUtils; 44 import com.android.tv.util.images.ImageLoader; 45 import java.util.List; 46 47 public class ProgramGuideSearchFragment extends SearchFragment { 48 private static final String TAG = "ProgramGuideSearch"; 49 private static final boolean DEBUG = false; 50 private static final int SEARCH_RESULT_MAX = 10; 51 52 private final Presenter mPresenter = 53 new Presenter() { 54 @Override 55 public Presenter.ViewHolder onCreateViewHolder(ViewGroup viewGroup) { 56 if (DEBUG) Log.d(TAG, "onCreateViewHolder"); 57 58 ImageCardView cardView = new ImageCardView(mMainActivity); 59 cardView.setFocusable(true); 60 cardView.setFocusableInTouchMode(true); 61 cardView.setMainImageAdjustViewBounds(false); 62 63 Resources res = mMainActivity.getResources(); 64 cardView.setMainImageDimensions( 65 res.getDimensionPixelSize(R.dimen.card_image_layout_width), 66 res.getDimensionPixelSize(R.dimen.card_image_layout_height)); 67 68 return new Presenter.ViewHolder(cardView); 69 } 70 71 @Override 72 public void onBindViewHolder(ViewHolder viewHolder, Object o) { 73 ImageCardView cardView = (ImageCardView) viewHolder.view; 74 LocalSearchProvider.SearchResult result = (LocalSearchProvider.SearchResult) o; 75 if (DEBUG) Log.d(TAG, "onBindViewHolder result:" + result); 76 77 cardView.setTitleText(result.getTitle()); 78 if (!TextUtils.isEmpty(result.getImageUri())) { 79 ImageLoader.loadBitmap( 80 mMainActivity, 81 result.getImageUri(), 82 mMainCardWidth, 83 mMainCardHeight, 84 createImageLoaderCallback(cardView)); 85 } else { 86 cardView.setMainImage( 87 mMainActivity.getDrawable(R.drawable.ic_tv_app_96x96)); 88 } 89 } 90 91 @Override 92 public void onUnbindViewHolder(ViewHolder viewHolder) { 93 // Do nothing here. 94 } 95 }; 96 createImageLoaderCallback( ImageCardView cardView)97 private static ImageLoader.ImageLoaderCallback<ImageCardView> createImageLoaderCallback( 98 ImageCardView cardView) { 99 return new ImageLoader.ImageLoaderCallback<ImageCardView>(cardView) { 100 @Override 101 public void onBitmapLoaded(ImageCardView cardView, Bitmap bitmap) { 102 cardView.setMainImage( 103 new BitmapDrawable(cardView.getContext().getResources(), bitmap)); 104 } 105 }; 106 } 107 108 private final SearchResultProvider mSearchResultProvider = 109 new SearchResultProvider() { 110 @Override 111 public ObjectAdapter getResultsAdapter() { 112 return mResultAdapter; 113 } 114 115 @Override 116 public boolean onQueryTextChange(String query) { 117 searchAndRefresh(query); 118 return true; 119 } 120 121 @Override 122 public boolean onQueryTextSubmit(String query) { 123 searchAndRefresh(query); 124 return true; 125 } 126 }; 127 128 private final OnItemViewClickedListener mItemClickedListener = 129 new OnItemViewClickedListener() { 130 @Override 131 public void onItemClicked( 132 Presenter.ViewHolder viewHolder, 133 Object o, 134 RowPresenter.ViewHolder viewHolder1, 135 Row row) { 136 LocalSearchProvider.SearchResult result = (LocalSearchProvider.SearchResult) o; 137 mMainActivity.getFragmentManager().popBackStack(); 138 mMainActivity.tuneToChannel( 139 mMainActivity 140 .getChannelDataManager() 141 .getChannel(result.getChannelId())); 142 } 143 }; 144 145 private final ArrayObjectAdapter mResultAdapter = 146 new ArrayObjectAdapter(new ListRowPresenter()); 147 private MainActivity mMainActivity; 148 private SearchInterface mSearch; 149 private int mMainCardWidth; 150 private int mMainCardHeight; 151 private SearchTask mSearchTask; 152 153 @Override 154 public void onCreate(Bundle savedInstanceState) { 155 super.onCreate(savedInstanceState); 156 157 mMainActivity = (MainActivity) getActivity(); 158 if (PermissionUtils.hasAccessAllEpg(mMainActivity)) { 159 mSearch = new TvProviderSearch(mMainActivity); 160 } else { 161 mSearch = new DataManagerSearch(mMainActivity); 162 } 163 Resources res = getResources(); 164 mMainCardWidth = res.getDimensionPixelSize(R.dimen.card_image_layout_width); 165 mMainCardHeight = res.getDimensionPixelSize(R.dimen.card_image_layout_height); 166 } 167 168 @Override 169 public View onCreateView( 170 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 171 View v = super.onCreateView(inflater, container, savedInstanceState); 172 v.setBackgroundResource(R.color.program_guide_scrim); 173 174 setBadgeDrawable(mMainActivity.getDrawable(R.drawable.ic_tv_app_96x96)); 175 setSearchResultProvider(mSearchResultProvider); 176 setOnItemViewClickedListener(mItemClickedListener); 177 return v; 178 } 179 180 @Override 181 public void onResume() { 182 super.onResume(); 183 184 SearchBar searchBar = (SearchBar) getView().findViewById(R.id.lb_search_bar); 185 searchBar.setSearchQuery(""); 186 mResultAdapter.clear(); 187 } 188 189 private void searchAndRefresh(String query) { 190 // TODO: Search directly from program data manager for performance. 191 // TODO: Search upcoming programs. 192 if (mSearchTask != null) { 193 mSearchTask.cancel(true); 194 } 195 mSearchTask = new SearchTask(query); 196 mSearchTask.execute(); 197 } 198 199 private class SearchTask extends AsyncTask<Void, Void, List<LocalSearchProvider.SearchResult>> { 200 private final String mQuery; 201 202 public SearchTask(String query) { 203 mQuery = query; 204 } 205 206 @Override 207 protected List<LocalSearchProvider.SearchResult> doInBackground(Void... params) { 208 return mSearch.search( 209 mQuery, SEARCH_RESULT_MAX, TvProviderSearch.ACTION_TYPE_AMBIGUOUS); 210 } 211 212 @Override 213 protected void onPostExecute(List<LocalSearchProvider.SearchResult> results) { 214 super.onPostExecute(results); 215 mResultAdapter.clear(); 216 217 if (DEBUG) { 218 Log.d( 219 TAG, 220 "searchAndRefresh query=" 221 + mQuery 222 + " results=" 223 + ((results == null) ? 0 : results.size())); 224 } 225 226 if (results == null || results.size() == 0) { 227 HeaderItem header = 228 new HeaderItem( 229 0, mMainActivity.getString(R.string.search_result_no_result)); 230 ArrayObjectAdapter resultsAdapter = new ArrayObjectAdapter(mPresenter); 231 mResultAdapter.add(new ListRow(header, resultsAdapter)); 232 } else { 233 HeaderItem header = 234 new HeaderItem(0, mMainActivity.getString(R.string.search_result_title)); 235 ArrayObjectAdapter resultsAdapter = new ArrayObjectAdapter(mPresenter); 236 resultsAdapter.addAll(0, results); 237 mResultAdapter.add(new ListRow(header, resultsAdapter)); 238 } 239 mSearchTask = null; 240 } 241 } 242 } 243