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 
createImageLoaderCallback( ImageCardView cardView)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