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