17d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko/*
27d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * Copyright (C) 2015 The Android Open Source Project
37d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *
47d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
57d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * you may not use this file except in compliance with the License.
67d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * You may obtain a copy of the License at
77d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *
87d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
97d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *
107d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * Unless required by applicable law or agreed to in writing, software
117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
127d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * See the License for the specific language governing permissions and
147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * limitations under the License.
157d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko */
167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
177d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkopackage com.android.tv.search;
187d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
197d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.content.Context;
207d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.content.Intent;
217d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.media.tv.TvContentRating;
227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.media.tv.TvContract;
237d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.media.tv.TvContract.Programs;
247d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.media.tv.TvInputManager;
25ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.MainThread;
267d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.text.TextUtils;
277d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.util.Log;
287d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
291abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.ApplicationSingletons;
307d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.TvApplication;
317d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.data.Channel;
327d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.data.ChannelDataManager;
337d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.data.Program;
347d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.data.ProgramDataManager;
357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.search.LocalSearchProvider.SearchResult;
367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.util.MainThreadExecutor;
377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.util.Utils;
387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.ArrayList;
407d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.Collections;
417d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.HashSet;
427d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.List;
437d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.Set;
447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.concurrent.Callable;
457d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.concurrent.ExecutionException;
467d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.concurrent.Future;
477d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
487d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko/**
497d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * An implementation of {@link SearchInterface} to search query from {@link ChannelDataManager}
507d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * and {@link ProgramDataManager}.
517d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko */
527d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkopublic class DataManagerSearch implements SearchInterface {
537d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private static final boolean DEBUG = false;
547d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private static final String TAG = "TvProviderSearch";
557d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
567d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final Context mContext;
577d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final TvInputManager mTvInputManager;
587d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final ChannelDataManager mChannelDataManager;
597d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final ProgramDataManager mProgramDataManager;
607d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
617d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    DataManagerSearch(Context context) {
627d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mContext = context;
637d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mChannelDataManager = appSingletons.getChannelDataManager();
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mProgramDataManager = appSingletons.getProgramDataManager();
677d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
687d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    @Override
707d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    public List<SearchResult> search(final String query, final int limit, final int action) {
717d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        Future<List<SearchResult>> future = MainThreadExecutor.getInstance()
727d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                .submit(new Callable<List<SearchResult>>() {
737d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    @Override
747d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    public List<SearchResult> call() throws Exception {
757d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        return searchFromDataManagers(query, limit, action);
767d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    }
777d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                });
787d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
797d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        try {
807d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            return future.get();
817d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        } catch (InterruptedException e) {
827d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            Thread.interrupted();
837d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            return Collections.EMPTY_LIST;
847d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        } catch (ExecutionException e) {
857d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            Log.w(TAG, "Error searching for " + query, e);
867d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            return Collections.EMPTY_LIST;
877d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
887d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
897d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
90ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    @MainThread
917d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private List<SearchResult> searchFromDataManagers(String query, int limit, int action) {
927d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        List<SearchResult> results = new ArrayList<>();
937d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (!mChannelDataManager.isDbLoadFinished()) {
947d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            return results;
957d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
967d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (action == ACTION_TYPE_SWITCH_CHANNEL
977d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                || action == ACTION_TYPE_SWITCH_INPUT) {
987d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            // Voice search query should be handled by the a system TV app.
997d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            return results;
1007d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
1017d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        Set<Long> channelsFound = new HashSet<>();
1027d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        List<Channel> channelList = mChannelDataManager.getBrowsableChannelList();
1037d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        query = query.toLowerCase();
1047d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (TextUtils.isDigitsOnly(query)) {
1057d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            for (Channel channel : channelList) {
1067d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                if (channelsFound.contains(channel.getId())) {
1077d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    continue;
1087d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1097d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                if (contains(channel.getDisplayNumber(), query)) {
1107d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    addResult(results, channelsFound, channel, null);
1117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1127d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                if (results.size() >= limit) {
1137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    return results;
1147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1157d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            // TODO: recently watched channels may have higher priority.
1177d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
1187d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        for (Channel channel : channelList) {
1197d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (channelsFound.contains(channel.getId())) {
1207d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                continue;
1217d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (contains(channel.getDisplayName(), query)
1237d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    || contains(channel.getDescription(), query)) {
1247d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                addResult(results, channelsFound, channel, null);
1257d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1267d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (results.size() >= limit) {
1277d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                return results;
1287d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1297d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
1307d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        for (Channel channel : channelList) {
1317d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (channelsFound.contains(channel.getId())) {
1327d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                continue;
1337d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1347d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            Program program = mProgramDataManager.getCurrentProgram(channel.getId());
1357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (program == null) {
1367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                continue;
1377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (contains(program.getTitle(), query)
1397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    && !isRatingBlocked(program.getContentRatings())) {
1407d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                addResult(results, channelsFound, channel, program);
1417d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1427d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (results.size() >= limit) {
1437d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                return results;
1447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1457d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
1467d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        for (Channel channel : channelList) {
1477d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (channelsFound.contains(channel.getId())) {
1487d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                continue;
1497d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1507d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            Program program = mProgramDataManager.getCurrentProgram(channel.getId());
1517d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (program == null) {
1527d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                continue;
1537d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1547d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (contains(program.getDescription(), query)
1557d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    && !isRatingBlocked(program.getContentRatings())) {
1567d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                addResult(results, channelsFound, channel, program);
1577d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1587d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (results.size() >= limit) {
1597d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                return results;
1607d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1617d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
1627d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        return results;
1637d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
1647d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1657d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    // It assumes that query is already lower cases.
1667d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private boolean contains(String string, String query) {
1677d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        return string != null && string.toLowerCase().contains(query);
1687d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
1697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1707d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    /**
1717d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     * If query is matched to channel, {@code program} should be null.
1727d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     */
1737d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private void addResult(List<SearchResult> results, Set<Long> channelsFound, Channel channel,
1747d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            Program program) {
1757d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (program == null) {
1767d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            program = mProgramDataManager.getCurrentProgram(channel.getId());
1777d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (program != null && isRatingBlocked(program.getContentRatings())) {
1787d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                program = null;
1797d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1807d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
1817d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1827d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        SearchResult result = new SearchResult();
1837d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1847d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        long channelId = channel.getId();
1857d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        result.channelId = channelId;
1867d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        result.channelNumber = channel.getDisplayNumber();
1877d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (program == null) {
1887d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.title = channel.getDisplayName();
1897d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.description = channel.getDescription();
1907d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.imageUri = TvContract.buildChannelLogoUri(channelId).toString();
1917d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.intentAction = Intent.ACTION_VIEW;
1927d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.intentData = buildIntentData(channelId);
1937d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.contentType = Programs.CONTENT_ITEM_TYPE;
1947d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.isLive = true;
1957d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.progressPercentage = LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE;
1967d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        } else {
1977d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.title = program.getTitle();
1987d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.description = buildProgramDescription(channel.getDisplayNumber(),
1997d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    channel.getDisplayName(), program.getStartTimeUtcMillis(),
2007d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    program.getEndTimeUtcMillis());
2017d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.imageUri = program.getPosterArtUri();
2027d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.intentAction = Intent.ACTION_VIEW;
2037d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.intentData = buildIntentData(channelId);
2047d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.contentType = Programs.CONTENT_ITEM_TYPE;
2057d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.isLive = true;
2067d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.videoWidth = program.getVideoWidth();
2077d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.videoHeight = program.getVideoHeight();
2087d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.duration = program.getDurationMillis();
2097d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            result.progressPercentage = getProgressPercentage(
2107d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    program.getStartTimeUtcMillis(), program.getEndTimeUtcMillis());
2117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
2127d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (DEBUG) {
2137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            Log.d(TAG, "Add a result : channel=" + channel + " program=" + program);
2147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
2157d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        results.add(result);
2167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        channelsFound.add(channel.getId());
2177d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
2187d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
2197d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private String buildProgramDescription(String channelNumber, String channelName,
2207d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            long programStartUtcMillis, long programEndUtcMillis) {
2217d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        return Utils.getDurationString(mContext, programStartUtcMillis, programEndUtcMillis, false)
2227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                + System.lineSeparator() + channelNumber + " " + channelName;
2237d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
2247d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
2257d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private int getProgressPercentage(long startUtcMillis, long endUtcMillis) {
2267d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        long current = System.currentTimeMillis();
2277d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (startUtcMillis > current || endUtcMillis <= current) {
2287d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            return LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE;
2297d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
2307d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        return (int)(100 * (current - startUtcMillis) / (endUtcMillis - startUtcMillis));
2317d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
2327d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
2337d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private String buildIntentData(long channelId) {
2347d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        return TvContract.buildChannelUri(channelId).buildUpon()
2357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                .appendQueryParameter(Utils.PARAM_SOURCE, SOURCE_TV_SEARCH)
2367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                .build().toString();
2377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
2387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
2397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private boolean isRatingBlocked(TvContentRating[] ratings) {
2407d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (ratings == null || ratings.length == 0
2417d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                || !mTvInputManager.isParentalControlsEnabled()) {
2427d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            return false;
2437d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
2447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        for (TvContentRating rating : ratings) {
2457d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            try {
2467d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                if (mTvInputManager.isRatingBlocked(rating)) {
2477d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    return true;
2487d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
2497d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            } catch (IllegalArgumentException e) {
2507d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                // Do nothing.
2517d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
2527d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
2537d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        return false;
2547d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
2557d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko}
256