1f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert/* 2f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * Copyright (C) 2010 The Android Open Source Project 3f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * 4f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); 5f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * you may not use this file except in compliance with the License. 6f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * You may obtain a copy of the License at 7f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * 8f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0 9f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * 10f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * Unless required by applicable law or agreed to in writing, software 11f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS, 12f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * See the License for the specific language governing permissions and 14f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * limitations under the License. 15f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert */ 16f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 17f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringertpackage com.android.quicksearchbox; 18f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 19f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 20f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringertimport com.android.quicksearchbox.util.BarrierConsumer; 21f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 22f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringertimport android.content.Context; 23f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 24f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringertimport java.util.ArrayList; 25f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringertimport java.util.Collection; 2613ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringertimport java.util.List; 2772f9b08ce84d0e13daf2d1c112d4e6d1d3ada045Bjorn Bringertimport java.util.concurrent.Executor; 28f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 29f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert/** 30f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * Base class for corpora backed by multiple sources. 31f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert */ 32f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringertpublic abstract class MultiSourceCorpus extends AbstractCorpus { 33f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 3472f9b08ce84d0e13daf2d1c112d4e6d1d3ada045Bjorn Bringert private final Executor mExecutor; 35f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 36f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert private final ArrayList<Source> mSources; 37f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 383cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood // calculated values based on properties of sources: 393cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood private boolean mSourcePropertiesValid; 403cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood private int mQueryThreshold; 413cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood private boolean mQueryAfterZeroResults; 423cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood private boolean mVoiceSearchEnabled; 43f3f70e5ae88f06ff6dabdec9e7c71a19ca1e7108Bjorn Bringert private boolean mIncludeInAll; 443cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood 4596c7058210699c82445169048b7c0fdfb16f59eeBjorn Bringert public MultiSourceCorpus(Context context, Config config, 4696c7058210699c82445169048b7c0fdfb16f59eeBjorn Bringert Executor executor, Source... sources) { 4796c7058210699c82445169048b7c0fdfb16f59eeBjorn Bringert super(context, config); 48f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert mExecutor = executor; 49f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 50f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert mSources = new ArrayList<Source>(); 51f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert for (Source source : sources) { 52c9cdac4f63e717208539ff9be5a282a5eacd2429Mathew Inwood addSource(source); 53c9cdac4f63e717208539ff9be5a282a5eacd2429Mathew Inwood } 543cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood 55c9cdac4f63e717208539ff9be5a282a5eacd2429Mathew Inwood } 56c9cdac4f63e717208539ff9be5a282a5eacd2429Mathew Inwood 57c9cdac4f63e717208539ff9be5a282a5eacd2429Mathew Inwood protected void addSource(Source source) { 58c9cdac4f63e717208539ff9be5a282a5eacd2429Mathew Inwood if (source != null) { 59c9cdac4f63e717208539ff9be5a282a5eacd2429Mathew Inwood mSources.add(source); 603cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood // invalidate calculated values: 613cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mSourcePropertiesValid = false; 62f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 63f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 64f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 65f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert public Collection<Source> getSources() { 66f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert return mSources; 67f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 68f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 69f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert /** 70f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * Creates a corpus result object for a set of source results. 71f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * This method should not call {@link Result#fill}. 72f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * 73f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * @param query The query text. 74f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * @param results The results of the queries. 75f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert * @param latency Latency in milliseconds of the suggestion queries. 76f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * @return An instance of {@link Result} or a subclass of it. 77f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert */ 78f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert protected Result createResult(String query, ArrayList<SourceResult> results, int latency) { 79f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert return new Result(query, results, latency); 8072f9b08ce84d0e13daf2d1c112d4e6d1d3ada045Bjorn Bringert } 81f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 8213ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringert /** 83cd1e3ba5f7c3f5242345ff6f674281e3d6366e24Mathew Inwood * Gets the sources to query for suggestions for the given input. 8413ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringert * 8513ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringert * @param query The current input. 86cd1e3ba5f7c3f5242345ff6f674281e3d6366e24Mathew Inwood * @param onlyCorpus If true, this is the only corpus being queried. 8713ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringert * @return The sources to query. 8813ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringert */ 89cd1e3ba5f7c3f5242345ff6f674281e3d6366e24Mathew Inwood protected List<Source> getSourcesToQuery(String query, boolean onlyCorpus) { 903cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood List<Source> sources = new ArrayList<Source>(); 913cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood for (Source candidate : getSources()) { 923cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood if (candidate.getQueryThreshold() <= query.length()) { 933cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood sources.add(candidate); 943cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood } 953cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood } 963cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood return sources; 973cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood } 983cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood 9949fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert private void updateSourceProperties() { 10049fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert if (mSourcePropertiesValid) return; 1013cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mQueryThreshold = Integer.MAX_VALUE; 1023cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mQueryAfterZeroResults = false; 1033cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mVoiceSearchEnabled = false; 104f3f70e5ae88f06ff6dabdec9e7c71a19ca1e7108Bjorn Bringert mIncludeInAll = false; 1053cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood for (Source s : getSources()) { 1063cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mQueryThreshold = Math.min(mQueryThreshold, s.getQueryThreshold()); 1073cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mQueryAfterZeroResults |= s.queryAfterZeroResults(); 1083cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mVoiceSearchEnabled |= s.voiceSearchEnabled(); 109f3f70e5ae88f06ff6dabdec9e7c71a19ca1e7108Bjorn Bringert mIncludeInAll |= s.includeInAll(); 1103cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood } 1113cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood if (mQueryThreshold == Integer.MAX_VALUE) { 1123cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mQueryThreshold = 0; 1133cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood } 1143cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood mSourcePropertiesValid = true; 1153cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood } 1163cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood 1173cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood public int getQueryThreshold() { 11849fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert updateSourceProperties(); 1193cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood return mQueryThreshold; 1203cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood } 1213cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood 1223cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood public boolean queryAfterZeroResults() { 12349fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert updateSourceProperties(); 1243cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood return mQueryAfterZeroResults; 1253cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood } 1263cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood 1273cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood public boolean voiceSearchEnabled() { 12849fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert updateSourceProperties(); 1293cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood return mVoiceSearchEnabled; 13013ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringert } 13113ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringert 132f3f70e5ae88f06ff6dabdec9e7c71a19ca1e7108Bjorn Bringert public boolean includeInAll() { 13349fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert updateSourceProperties(); 134f3f70e5ae88f06ff6dabdec9e7c71a19ca1e7108Bjorn Bringert return mIncludeInAll; 13549fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert } 13649fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert 137cd1e3ba5f7c3f5242345ff6f674281e3d6366e24Mathew Inwood public CorpusResult getSuggestions(String query, int queryLimit, boolean onlyCorpus) { 138f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert LatencyTracker latencyTracker = new LatencyTracker(); 139cd1e3ba5f7c3f5242345ff6f674281e3d6366e24Mathew Inwood List<Source> sources = getSourcesToQuery(query, onlyCorpus); 140f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert BarrierConsumer<SourceResult> consumer = 14113ace1d0d0fa7e4c7aa7898a828763d8880db463Bjorn Bringert new BarrierConsumer<SourceResult>(sources.size()); 142cd1e3ba5f7c3f5242345ff6f674281e3d6366e24Mathew Inwood boolean onlySource = sources.size() == 1; 14372f9b08ce84d0e13daf2d1c112d4e6d1d3ada045Bjorn Bringert for (Source source : sources) { 14472f9b08ce84d0e13daf2d1c112d4e6d1d3ada045Bjorn Bringert QueryTask<SourceResult> task = new QueryTask<SourceResult>(query, queryLimit, 145cd1e3ba5f7c3f5242345ff6f674281e3d6366e24Mathew Inwood source, null, consumer, onlySource); 14672f9b08ce84d0e13daf2d1c112d4e6d1d3ada045Bjorn Bringert mExecutor.execute(task); 14772f9b08ce84d0e13daf2d1c112d4e6d1d3ada045Bjorn Bringert } 148f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert ArrayList<SourceResult> results = consumer.getValues(); 149f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert int latency = latencyTracker.getLatency(); 150f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert Result result = createResult(query, results, latency); 151f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert result.fill(); 152f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert return result; 153f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 154f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 155f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert /** 156f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * Base class for results returned by {@link MultiSourceCorpus#getSuggestions}. 157f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * Subclasses of {@link MultiSourceCorpus} should override 158f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * {@link MultiSourceCorpus#createResult} and return an instance of this class or a 159f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * subclass. 160f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert */ 161f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert protected class Result extends ListSuggestionCursor implements CorpusResult { 162f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 163f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert private final ArrayList<SourceResult> mResults; 164f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 165f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert private final int mLatency; 166f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert 167f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert public Result(String userQuery, ArrayList<SourceResult> results, int latency) { 168f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert super(userQuery); 169f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert mResults = results; 170f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert mLatency = latency; 171f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 172f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 173f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert protected ArrayList<SourceResult> getResults() { 174f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert return mResults; 175f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 176f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 177f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert /** 178f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * Fills the list of suggestions using the list of results. 179f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert * The default implementation concatenates the results. 180f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert */ 181f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert public void fill() { 182f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert for (SourceResult result : getResults()) { 183f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert int count = result.getCount(); 184f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert for (int i = 0; i < count; i++) { 185f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert result.moveTo(i); 186f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert add(new SuggestionPosition(result)); 187f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 188f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 189f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 190f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 191f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert public Corpus getCorpus() { 192f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert return MultiSourceCorpus.this; 193f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 194f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 195f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert public int getLatency() { 196f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert return mLatency; 197f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert } 198f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert 199f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert @Override 200f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert public void close() { 201f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert super.close(); 202f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert for (SourceResult result : mResults) { 203f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert result.close(); 204f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 205f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 206bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert 207bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert @Override 208bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert public String toString() { 209848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani return "{" + getCorpus() + "[" + getUserQuery() + "]" + ";n=" + getCount() + "}"; 210bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert } 211f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert } 212f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert 213f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert} 214