SuggestionsProviderImpl.java revision 6d5cbd67f7a5f824babb5c892b0f30bfd9f4ff23
1/* 2 * Copyright (C) 2009 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.quicksearchbox; 18 19import com.android.quicksearchbox.util.BatchingNamedTaskExecutor; 20import com.android.quicksearchbox.util.Consumer; 21import com.android.quicksearchbox.util.NamedTaskExecutor; 22import com.android.quicksearchbox.util.Util; 23 24import android.os.Handler; 25import android.util.Log; 26 27import java.util.ArrayList; 28import java.util.List; 29import java.util.Set; 30 31/** 32 * Suggestions provider implementation. 33 * 34 * The provider will only handle a single query at a time. If a new query comes 35 * in, the old one is canceled. 36 */ 37public class SuggestionsProviderImpl implements SuggestionsProvider { 38 39 private static final boolean DBG = true; 40 private static final String TAG = "QSB.SuggestionsProviderImpl"; 41 42 private final Config mConfig; 43 44 private final NamedTaskExecutor mQueryExecutor; 45 46 private final Handler mPublishThread; 47 48 private final Promoter mPromoter; 49 50 private final ShortcutRepository mShortcutRepo; 51 52 private final Logger mLogger; 53 54 private final ShouldQueryStrategy mShouldQueryStrategy = new ShouldQueryStrategy(); 55 56 private BatchingNamedTaskExecutor mBatchingExecutor; 57 58 public SuggestionsProviderImpl(Config config, 59 NamedTaskExecutor queryExecutor, 60 Handler publishThread, 61 Promoter promoter, 62 ShortcutRepository shortcutRepo, 63 Logger logger) { 64 mConfig = config; 65 mQueryExecutor = queryExecutor; 66 mPublishThread = publishThread; 67 mPromoter = promoter; 68 mShortcutRepo = shortcutRepo; 69 mLogger = logger; 70 } 71 72 public void close() { 73 cancelPendingTasks(); 74 } 75 76 /** 77 * Cancels all pending query tasks. 78 */ 79 private void cancelPendingTasks() { 80 if (mBatchingExecutor != null) { 81 mBatchingExecutor.cancelPendingTasks(); 82 mBatchingExecutor = null; 83 } 84 } 85 86 protected SuggestionCursor getShortcutsForQuery(String query, List<Corpus> corpora, 87 int maxShortcuts) { 88 if (mShortcutRepo == null) return null; 89 return mShortcutRepo.getShortcutsForQuery(query, corpora, maxShortcuts); 90 } 91 92 /** 93 * Gets the sources that should be queried for the given query. 94 */ 95 private List<Corpus> getCorporaToQuery(String query, List<Corpus> orderedCorpora) { 96 ArrayList<Corpus> corporaToQuery = new ArrayList<Corpus>(orderedCorpora.size()); 97 for (Corpus corpus : orderedCorpora) { 98 if (shouldQueryCorpus(corpus, query)) { 99 corporaToQuery.add(corpus); 100 } 101 } 102 return corporaToQuery; 103 } 104 105 protected boolean shouldQueryCorpus(Corpus corpus, String query) { 106 if (query.length() == 0 && !corpus.isWebCorpus()) { 107 // Only the web corpus sees zero length queries. 108 return false; 109 } 110 return mShouldQueryStrategy.shouldQueryCorpus(corpus, query); 111 } 112 113 private void updateShouldQueryStrategy(CorpusResult cursor) { 114 if (cursor.getCount() == 0) { 115 mShouldQueryStrategy.onZeroResults(cursor.getCorpus(), 116 cursor.getUserQuery()); 117 } 118 } 119 120 public Suggestions getSuggestions(String query, List<Corpus> corpora, int maxSuggestions) { 121 if (DBG) Log.d(TAG, "getSuggestions(" + query + ")"); 122 cancelPendingTasks(); 123 List<Corpus> corporaToQuery = getCorporaToQuery(query, corpora); 124 int numPromotedSources = mConfig.getNumPromotedSources(); 125 Set<Corpus> promotedCorpora = Util.setOfFirstN(corporaToQuery, numPromotedSources); 126 final Suggestions suggestions = new Suggestions(mPromoter, 127 maxSuggestions, 128 query, 129 corporaToQuery.size(), 130 promotedCorpora); 131 int maxShortcuts = mConfig.getMaxShortcutsReturned(); 132 SuggestionCursor shortcuts = getShortcutsForQuery(query, corpora, maxShortcuts); 133 if (shortcuts != null) { 134 suggestions.setShortcuts(shortcuts); 135 } 136 137 // Fast path for the zero sources case 138 if (corporaToQuery.size() == 0) { 139 return suggestions; 140 } 141 142 mBatchingExecutor = new BatchingNamedTaskExecutor(mQueryExecutor, numPromotedSources); 143 144 SuggestionCursorReceiver receiver = new SuggestionCursorReceiver( 145 mBatchingExecutor, suggestions); 146 147 int maxResultsPerSource = mConfig.getMaxResultsPerSource(); 148 QueryTask.startQueries(query, maxResultsPerSource, corporaToQuery, mBatchingExecutor, 149 mPublishThread, receiver); 150 151 return suggestions; 152 } 153 154 private class SuggestionCursorReceiver implements Consumer<CorpusResult> { 155 private final BatchingNamedTaskExecutor mExecutor; 156 private final Suggestions mSuggestions; 157 158 public SuggestionCursorReceiver(BatchingNamedTaskExecutor executor, 159 Suggestions suggestions) { 160 mExecutor = executor; 161 mSuggestions = suggestions; 162 } 163 164 public boolean consume(CorpusResult cursor) { 165 updateShouldQueryStrategy(cursor); 166 mSuggestions.addCorpusResult(cursor); 167 if (!mSuggestions.isClosed()) { 168 executeNextBatchIfNeeded(); 169 } 170 if (cursor != null && mLogger != null) { 171 mLogger.logLatency(cursor); 172 } 173 return true; 174 } 175 176 private void executeNextBatchIfNeeded() { 177 if (mSuggestions.getSourceCount() % mConfig.getNumPromotedSources() == 0) { 178 // We've just finished one batch 179 if (mSuggestions.getPromoted().getCount() < mConfig.getMaxPromotedSuggestions()) { 180 // But we still don't have enough results, ask for more 181 mExecutor.executeNextBatch(); 182 } 183 } 184 } 185 } 186 187} 188