SuggestionsProviderImpl.java revision 0484fb4d652bfa9d5c7fb238a7cec1a6f2244e44
10484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert/*
20484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * Copyright (C) 2009 The Android Open Source Project
30484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert *
40484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
50484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * you may not use this file except in compliance with the License.
60484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * You may obtain a copy of the License at
70484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert *
80484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
90484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert *
100484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
110484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
120484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * See the License for the specific language governing permissions and
140484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * limitations under the License.
150484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert */
160484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
170484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringertpackage com.android.quicksearchbox;
180484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
190484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringertimport android.os.Handler;
200484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringertimport android.util.Log;
210484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
220484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringertimport java.util.ArrayList;
230484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
240484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert/**
250484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * Common suggestions provider base class.
260484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert *
270484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * The provider will only handle a single query at a time. If a new query comes
280484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert * in, the old one is canceled.
290484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert */
300484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringertpublic abstract class AbstractSuggestionsProvider implements SuggestionsProvider {
310484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
320484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static final boolean DBG = true;
330484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static final String TAG = "QSB.AbstractSuggestionsProvider";
340484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
350484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private final Config mConfig;
360484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
370484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private final SourceTaskExecutor mQueryExecutor;
380484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
390484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private final Handler mPublishThread;
400484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
410484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private final Promoter mPromoter;
420484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
430484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    public AbstractSuggestionsProvider(Config config,
440484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            SourceTaskExecutor queryExecutor,
450484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            Handler publishThread,
460484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            Promoter promoter) {
470484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        mConfig = config;
480484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        mQueryExecutor = queryExecutor;
490484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        mPublishThread = publishThread;
500484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        mPromoter = promoter;
510484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
520484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
530484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    public void close() {
540484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        cancelPendingTasks();
550484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
560484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
570484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    /**
580484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert     * Cancels all pending query tasks.
590484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert     */
600484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private void cancelPendingTasks() {
610484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        mQueryExecutor.cancelPendingTasks();
620484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
630484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
640484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    public abstract ArrayList<Source> getOrderedSources();
650484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
660484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    protected abstract SuggestionCursor getShortcutsForQuery(String query);
670484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
680484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    /**
690484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert     * Gets the sources that should be queried for the given query.
700484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert     *
710484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert     */
720484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private ArrayList<Source> getSourcesToQuery(String query) {
730484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        if (query.length() == 0) {
740484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            return new ArrayList<Source>();
750484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
760484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        ArrayList<Source> orderedSources = getOrderedSources();
770484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        ArrayList<Source> sourcesToQuery = new ArrayList<Source>(orderedSources.size());
780484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        for (Source source : orderedSources) {
790484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            if (shouldQuerySource(source, query)) {
800484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                sourcesToQuery.add(source);
810484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            }
820484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
830484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        return sourcesToQuery;
840484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
850484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
860484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    protected boolean shouldQuerySource(Source source, String query) {
870484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        return query.length() >= source.getQueryThreshold();
880484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
890484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
900484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    public Suggestions getSuggestions(String query) {
910484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        if (DBG) Log.d(TAG, "getSuggestions(" + query + ")");
920484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        cancelPendingTasks();
930484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        ArrayList<Source> sourcesToQuery = getSourcesToQuery(query);
940484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        final Suggestions suggestions = new Suggestions(mPromoter,
950484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                mConfig.getMaxPromotedSuggestions(),
960484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                query,
970484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                sourcesToQuery.size());
980484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        SuggestionCursor shortcuts = getShortcutsForQuery(query);
990484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        if (shortcuts != null) {
1000484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            suggestions.setShortcuts(shortcuts);
1010484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
1020484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1030484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        // Fast path for the zero sources case
1040484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        if (sourcesToQuery.size() == 0) {
1050484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            return suggestions;
1060484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
1070484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1080484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        SuggestionCursorReceiver receiver = new SuggestionCursorReceiver() {
1090484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            public void receiveSuggestionCursor(final SuggestionCursor cursor) {
1100484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                mPublishThread.post(new Runnable() {
1110484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                    public void run() {
1120484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                        suggestions.addSourceResult(cursor);
1130484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                    }
1140484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                });
1150484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            }
1160484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        };
1170484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1180484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        int maxResultsPerSource = mConfig.getMaxResultsPerSource();
1190484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        for (Source source : sourcesToQuery) {
1200484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            QueryTask task = new QueryTask(query, source, maxResultsPerSource, receiver);
1210484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            mQueryExecutor.execute(task);
1220484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
1230484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1240484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        return suggestions;
1250484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
1260484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1270484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private interface SuggestionCursorReceiver {
1280484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        void receiveSuggestionCursor(SuggestionCursor cursor);
1290484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
1300484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1310484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    /**
1320484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert     * Gets suggestions from a given source.
1330484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert     */
1340484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static class QueryTask implements SourceTask {
1350484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        private final String mQuery;
1360484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        private final Source mSource;
1370484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        private final int mQueryLimit;
1380484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        private final SuggestionCursorReceiver mReceiver;
1390484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1400484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        public QueryTask(String query, Source source, int queryLimit,
1410484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert                SuggestionCursorReceiver receiver) {
1420484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            mQuery = query;
1430484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            mSource = source;
1440484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            mQueryLimit = queryLimit;
1450484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            mReceiver = receiver;
1460484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
1470484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1480484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        public Source getSource() {
1490484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            return mSource;
1500484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
1510484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1520484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        public void run() {
1530484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            SuggestionCursor cursor = mSource.getSuggestions(mQuery, mQueryLimit);
1540484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            mReceiver.receiveSuggestionCursor(cursor);
1550484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
1560484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
1570484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        @Override
1580484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        public String toString() {
1590484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert            return mSource + "[" + mQuery + "]";
1600484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert        }
1610484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
1620484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert}
163