10ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze/*
22078bc2358031ef3a191900d9036daf4251911c1Matthew Fritze * Copyright (C) 2017 The Android Open Source Project
30ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze *
40ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * Licensed under the Apache License, Version 2.0 (the "License");
50ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * you may not use this file except in compliance with the License.
60ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * You may obtain a copy of the License at
70ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze *
80ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze *      http://www.apache.org/licenses/LICENSE-2.0
90ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze *
100ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * Unless required by applicable law or agreed to in writing, software
110ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * distributed under the License is distributed on an "AS IS" BASIS,
120ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * See the License for the specific language governing permissions and
140ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * limitations under the License.
152078bc2358031ef3a191900d9036daf4251911c1Matthew Fritze *
160ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze */
170ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
182078bc2358031ef3a191900d9036daf4251911c1Matthew Fritzepackage com.android.settings.search;
190ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
20623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhangimport static com.android.settings.search.IndexDatabaseHelper.IndexColumns;
21623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhangimport static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
22623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhang
230ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritzeimport android.content.Context;
240ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritzeimport android.database.Cursor;
250ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritzeimport android.database.sqlite.SQLiteDatabase;
26fbcf4e6e14d090aaced06d3f8708d6e9df1962baFan Zhangimport android.support.annotation.VisibleForTesting;
277fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze
28a96b11f65d9f29dd4037da85e12f5cf9dcfa0176Fan Zhangimport com.android.settings.dashboard.SiteMapManager;
290ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritzeimport com.android.settings.utils.AsyncLoader;
300ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
316efea1e624bb4b48290000cfdb82df224a31b81dMatthew Fritzeimport java.util.HashSet;
326efea1e624bb4b48290000cfdb82df224a31b81dMatthew Fritzeimport java.util.Set;
330ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
340ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze/**
350ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze * AsyncTask to retrieve Settings, First party app and any intent based results.
360ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze */
37683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryadpublic class DatabaseResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
3812daf6830de241cc86408f904300ff0bdee02a9cFan Zhang    private static final String LOG = "DatabaseResultLoader";
3965fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze
4065fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze    /* These indices are used to match the columns of the this loader's SELECT statement.
417fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze     These are not necessarily the same order nor similar coverage as the schema defined in
4265fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze     IndexDatabaseHelper */
437fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_ID = 0;
447fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_TITLE = 1;
457fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_SUMMARY_ON = 2;
467fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_SUMMARY_OFF = 3;
477fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_CLASS_NAME = 4;
487fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_SCREEN_TITLE = 5;
497fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_ICON = 6;
507fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_INTENT_ACTION = 7;
517fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
527fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
537fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_KEY = 10;
547fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
557fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    static final int COLUMN_INDEX_PAYLOAD = 12;
567fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze
577fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    public static final String[] SELECT_COLUMNS = {
587fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DOCID,
597fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_TITLE,
607fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_SUMMARY_ON,
617fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_SUMMARY_OFF,
627fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.CLASS_NAME,
637fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.SCREEN_TITLE,
647fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.ICON,
657fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.INTENT_ACTION,
667fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.INTENT_TARGET_PACKAGE,
677fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.INTENT_TARGET_CLASS,
687fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_KEY_REF,
697fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.PAYLOAD_TYPE,
707fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.PAYLOAD
717fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    };
727fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze
737fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    public static final String[] MATCH_COLUMNS_PRIMARY = {
747fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_TITLE,
757fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_TITLE_NORMALIZED,
767fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    };
777fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze
787fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    public static final String[] MATCH_COLUMNS_SECONDARY = {
797fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_SUMMARY_ON,
807fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_SUMMARY_ON_NORMALIZED,
817fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_SUMMARY_OFF,
827fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
837fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    };
847fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze
857fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    public static final String[] MATCH_COLUMNS_TERTIARY = {
867fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_KEYWORDS,
877fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            IndexColumns.DATA_ENTRIES
887fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    };
897fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze
907fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    /**
917fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze     * Base ranks defines the best possible rank based on what the query matches.
92b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * If the query matches the prefix of the first word in the title, the best rank it can be is 1
93b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * If the query matches the prefix of the other words in the title, the best rank it can be is 3
94b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * If the query only matches the summary, the best rank it can be is 7
95b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * If the query only matches keywords or entries, the best rank it can be is 9
96b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     *
977fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze     */
98b759516cbf26c9bb5f71c7f93e764fac73f48701Matthew Fritze    public static final int[] BASE_RANKS = {1, 3, 7, 9};
9965fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze
100fbcf4e6e14d090aaced06d3f8708d6e9df1962baFan Zhang    @VisibleForTesting
101fbcf4e6e14d090aaced06d3f8708d6e9df1962baFan Zhang    final String mQueryText;
10240ce0fab758714c973945ec8f053c52076d5fc50Matthew Fritze    private final Context mContext;
103a96b11f65d9f29dd4037da85e12f5cf9dcfa0176Fan Zhang    private final CursorToSearchResultConverter mConverter;
104a96b11f65d9f29dd4037da85e12f5cf9dcfa0176Fan Zhang    private final SiteMapManager mSiteMapManager;
105a96b11f65d9f29dd4037da85e12f5cf9dcfa0176Fan Zhang
10640ce0fab758714c973945ec8f053c52076d5fc50Matthew Fritze    public DatabaseResultLoader(Context context, String queryText, SiteMapManager mapManager) {
1070ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze        super(context);
10840ce0fab758714c973945ec8f053c52076d5fc50Matthew Fritze        mSiteMapManager = mapManager;
10940ce0fab758714c973945ec8f053c52076d5fc50Matthew Fritze        mContext = context;
110fbcf4e6e14d090aaced06d3f8708d6e9df1962baFan Zhang        mQueryText = queryText;
1116efea1e624bb4b48290000cfdb82df224a31b81dMatthew Fritze        mConverter = new CursorToSearchResultConverter(context);
1120ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze    }
1130ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
1140ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze    @Override
115683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad    protected void onDiscardResult(Set<? extends SearchResult> result) {
1160ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze        // TODO Search
1170ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze    }
1180ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
1190ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze    @Override
120683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad    public Set<? extends SearchResult> loadInBackground() {
1210ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze        if (mQueryText == null || mQueryText.isEmpty()) {
1220ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze            return null;
1230ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze        }
1240ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
125683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad        final Set<SearchResult> results = new HashSet<>();
1267fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze
127683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad        results.addAll(firstWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[0]));
128683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad        results.addAll(secondaryWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[1]));
129683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad        results.addAll(anyWordQuery(MATCH_COLUMNS_SECONDARY, BASE_RANKS[2]));
130683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad        results.addAll(anyWordQuery(MATCH_COLUMNS_TERTIARY, BASE_RANKS[3]));
1316efea1e624bb4b48290000cfdb82df224a31b81dMatthew Fritze        return results;
1320ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze    }
1330ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
1340ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze    @Override
1350ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze    protected boolean onCancelLoad() {
1360ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze        // TODO
1370ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze        return super.onCancelLoad();
1380ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze    }
1390ed37c351333429f58e1561fcb8e6af9c2041507Matthew Fritze
1407fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    /**
141b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * Creates and executes the query which matches prefixes of the first word of the given columns.
142b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     *
143b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param matchColumns The columns to match on
144b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param baseRank The highest rank achievable by these results
145683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad     * @return A set of the matching results.
146b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     */
1476efea1e624bb4b48290000cfdb82df224a31b81dMatthew Fritze    private Set<SearchResult> firstWordQuery(String[] matchColumns, int baseRank) {
148b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final String whereClause = buildSingleWordWhereClause(matchColumns);
149b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final String query = mQueryText + "%";
150b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final String[] selection = buildSingleWordSelection(query, matchColumns.length);
151b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
152b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        return query(whereClause, selection, baseRank);
153b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    }
154b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
155b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    /**
156b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * Creates and executes the query which matches prefixes of the non-first words of the
157b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * given columns.
158b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     *
159b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param matchColumns The columns to match on
160b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param baseRank The highest rank achievable by these results
161683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad     * @return A set of the matching results.
162b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     */
1636efea1e624bb4b48290000cfdb82df224a31b81dMatthew Fritze    private Set<SearchResult> secondaryWordQuery(String[] matchColumns, int baseRank) {
164b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final String whereClause = buildSingleWordWhereClause(matchColumns);
165b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final String query = "% " + mQueryText + "%";
166b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final String[] selection = buildSingleWordSelection(query, matchColumns.length);
167b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
168b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        return query(whereClause, selection, baseRank);
169b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    }
170b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
171b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    /**
172b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * Creates and executes the query which matches prefixes of the any word of the given columns.
173b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     *
174b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param matchColumns The columns to match on
175b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param baseRank The highest rank achievable by these results
176683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad     * @return A set of the matching results.
177b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     */
1786efea1e624bb4b48290000cfdb82df224a31b81dMatthew Fritze    private Set<SearchResult> anyWordQuery(String[] matchColumns, int baseRank) {
179b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final String whereClause = buildTwoWordWhereClause(matchColumns);
180b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final String[] selection = buildAnyWordSelection(matchColumns.length * 2);
181b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
182b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        return query(whereClause, selection, baseRank);
183b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    }
184b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
185b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    /**
186b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * Generic method used by all of the query methods above to execute a query.
187b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     *
188b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param whereClause Where clause for the SQL query which uses bindings.
189b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param selection List of the transformed query to match each bind in the whereClause
190b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param baseRank The highest rank achievable by these results.
191683ccdf97bd8b3ed30b7014f2df17227cff92561Soroosh Mariooryad     * @return A set of the matching results.
192b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     */
1936efea1e624bb4b48290000cfdb82df224a31b81dMatthew Fritze    private Set<SearchResult> query(String whereClause, String[] selection, int baseRank) {
194623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhang        final SQLiteDatabase database =
195623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhang                IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
196623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhang        try (Cursor resultCursor = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS, whereClause,
197623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhang                selection, null, null, null)) {
198623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhang            return mConverter.convertCursor(mSiteMapManager, resultCursor, baseRank);
199623faaa00e7bd04463e1393bbb8f210d005ef47fFan Zhang        }
200b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    }
201b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
202b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    /**
203b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * Builds the SQLite WHERE clause that matches all matchColumns for a single query.
204b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     *
205b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param matchColumns List of columns that will be used for matching.
206b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @return The constructed WHERE clause.
207b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     */
208b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    private static String buildSingleWordWhereClause(String[] matchColumns) {
209b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        StringBuilder sb = new StringBuilder(" (");
210b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        final int count = matchColumns.length;
211b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        for (int n = 0; n < count; n++) {
212b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze            sb.append(matchColumns[n]);
213b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze            sb.append(" like ? ");
214b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze            if (n < count - 1) {
215b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze                sb.append(" OR ");
216b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze            }
217b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        }
218b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        sb.append(") AND enabled = 1");
219b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        return sb.toString();
220b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    }
221b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
222b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    /**
223b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * Builds the SQLite WHERE clause that matches all matchColumns to two different queries.
224b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     *
225b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param matchColumns List of columns that will be used for matching.
226b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @return The constructed WHERE clause.
227b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     */
228b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    private static String buildTwoWordWhereClause(String[] matchColumns) {
2293f3b547e4a2eeeb362d9b7c98b05f9a244cd754eMatthew Fritze        StringBuilder sb = new StringBuilder(" (");
2307fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze        final int count = matchColumns.length;
2317fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze        for (int n = 0; n < count; n++) {
2327fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            sb.append(matchColumns[n]);
233314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze            sb.append(" like ? OR ");
234314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze            sb.append(matchColumns[n]);
2357fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            sb.append(" like ?");
2367fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            if (n < count - 1) {
2377fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze                sb.append(" OR ");
2387fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze            }
2397fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze        }
2403f3b547e4a2eeeb362d9b7c98b05f9a244cd754eMatthew Fritze        sb.append(") AND enabled = 1");
2417fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze        return sb.toString();
2427fda314980107337d2eb9237ed00e997ae4ee345Matthew Fritze    }
243314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze
244314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze    /**
245b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * Fills out the selection array to match the query as the prefix of a single word.
246b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     *
247b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     * @param size is the number of columns to be matched.
248b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze     */
249b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    private String[] buildSingleWordSelection(String query, int size) {
250b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        String[] selection = new String[size];
251b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
252b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        for(int i = 0; i < size; i ++) {
253b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze            selection[i] = query;
254b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        }
255b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze        return selection;
256b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    }
257b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze
258b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    /**
259314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze     * Fills out the selection array to match the query as the prefix of a word.
260314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze     *
261314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze     * @param size is twice the number of columns to be matched. The first match is for the prefix
262314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze     *             of the first word in the column. The second match is for any subsequent word
263314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze     *             prefix match.
264314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze     */
265b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze    private String[] buildAnyWordSelection(int size) {
266314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze        String[] selection = new String[size];
267314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze        final String query = mQueryText + "%";
268314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze        final String subStringQuery = "% " + mQueryText + "%";
269314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze
270314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze        for(int i = 0; i < (size - 1); i += 2) {
271314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze            selection[i] = query;
272314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze            selection[i + 1] = subStringQuery;
273314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze        }
274314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze        return selection;
275314b09eb4b6bff2626604155e4273956aeaa24b5Matthew Fritze    }
276b7b286cb893268bec38584acdf5696f68675ecedMatthew Fritze}