163f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee/* 2816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Copyright (C) 2015 The Android Open Source Project 363f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * 463f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * Licensed under the Apache License, Version 2.0 (the "License"); 563f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * you may not use this file except in compliance with the License. 663f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * You may obtain a copy of the License at 763f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * 863f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * http://www.apache.org/licenses/LICENSE-2.0 963f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * 1063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * Unless required by applicable law or agreed to in writing, software 1163f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * distributed under the License is distributed on an "AS IS" BASIS, 1263f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1363f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * See the License for the specific language governing permissions and 1463f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee * limitations under the License. 1563f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee */ 1663f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee 1763f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Leepackage com.android.tv.search; 1863f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee 19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.ContentResolver; 2063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Leeimport android.content.Context; 2163f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Leeimport android.content.Intent; 2263f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Leeimport android.database.Cursor; 23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContentRating; 24816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContract; 259035590d94d136a020e499ce720b9d2cf1f6f45cJae Seoimport android.media.tv.TvContract.Channels; 269035590d94d136a020e499ce720b9d2cf1f6f45cJae Seoimport android.media.tv.TvContract.Programs; 27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContract.WatchedPrograms; 28816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvInputInfo; 29816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvInputManager; 3063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Leeimport android.net.Uri; 31919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Choimport android.os.SystemClock; 3207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport android.support.annotation.WorkerThread; 33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.text.TextUtils; 34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log; 35ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.TvContentRatingCache; 36944779887775bd950cf1abf348d2df461593f6abLive Channels Teamimport com.android.tv.common.util.PermissionUtils; 37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.search.LocalSearchProvider.SearchResult; 38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.Utils; 3963f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Leeimport java.util.ArrayList; 40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Collections; 41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Comparator; 428294723aefc8341646c66f378a02098db95716deChulwoo Leeimport java.util.HashMap; 438294723aefc8341646c66f378a02098db95716deChulwoo Leeimport java.util.HashSet; 4463f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Leeimport java.util.List; 45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Locale; 468294723aefc8341646c66f378a02098db95716deChulwoo Leeimport java.util.Map; 47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Objects; 488294723aefc8341646c66f378a02098db95716deChulwoo Leeimport java.util.Set; 490645b17e2818b69b996da48fd93731bc8a01f114Live Channels Teamimport java.util.concurrent.TimeUnit; 5063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee 5195961816a768da387f0b5523cf4363ace2044089Nick Chalko/** An implementation of {@link SearchInterface} to search query from TvProvider directly. */ 527d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkopublic class TvProviderSearch implements SearchInterface { 53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final String TAG = "TvProviderSearch"; 54919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho private static final boolean DEBUG = false; 55a4e80f54bf945b6601982ee2a74f44fa3f132889Chulwoo Lee 560645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team private static final long SEARCH_TIME_FRAME_MS = TimeUnit.DAYS.toMillis(14); 570645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team 58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final int NO_LIMIT = 0; 59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Context mContext; 61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final ContentResolver mContentResolver; 62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final TvInputManager mTvInputManager; 63ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private final TvContentRatingCache mTvContentRatingCache = TvContentRatingCache.getInstance(); 64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TvProviderSearch(Context context) { 66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContext = context; 67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentResolver = context.getContentResolver(); 68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE); 69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 7295961816a768da387f0b5523cf4363ace2044089Nick Chalko * Search channels, inputs, or programs from TvProvider. This assumes that parental control 7395961816a768da387f0b5523cf4363ace2044089Nick Chalko * settings will not be change while searching. 74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @param action One of {@link #ACTION_TYPE_SWITCH_CHANNEL}, {@link #ACTION_TYPE_SWITCH_INPUT}, 7695961816a768da387f0b5523cf4363ace2044089Nick Chalko * or {@link #ACTION_TYPE_AMBIGUOUS}, 77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 787d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko @Override 7907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko @WorkerThread 80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public List<SearchResult> search(String query, int limit, int action) { 810645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team // TODO(b/72499463): add a test. 82816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<SearchResult> results = new ArrayList<>(); 837d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko if (!PermissionUtils.hasAccessAllEpg(mContext)) { 847d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko // TODO: support this feature for non-system LC app. b/23939816 857d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko return results; 867d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 87816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Set<Long> channelsFound = new HashSet<>(); 88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (action == ACTION_TYPE_SWITCH_CHANNEL) { 89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko results.addAll(searchChannels(query, channelsFound, limit)); 90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else if (action == ACTION_TYPE_SWITCH_INPUT) { 91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko results.addAll(searchInputs(query, limit)); 92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 93816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Search channels first. 94816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko results.addAll(searchChannels(query, channelsFound, limit)); 95816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (results.size() >= limit) { 96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return results; 97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 98816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 99816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // In case the user wanted to perform the action "switch to XXX", which is indicated by 100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // setting the limit to 1, search inputs. 101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (limit == 1) { 102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko results.addAll(searchInputs(query, limit)); 103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!results.isEmpty()) { 104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return results; 105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Lastly, search programs. 109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko limit -= results.size(); 11095961816a768da387f0b5523cf4363ace2044089Nick Chalko results.addAll( 11195961816a768da387f0b5523cf4363ace2044089Nick Chalko searchPrograms( 11295961816a768da387f0b5523cf4363ace2044089Nick Chalko query, 11395961816a768da387f0b5523cf4363ace2044089Nick Chalko null, 11495961816a768da387f0b5523cf4363ace2044089Nick Chalko new String[] {Programs.COLUMN_TITLE, Programs.COLUMN_SHORT_DESCRIPTION}, 11595961816a768da387f0b5523cf4363ace2044089Nick Chalko channelsFound, 11695961816a768da387f0b5523cf4363ace2044089Nick Chalko limit)); 117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 11863f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee return results; 11963f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee } 12063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee 12195961816a768da387f0b5523cf4363ace2044089Nick Chalko private void appendSelectionString( 12295961816a768da387f0b5523cf4363ace2044089Nick Chalko StringBuilder sb, String[] columnForExactMatching, String[] columnForPartialMatching) { 123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean firstColumn = true; 124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (columnForExactMatching != null) { 125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (String column : columnForExactMatching) { 126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!firstColumn) { 127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko sb.append(" OR "); 128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko firstColumn = false; 130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko sb.append(column).append("=?"); 132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (columnForPartialMatching != null) { 135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (String column : columnForPartialMatching) { 136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!firstColumn) { 137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko sb.append(" OR "); 138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko firstColumn = false; 140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko sb.append(column).append(" LIKE ?"); 142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 145816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 14695961816a768da387f0b5523cf4363ace2044089Nick Chalko private void insertSelectionArgumentStrings( 14795961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] selectionArgs, 14895961816a768da387f0b5523cf4363ace2044089Nick Chalko int pos, 14995961816a768da387f0b5523cf4363ace2044089Nick Chalko String query, 15095961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] columnForExactMatching, 15195961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] columnForPartialMatching) { 152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (columnForExactMatching != null) { 153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int until = pos + columnForExactMatching.length; 154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (; pos < until; ++pos) { 155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko selectionArgs[pos] = query; 156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String selectionArg = "%" + query + "%"; 159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (columnForPartialMatching != null) { 160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int until = pos + columnForPartialMatching.length; 161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (; pos < until; ++pos) { 162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko selectionArgs[pos] = selectionArg; 163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 16707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko @WorkerThread 168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private List<SearchResult> searchChannels(String query, Set<Long> channels, int limit) { 169919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho if (DEBUG) Log.d(TAG, "Searching channels: '" + query + "'"); 170919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho long time = SystemClock.elapsedRealtime(); 171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<SearchResult> results = new ArrayList<>(); 172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (TextUtils.isDigitsOnly(query)) { 17395961816a768da387f0b5523cf4363ace2044089Nick Chalko results.addAll( 17495961816a768da387f0b5523cf4363ace2044089Nick Chalko searchChannels( 17595961816a768da387f0b5523cf4363ace2044089Nick Chalko query, 17695961816a768da387f0b5523cf4363ace2044089Nick Chalko new String[] {Channels.COLUMN_DISPLAY_NUMBER}, 17795961816a768da387f0b5523cf4363ace2044089Nick Chalko null, 17895961816a768da387f0b5523cf4363ace2044089Nick Chalko channels, 17995961816a768da387f0b5523cf4363ace2044089Nick Chalko NO_LIMIT)); 180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (results.size() > 1) { 181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Collections.sort(results, new ChannelComparatorWithSameDisplayNumber()); 182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (results.size() < limit) { 18595961816a768da387f0b5523cf4363ace2044089Nick Chalko results.addAll( 18695961816a768da387f0b5523cf4363ace2044089Nick Chalko searchChannels( 18795961816a768da387f0b5523cf4363ace2044089Nick Chalko query, 18895961816a768da387f0b5523cf4363ace2044089Nick Chalko null, 18995961816a768da387f0b5523cf4363ace2044089Nick Chalko new String[] { 19095961816a768da387f0b5523cf4363ace2044089Nick Chalko Channels.COLUMN_DISPLAY_NAME, Channels.COLUMN_DESCRIPTION 19195961816a768da387f0b5523cf4363ace2044089Nick Chalko }, 19295961816a768da387f0b5523cf4363ace2044089Nick Chalko channels, 19395961816a768da387f0b5523cf4363ace2044089Nick Chalko limit - results.size())); 1948294723aefc8341646c66f378a02098db95716deChulwoo Lee } 195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (results.size() > limit) { 196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko results = results.subList(0, limit); 197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1980645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team for (int i = 0; i < results.size(); i++) { 1990645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team results.set(i, fillProgramInfo(results.get(i))); 200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 201919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho if (DEBUG) { 20295961816a768da387f0b5523cf4363ace2044089Nick Chalko Log.d( 20395961816a768da387f0b5523cf4363ace2044089Nick Chalko TAG, 20495961816a768da387f0b5523cf4363ace2044089Nick Chalko "Found " 20595961816a768da387f0b5523cf4363ace2044089Nick Chalko + results.size() 20695961816a768da387f0b5523cf4363ace2044089Nick Chalko + " channels. Elapsed time for searching" 20795961816a768da387f0b5523cf4363ace2044089Nick Chalko + " channels: " 20895961816a768da387f0b5523cf4363ace2044089Nick Chalko + (SystemClock.elapsedRealtime() - time) 20995961816a768da387f0b5523cf4363ace2044089Nick Chalko + "(msec)"); 210919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho } 211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return results; 2128294723aefc8341646c66f378a02098db95716deChulwoo Lee } 2138294723aefc8341646c66f378a02098db95716deChulwoo Lee 21407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko @WorkerThread 21595961816a768da387f0b5523cf4363ace2044089Nick Chalko private List<SearchResult> searchChannels( 21695961816a768da387f0b5523cf4363ace2044089Nick Chalko String query, 21795961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] columnForExactMatching, 21895961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] columnForPartialMatching, 21995961816a768da387f0b5523cf4363ace2044089Nick Chalko Set<Long> channelsFound, 22095961816a768da387f0b5523cf4363ace2044089Nick Chalko int limit) { 22163f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee String[] projection = { 22295961816a768da387f0b5523cf4363ace2044089Nick Chalko Channels._ID, 22395961816a768da387f0b5523cf4363ace2044089Nick Chalko Channels.COLUMN_DISPLAY_NUMBER, 22495961816a768da387f0b5523cf4363ace2044089Nick Chalko Channels.COLUMN_DISPLAY_NAME, 22595961816a768da387f0b5523cf4363ace2044089Nick Chalko Channels.COLUMN_DESCRIPTION 22663f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee }; 227a4e80f54bf945b6601982ee2a74f44fa3f132889Chulwoo Lee 2288294723aefc8341646c66f378a02098db95716deChulwoo Lee StringBuilder sb = new StringBuilder(); 22995961816a768da387f0b5523cf4363ace2044089Nick Chalko sb.append(Channels.COLUMN_BROWSABLE) 23095961816a768da387f0b5523cf4363ace2044089Nick Chalko .append("=1 AND ") 23195961816a768da387f0b5523cf4363ace2044089Nick Chalko .append(Channels.COLUMN_SEARCHABLE) 23295961816a768da387f0b5523cf4363ace2044089Nick Chalko .append("=1"); 233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mTvInputManager.isParentalControlsEnabled()) { 234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko sb.append(" AND ").append(Channels.COLUMN_LOCKED).append("=0"); 2358294723aefc8341646c66f378a02098db95716deChulwoo Lee } 236816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko sb.append(" AND ("); 237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko appendSelectionString(sb, columnForExactMatching, columnForPartialMatching); 2388294723aefc8341646c66f378a02098db95716deChulwoo Lee sb.append(")"); 2398294723aefc8341646c66f378a02098db95716deChulwoo Lee String selection = sb.toString(); 2408294723aefc8341646c66f378a02098db95716deChulwoo Lee 24195961816a768da387f0b5523cf4363ace2044089Nick Chalko int len = 24295961816a768da387f0b5523cf4363ace2044089Nick Chalko (columnForExactMatching == null ? 0 : columnForExactMatching.length) 24395961816a768da387f0b5523cf4363ace2044089Nick Chalko + (columnForPartialMatching == null ? 0 : columnForPartialMatching.length); 244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String[] selectionArgs = new String[len]; 24595961816a768da387f0b5523cf4363ace2044089Nick Chalko insertSelectionArgumentStrings( 24695961816a768da387f0b5523cf4363ace2044089Nick Chalko selectionArgs, 0, query, columnForExactMatching, columnForPartialMatching); 247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<SearchResult> searchResults = new ArrayList<>(); 249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 25095961816a768da387f0b5523cf4363ace2044089Nick Chalko try (Cursor c = 25195961816a768da387f0b5523cf4363ace2044089Nick Chalko mContentResolver.query( 25295961816a768da387f0b5523cf4363ace2044089Nick Chalko Channels.CONTENT_URI, projection, selection, selectionArgs, null)) { 253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (c != null) { 254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int count = 0; 255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (c.moveToNext()) { 256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long id = c.getLong(0); 257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Filter out the channel which has been already searched. 258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelsFound.contains(id)) { 259816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko continue; 260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 261816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelsFound.add(id); 262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 2630645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team SearchResult.Builder result = SearchResult.builder(); 2640645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setChannelId(id); 2650645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setChannelNumber(c.getString(1)); 2660645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setTitle(c.getString(2)); 2670645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setDescription(c.getString(3)); 2680645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setImageUri(TvContract.buildChannelLogoUri(id).toString()); 2690645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIntentAction(Intent.ACTION_VIEW); 2700645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIntentData(buildIntentData(id)); 2710645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setContentType(Programs.CONTENT_ITEM_TYPE); 2720645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIsLive(true); 2730645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setProgressPercentage(LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE); 274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 2750645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team searchResults.add(result.build()); 276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (limit != NO_LIMIT && ++count >= limit) { 278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return searchResults; 284816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 285816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Replaces the channel information - title, description, channel logo - with the current 288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * program information of the channel if the current program information exists and it is not 289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * blocked. 290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 29107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko @WorkerThread 2920645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team private SearchResult fillProgramInfo(SearchResult result) { 293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long now = System.currentTimeMillis(); 2940645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team Uri uri = TvContract.buildProgramsUriForChannel(result.getChannelId(), now, now); 29595961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] projection = 29695961816a768da387f0b5523cf4363ace2044089Nick Chalko new String[] { 29795961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_TITLE, 29895961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_POSTER_ART_URI, 29995961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_CONTENT_RATING, 30095961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_VIDEO_WIDTH, 30195961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_VIDEO_HEIGHT, 30295961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_START_TIME_UTC_MILLIS, 30395961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_END_TIME_UTC_MILLIS 30495961816a768da387f0b5523cf4363ace2044089Nick Chalko }; 305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko try (Cursor c = mContentResolver.query(uri, projection, null, null, null)) { 307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (c != null && c.moveToNext() && !isRatingBlocked(c.getString(2))) { 3080645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team String channelName = result.getTitle(); 3090645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team String channelNumber = result.getChannelNumber(); 3100645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team SearchResult.Builder builder = SearchResult.builder(); 311816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startUtcMillis = c.getLong(5); 312816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long endUtcMillis = c.getLong(6); 3130645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team builder.setTitle(c.getString(0)); 3140645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team builder.setDescription( 31595961816a768da387f0b5523cf4363ace2044089Nick Chalko buildProgramDescription( 3160645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team channelNumber, channelName, startUtcMillis, endUtcMillis)); 317816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String imageUri = c.getString(1); 318816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (imageUri != null) { 3190645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team builder.setImageUri(imageUri); 320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 3210645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team builder.setVideoWidth(c.getInt(3)); 3220645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team builder.setVideoHeight(c.getInt(4)); 3230645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team builder.setDuration(endUtcMillis - startUtcMillis); 3240645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team builder.setProgressPercentage(getProgressPercentage(startUtcMillis, endUtcMillis)); 3250645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team return builder.build(); 326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 3278294723aefc8341646c66f378a02098db95716deChulwoo Lee } 3280645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team return result; 329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 33195961816a768da387f0b5523cf4363ace2044089Nick Chalko private String buildProgramDescription( 33295961816a768da387f0b5523cf4363ace2044089Nick Chalko String channelNumber, 33395961816a768da387f0b5523cf4363ace2044089Nick Chalko String channelName, 33495961816a768da387f0b5523cf4363ace2044089Nick Chalko long programStartUtcMillis, 33595961816a768da387f0b5523cf4363ace2044089Nick Chalko long programEndUtcMillis) { 336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return Utils.getDurationString(mContext, programStartUtcMillis, programEndUtcMillis, false) 33795961816a768da387f0b5523cf4363ace2044089Nick Chalko + System.lineSeparator() 33895961816a768da387f0b5523cf4363ace2044089Nick Chalko + channelNumber 33995961816a768da387f0b5523cf4363ace2044089Nick Chalko + " " 34095961816a768da387f0b5523cf4363ace2044089Nick Chalko + channelName; 341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 3428294723aefc8341646c66f378a02098db95716deChulwoo Lee 343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private int getProgressPercentage(long startUtcMillis, long endUtcMillis) { 344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long current = System.currentTimeMillis(); 345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (startUtcMillis > current || endUtcMillis <= current) { 346816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE; 347816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 34895961816a768da387f0b5523cf4363ace2044089Nick Chalko return (int) (100 * (current - startUtcMillis) / (endUtcMillis - startUtcMillis)); 34963f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee } 35063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee 35107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko @WorkerThread 35295961816a768da387f0b5523cf4363ace2044089Nick Chalko private List<SearchResult> searchPrograms( 35395961816a768da387f0b5523cf4363ace2044089Nick Chalko String query, 35495961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] columnForExactMatching, 35595961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] columnForPartialMatching, 35695961816a768da387f0b5523cf4363ace2044089Nick Chalko Set<Long> channelsFound, 35795961816a768da387f0b5523cf4363ace2044089Nick Chalko int limit) { 358919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho if (DEBUG) Log.d(TAG, "Searching programs: '" + query + "'"); 359919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho long time = SystemClock.elapsedRealtime(); 36063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee String[] projection = { 36195961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_CHANNEL_ID, 36295961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_TITLE, 36395961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_POSTER_ART_URI, 36495961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_CONTENT_RATING, 36595961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_VIDEO_WIDTH, 36695961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_VIDEO_HEIGHT, 36795961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.COLUMN_START_TIME_UTC_MILLIS, 3680645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team Programs.COLUMN_END_TIME_UTC_MILLIS, 3690645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team Programs._ID 37063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee }; 371a4e80f54bf945b6601982ee2a74f44fa3f132889Chulwoo Lee 3728294723aefc8341646c66f378a02098db95716deChulwoo Lee StringBuilder sb = new StringBuilder(); 3738294723aefc8341646c66f378a02098db95716deChulwoo Lee // Search among the programs which are now being on the air. 3748294723aefc8341646c66f378a02098db95716deChulwoo Lee sb.append(Programs.COLUMN_START_TIME_UTC_MILLIS).append("<=? AND "); 3758294723aefc8341646c66f378a02098db95716deChulwoo Lee sb.append(Programs.COLUMN_END_TIME_UTC_MILLIS).append(">=? AND ("); 376816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko appendSelectionString(sb, columnForExactMatching, columnForPartialMatching); 3778294723aefc8341646c66f378a02098db95716deChulwoo Lee sb.append(")"); 37863f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee String selection = sb.toString(); 379816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 38095961816a768da387f0b5523cf4363ace2044089Nick Chalko int len = 38195961816a768da387f0b5523cf4363ace2044089Nick Chalko (columnForExactMatching == null ? 0 : columnForExactMatching.length) 38295961816a768da387f0b5523cf4363ace2044089Nick Chalko + (columnForPartialMatching == null ? 0 : columnForPartialMatching.length); 383816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String[] selectionArgs = new String[len + 2]; 3840645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team long now = System.currentTimeMillis(); 3850645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team selectionArgs[0] = String.valueOf(now + SEARCH_TIME_FRAME_MS); 3860645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team selectionArgs[1] = String.valueOf(now); 38795961816a768da387f0b5523cf4363ace2044089Nick Chalko insertSelectionArgumentStrings( 38895961816a768da387f0b5523cf4363ace2044089Nick Chalko selectionArgs, 2, query, columnForExactMatching, columnForPartialMatching); 38963f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee 390816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<SearchResult> searchResults = new ArrayList<>(); 3918294723aefc8341646c66f378a02098db95716deChulwoo Lee 39295961816a768da387f0b5523cf4363ace2044089Nick Chalko try (Cursor c = 39395961816a768da387f0b5523cf4363ace2044089Nick Chalko mContentResolver.query( 39495961816a768da387f0b5523cf4363ace2044089Nick Chalko Programs.CONTENT_URI, projection, selection, selectionArgs, null)) { 395816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (c != null) { 396816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int count = 0; 397816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (c.moveToNext()) { 398816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long id = c.getLong(0); 399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Filter out the program whose channel is already searched. 400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channelsFound.contains(id)) { 401816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko continue; 402816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 403816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelsFound.add(id); 4048294723aefc8341646c66f378a02098db95716deChulwoo Lee 405816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Don't know whether the channel is searchable or not. 406816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String[] channelProjection = { 40795961816a768da387f0b5523cf4363ace2044089Nick Chalko Channels._ID, Channels.COLUMN_DISPLAY_NUMBER, Channels.COLUMN_DISPLAY_NAME 408816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko }; 409816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko sb = new StringBuilder(); 41095961816a768da387f0b5523cf4363ace2044089Nick Chalko sb.append(Channels._ID) 41195961816a768da387f0b5523cf4363ace2044089Nick Chalko .append("=? AND ") 41295961816a768da387f0b5523cf4363ace2044089Nick Chalko .append(Channels.COLUMN_BROWSABLE) 41395961816a768da387f0b5523cf4363ace2044089Nick Chalko .append("=1 AND ") 41495961816a768da387f0b5523cf4363ace2044089Nick Chalko .append(Channels.COLUMN_SEARCHABLE) 41595961816a768da387f0b5523cf4363ace2044089Nick Chalko .append("=1"); 416816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mTvInputManager.isParentalControlsEnabled()) { 417816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko sb.append(" AND ").append(Channels.COLUMN_LOCKED).append("=0"); 418816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 419816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String selectionChannel = sb.toString(); 42095961816a768da387f0b5523cf4363ace2044089Nick Chalko try (Cursor cChannel = 42195961816a768da387f0b5523cf4363ace2044089Nick Chalko mContentResolver.query( 42295961816a768da387f0b5523cf4363ace2044089Nick Chalko Channels.CONTENT_URI, 42395961816a768da387f0b5523cf4363ace2044089Nick Chalko channelProjection, 42495961816a768da387f0b5523cf4363ace2044089Nick Chalko selectionChannel, 42595961816a768da387f0b5523cf4363ace2044089Nick Chalko new String[] {String.valueOf(id)}, 42695961816a768da387f0b5523cf4363ace2044089Nick Chalko null)) { 42795961816a768da387f0b5523cf4363ace2044089Nick Chalko if (cChannel != null 42895961816a768da387f0b5523cf4363ace2044089Nick Chalko && cChannel.moveToNext() 429816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko && !isRatingBlocked(c.getString(3))) { 430816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startUtcMillis = c.getLong(6); 431816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long endUtcMillis = c.getLong(7); 4320645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team SearchResult.Builder result = SearchResult.builder(); 4330645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setChannelId(c.getLong(0)); 4340645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setTitle(c.getString(1)); 4350645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setDescription( 43695961816a768da387f0b5523cf4363ace2044089Nick Chalko buildProgramDescription( 43795961816a768da387f0b5523cf4363ace2044089Nick Chalko cChannel.getString(1), 43895961816a768da387f0b5523cf4363ace2044089Nick Chalko cChannel.getString(2), 43995961816a768da387f0b5523cf4363ace2044089Nick Chalko startUtcMillis, 4400645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team endUtcMillis)); 4410645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setImageUri(c.getString(2)); 4420645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIntentAction(Intent.ACTION_VIEW); 4430645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIntentData(buildIntentData(id)); 4440645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIntentExtraData( 4450645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team TvContract.buildProgramUri(c.getLong(8)).toString()); 4460645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setContentType(Programs.CONTENT_ITEM_TYPE); 4470645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIsLive(true); 4480645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setVideoWidth(c.getInt(4)); 4490645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setVideoHeight(c.getInt(5)); 4500645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setDuration(endUtcMillis - startUtcMillis); 4510645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setProgressPercentage( 4520645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team getProgressPercentage(startUtcMillis, endUtcMillis)); 4530645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team searchResults.add(result.build()); 454816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 455816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (limit != NO_LIMIT && ++count >= limit) { 456816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 4578294723aefc8341646c66f378a02098db95716deChulwoo Lee } 4588294723aefc8341646c66f378a02098db95716deChulwoo Lee } 4598294723aefc8341646c66f378a02098db95716deChulwoo Lee } 460816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 461816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 462816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 463919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho if (DEBUG) { 46495961816a768da387f0b5523cf4363ace2044089Nick Chalko Log.d( 46595961816a768da387f0b5523cf4363ace2044089Nick Chalko TAG, 46695961816a768da387f0b5523cf4363ace2044089Nick Chalko "Found " 46795961816a768da387f0b5523cf4363ace2044089Nick Chalko + searchResults.size() 46895961816a768da387f0b5523cf4363ace2044089Nick Chalko + " programs. Elapsed time for searching" 46995961816a768da387f0b5523cf4363ace2044089Nick Chalko + " programs: " 47095961816a768da387f0b5523cf4363ace2044089Nick Chalko + (SystemClock.elapsedRealtime() - time) 47195961816a768da387f0b5523cf4363ace2044089Nick Chalko + "(msec)"); 472919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho } 473816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return searchResults; 4748294723aefc8341646c66f378a02098db95716deChulwoo Lee } 4758294723aefc8341646c66f378a02098db95716deChulwoo Lee 476816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private String buildIntentData(long channelId) { 477633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko return TvContract.buildChannelUri(channelId).toString(); 478816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 4798294723aefc8341646c66f378a02098db95716deChulwoo Lee 480816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private boolean isRatingBlocked(String ratings) { 481ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (TextUtils.isEmpty(ratings) || !mTvInputManager.isParentalControlsEnabled()) { 482816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return false; 483816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 484ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko TvContentRating[] ratingArray = mTvContentRatingCache.getRatings(ratings); 485ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (ratingArray != null) { 486ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko for (TvContentRating r : ratingArray) { 487ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (mTvInputManager.isRatingBlocked(r)) { 488816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return true; 48963f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee } 49063f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee } 491816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 492816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return false; 493816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 494816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 495816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private List<SearchResult> searchInputs(String query, int limit) { 496919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho if (DEBUG) Log.d(TAG, "Searching inputs: '" + query + "'"); 497919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho long time = SystemClock.elapsedRealtime(); 498816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 499816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko query = canonicalizeLabel(query); 500816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<TvInputInfo> inputList = mTvInputManager.getTvInputList(); 501816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<SearchResult> results = new ArrayList<>(); 502816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 503816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Find exact matches first. 504816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (TvInputInfo input : inputList) { 505633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko if (input.getType() == TvInputInfo.TYPE_TUNER) { 506633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko continue; 507633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko } 508816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String label = canonicalizeLabel(input.loadLabel(mContext)); 509816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String customLabel = canonicalizeLabel(input.loadCustomLabel(mContext)); 510816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (TextUtils.equals(query, label) || TextUtils.equals(query, customLabel)) { 511816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko results.add(buildSearchResultForInput(input.getId())); 512816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (results.size() >= limit) { 513919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho if (DEBUG) { 51495961816a768da387f0b5523cf4363ace2044089Nick Chalko Log.d( 51595961816a768da387f0b5523cf4363ace2044089Nick Chalko TAG, 51695961816a768da387f0b5523cf4363ace2044089Nick Chalko "Found " 51795961816a768da387f0b5523cf4363ace2044089Nick Chalko + results.size() 51895961816a768da387f0b5523cf4363ace2044089Nick Chalko + " inputs. Elapsed time for" 51995961816a768da387f0b5523cf4363ace2044089Nick Chalko + " searching inputs: " 52095961816a768da387f0b5523cf4363ace2044089Nick Chalko + (SystemClock.elapsedRealtime() - time) 52195961816a768da387f0b5523cf4363ace2044089Nick Chalko + "(msec)"); 522919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho } 523816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return results; 524816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 52563f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee } 52663f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee } 52763f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee 528816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Then look for partial matches. 529816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (TvInputInfo input : inputList) { 530633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko if (input.getType() == TvInputInfo.TYPE_TUNER) { 531633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko continue; 532633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko } 533816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String label = canonicalizeLabel(input.loadLabel(mContext)); 534816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String customLabel = canonicalizeLabel(input.loadCustomLabel(mContext)); 53595961816a768da387f0b5523cf4363ace2044089Nick Chalko if ((label != null && label.contains(query)) 53695961816a768da387f0b5523cf4363ace2044089Nick Chalko || (customLabel != null && customLabel.contains(query))) { 537816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko results.add(buildSearchResultForInput(input.getId())); 538816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (results.size() >= limit) { 539919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho if (DEBUG) { 54095961816a768da387f0b5523cf4363ace2044089Nick Chalko Log.d( 54195961816a768da387f0b5523cf4363ace2044089Nick Chalko TAG, 54295961816a768da387f0b5523cf4363ace2044089Nick Chalko "Found " 54395961816a768da387f0b5523cf4363ace2044089Nick Chalko + results.size() 54495961816a768da387f0b5523cf4363ace2044089Nick Chalko + " inputs. Elapsed time for" 54595961816a768da387f0b5523cf4363ace2044089Nick Chalko + " searching inputs: " 54695961816a768da387f0b5523cf4363ace2044089Nick Chalko + (SystemClock.elapsedRealtime() - time) 54795961816a768da387f0b5523cf4363ace2044089Nick Chalko + "(msec)"); 548919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho } 549816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return results; 550816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 551816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 552816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 553919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho if (DEBUG) { 55495961816a768da387f0b5523cf4363ace2044089Nick Chalko Log.d( 55595961816a768da387f0b5523cf4363ace2044089Nick Chalko TAG, 55695961816a768da387f0b5523cf4363ace2044089Nick Chalko "Found " 55795961816a768da387f0b5523cf4363ace2044089Nick Chalko + results.size() 55895961816a768da387f0b5523cf4363ace2044089Nick Chalko + " inputs. Elapsed time for searching" 55995961816a768da387f0b5523cf4363ace2044089Nick Chalko + " inputs: " 56095961816a768da387f0b5523cf4363ace2044089Nick Chalko + (SystemClock.elapsedRealtime() - time) 56195961816a768da387f0b5523cf4363ace2044089Nick Chalko + "(msec)"); 562919e1ed7e914029a1a0054237d86dc7b19ced898Youngsang Cho } 56363f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee return results; 56463f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee } 5658294723aefc8341646c66f378a02098db95716deChulwoo Lee 566816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private String canonicalizeLabel(CharSequence cs) { 567816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Locale locale = mContext.getResources().getConfiguration().locale; 568816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return cs != null ? cs.toString().replaceAll("[ -]", "").toLowerCase(locale) : null; 569816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 570816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 571816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private SearchResult buildSearchResultForInput(String inputId) { 5720645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team SearchResult.Builder result = SearchResult.builder(); 5730645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIntentAction(Intent.ACTION_VIEW); 5740645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team result.setIntentData(TvContract.buildChannelUriForPassthroughInput(inputId).toString()); 5750645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team return result.build(); 576816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 577816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 57807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko @WorkerThread 579816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class ChannelComparatorWithSameDisplayNumber implements Comparator<SearchResult> { 580816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Map<Long, Long> mMaxWatchStartTimeMap = new HashMap<>(); 581816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 582816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 583816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public int compare(SearchResult lhs, SearchResult rhs) { 584816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Show recently watched channel first 5850645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team Long lhsMaxWatchStartTime = mMaxWatchStartTimeMap.get(lhs.getChannelId()); 586816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (lhsMaxWatchStartTime == null) { 5870645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team lhsMaxWatchStartTime = getMaxWatchStartTime(lhs.getChannelId()); 5880645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team mMaxWatchStartTimeMap.put(lhs.getChannelId(), lhsMaxWatchStartTime); 589816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 5900645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team Long rhsMaxWatchStartTime = mMaxWatchStartTimeMap.get(rhs.getChannelId()); 591816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (rhsMaxWatchStartTime == null) { 5920645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team rhsMaxWatchStartTime = getMaxWatchStartTime(rhs.getChannelId()); 5930645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team mMaxWatchStartTimeMap.put(rhs.getChannelId(), rhsMaxWatchStartTime); 594816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 595816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!Objects.equals(lhsMaxWatchStartTime, rhsMaxWatchStartTime)) { 596816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return Long.compare(rhsMaxWatchStartTime, lhsMaxWatchStartTime); 597816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 598816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Show recently added channel first if there's no watch history. 5990645b17e2818b69b996da48fd93731bc8a01f114Live Channels Team return Long.compare(rhs.getChannelId(), lhs.getChannelId()); 600816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 601816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 602816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private long getMaxWatchStartTime(long channelId) { 603816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Uri uri = WatchedPrograms.CONTENT_URI; 60495961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] projections = 60595961816a768da387f0b5523cf4363ace2044089Nick Chalko new String[] { 60695961816a768da387f0b5523cf4363ace2044089Nick Chalko "MAX(" 60795961816a768da387f0b5523cf4363ace2044089Nick Chalko + WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS 60895961816a768da387f0b5523cf4363ace2044089Nick Chalko + ") AS max_watch_start_time" 60995961816a768da387f0b5523cf4363ace2044089Nick Chalko }; 610816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String selection = WatchedPrograms.COLUMN_CHANNEL_ID + "=?"; 61195961816a768da387f0b5523cf4363ace2044089Nick Chalko String[] selectionArgs = new String[] {Long.toString(channelId)}; 61295961816a768da387f0b5523cf4363ace2044089Nick Chalko try (Cursor c = 61395961816a768da387f0b5523cf4363ace2044089Nick Chalko mContentResolver.query(uri, projections, selection, selectionArgs, null)) { 614816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (c != null && c.moveToNext()) { 615816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return c.getLong(0); 616816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 617816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 618816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return -1; 619816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 6208294723aefc8341646c66f378a02098db95716deChulwoo Lee } 62163f6ab4e8240eef8b078651f2e7b3c92ff5f196bChulwoo Lee} 622