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
17package com.android.tv.search;
18
19import android.content.res.Resources;
20import android.graphics.Bitmap;
21import android.graphics.drawable.BitmapDrawable;
22import android.os.AsyncTask;
23import android.os.Bundle;
24import android.support.v17.leanback.app.SearchFragment;
25import android.support.v17.leanback.widget.ArrayObjectAdapter;
26import android.support.v17.leanback.widget.HeaderItem;
27import android.support.v17.leanback.widget.ImageCardView;
28import android.support.v17.leanback.widget.ListRow;
29import android.support.v17.leanback.widget.ListRowPresenter;
30import android.support.v17.leanback.widget.ObjectAdapter;
31import android.support.v17.leanback.widget.OnItemViewClickedListener;
32import android.support.v17.leanback.widget.Presenter;
33import android.support.v17.leanback.widget.Row;
34import android.support.v17.leanback.widget.RowPresenter;
35import android.support.v17.leanback.widget.SearchBar;
36import android.text.TextUtils;
37import android.util.Log;
38import android.view.LayoutInflater;
39import android.view.View;
40import android.view.ViewGroup;
41
42import com.android.tv.MainActivity;
43import com.android.tv.R;
44import com.android.tv.util.ImageLoader;
45import com.android.tv.util.PermissionUtils;
46
47import java.util.List;
48
49public 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