1a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov/*
2a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * Copyright (C) 2009 The Android Open Source Project
3a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov *
4a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
5a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * you may not use this file except in compliance with the License.
6a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * You may obtain a copy of the License at
7a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov *
8a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
9a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov *
10a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
11a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
12a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * See the License for the specific language governing permissions and
14a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * limitations under the License
15a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov */
16a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
17a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovpackage com.android.providers.contacts;
18a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
19a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport android.app.SearchManager;
20fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwoodimport android.content.Context;
21a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport android.database.Cursor;
224cb5cd47d9c3378e22050011152c88f2bc666514Dmitri Plotnikovimport android.database.MatrixCursor;
23a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport android.database.sqlite.SQLiteDatabase;
24a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport android.net.Uri;
25a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
26a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
27a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
284cb5cd47d9c3378e22050011152c88f2bc666514Dmitri Plotnikovimport android.provider.ContactsContract.Contacts;
294cb5cd47d9c3378e22050011152c88f2bc666514Dmitri Plotnikovimport android.provider.ContactsContract.Data;
30174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
314cb5cd47d9c3378e22050011152c88f2bc666514Dmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
32fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwoodimport android.telephony.TelephonyManager;
33a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport android.text.TextUtils;
34a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
35873d6bed8f795a0f4b75560ecbe92feade516514Mathew Inwoodimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
36873d6bed8f795a0f4b75560ecbe92feade516514Mathew Inwoodimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
37873d6bed8f795a0f4b75560ecbe92feade516514Mathew Inwoodimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
38873d6bed8f795a0f4b75560ecbe92feade516514Mathew Inwoodimport com.android.providers.contacts.ContactsDatabaseHelper.Views;
39873d6bed8f795a0f4b75560ecbe92feade516514Mathew Inwood
40a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovimport java.util.ArrayList;
41a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
42a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov/**
43a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov * Support for global search integration for Contacts.
44a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov */
45a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikovpublic class GlobalSearchSupport {
46a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
47fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood    private static final String[] SEARCH_SUGGESTIONS_COLUMNS = {
48a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            "_id",
49a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            SearchManager.SUGGEST_COLUMN_TEXT_1,
50a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            SearchManager.SUGGEST_COLUMN_TEXT_2,
51a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            SearchManager.SUGGEST_COLUMN_ICON_1,
52a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            SearchManager.SUGGEST_COLUMN_ICON_2,
53174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
54fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood            SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
55a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
56174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA,
5789f718673f71c71951ded3723b35a0c82d8dda34Mark Brophy            SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT,
58a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    };
59a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
60174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    private static final char SNIPPET_START_MATCH = '\u0001';
61174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    private static final char SNIPPET_END_MATCH = '\u0001';
62174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    private static final String SNIPPET_ELLIPSIS = "\u2026";
63174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    private static final int SNIPPET_MAX_TOKENS = 5;
64174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
65174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    private static final String PRESENCE_SQL =
66174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        "(SELECT " + StatusUpdates.PRESENCE +
67174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        " FROM " + Tables.AGGREGATED_PRESENCE +
68174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        " WHERE " + AggregatedPresenceColumns.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")";
69174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
70a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private static class SearchSuggestion {
71174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        long contactId;
72174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String photoUri;
732d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        String lookupKey;
74a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        int presence = -1;
75a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        String text1;
76a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        String text2;
77a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        String icon1;
78a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        String icon2;
79fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        String intentData;
80fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        String intentAction;
81174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String filter;
8289f718673f71c71951ded3723b35a0c82d8dda34Mark Brophy        String lastAccessTime;
83a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
84174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        @SuppressWarnings({"unchecked"})
85084a9d5ee564c758ff7709690ab404d6ac58e78dMathew Inwood        public ArrayList<?> asList(String[] projection) {
86fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood            if (icon1 == null) {
87fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                if (photoUri != null) {
88fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                    icon1 = photoUri.toString();
89fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                } else {
90fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                    icon1 = String.valueOf(com.android.internal.R.drawable.ic_contact_picture);
91fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                }
92a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            }
93a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
94a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            if (presence != -1) {
9582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                icon2 = String.valueOf(StatusUpdates.getPresenceIconResourceId(presence));
96a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            }
97a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
98a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            ArrayList<Object> list = new ArrayList<Object>();
99b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            if (projection == null) {
100fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(contactId); // _id
101fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(text1); // text1
102fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(text2); // text2
103fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(icon1); // icon1
104fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(icon2); // icon2
105fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(intentData == null ? buildUri() : intentData); // intent data
106fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(intentAction); // intentAction
107fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(lookupKey); // shortcut id
108fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(filter); // extra data
109fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(lastAccessTime); // last access hint
110b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            } else {
111b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                for (int i = 0; i < projection.length; i++) {
112b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                    addColumnValue(list, projection[i]);
113b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                }
114b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            }
115a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            return list;
116a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        }
117b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
118b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        private void addColumnValue(ArrayList<Object> list, String column) {
119b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            if ("_id".equals(column)) {
120b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                list.add(contactId);
121b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            } else if (SearchManager.SUGGEST_COLUMN_TEXT_1.equals(column)) {
122b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                list.add(text1);
123b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            } else if (SearchManager.SUGGEST_COLUMN_TEXT_2.equals(column)) {
124b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                list.add(text2);
125b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            } else if (SearchManager.SUGGEST_COLUMN_ICON_1.equals(column)) {
126b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                list.add(icon1);
127b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            } else if (SearchManager.SUGGEST_COLUMN_ICON_2.equals(column)) {
128b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                list.add(icon2);
129174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            } else if (SearchManager.SUGGEST_COLUMN_INTENT_DATA.equals(column)) {
130fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                list.add(intentData == null ? buildUri() : intentData);
131b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            } else if (SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID.equals(column)) {
1322d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                list.add(lookupKey);
133b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            } else if (SearchManager.SUGGEST_COLUMN_SHORTCUT_ID.equals(column)) {
1342d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                list.add(lookupKey);
135174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            } else if (SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA.equals(column)) {
136174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                list.add(filter);
13789f718673f71c71951ded3723b35a0c82d8dda34Mark Brophy            } else if (SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT.equals(column)) {
13889f718673f71c71951ded3723b35a0c82d8dda34Mark Brophy                list.add(lastAccessTime);
139b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            } else {
140b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                throw new IllegalArgumentException("Invalid column name: " + column);
141b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            }
142b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
143174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
144174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        private String buildUri() {
145174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            return Contacts.getLookupUri(contactId, lookupKey).toString();
146174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
14757e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann
14857e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann        public void reset() {
14957e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            contactId = 0;
15057e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            photoUri = null;
15157e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            lookupKey = null;
15257e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            presence = -1;
15357e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            text1 = null;
15457e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            text2 = null;
15557e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            icon1 = null;
15657e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            icon2 = null;
15757e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            intentData = null;
15857e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            intentAction = null;
15957e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            filter = null;
16057e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann            lastAccessTime = null;
16157e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann        }
162a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    }
163a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
164a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private final ContactsProvider2 mContactsProvider;
165a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
166dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov    @SuppressWarnings("all")
167a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    public GlobalSearchSupport(ContactsProvider2 contactsProvider) {
168a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mContactsProvider = contactsProvider;
169fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood
170fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        TelephonyManager telman = (TelephonyManager)
171fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                mContactsProvider.getContext().getSystemService(Context.TELEPHONY_SERVICE);
172dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov
173dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov        // To ensure the data column position. This is dead code if properly configured.
174dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov        if (Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
175dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov                || Email.DATA != Data.DATA1) {
176dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov            throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
177dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov                    + " data is not in DATA1 column");
178dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov        }
179dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov    }
180dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov
181174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public Cursor handleSearchSuggestionsQuery(
182174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            SQLiteDatabase db, Uri uri, String[] projection, String limit) {
183084a9d5ee564c758ff7709690ab404d6ac58e78dMathew Inwood        final MatrixCursor cursor = new MatrixCursor(
184084a9d5ee564c758ff7709690ab404d6ac58e78dMathew Inwood                projection == null ? SEARCH_SUGGESTIONS_COLUMNS : projection);
185fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood
186b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        if (uri.getPathSegments().size() <= 1) {
187084a9d5ee564c758ff7709690ab404d6ac58e78dMathew Inwood            // no search term, return empty
18825110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood        } else {
189084a9d5ee564c758ff7709690ab404d6ac58e78dMathew Inwood            String selection = null;
190084a9d5ee564c758ff7709690ab404d6ac58e78dMathew Inwood            String searchClause = uri.getLastPathSegment();
191084a9d5ee564c758ff7709690ab404d6ac58e78dMathew Inwood            addSearchSuggestionsBasedOnFilter(
192084a9d5ee564c758ff7709690ab404d6ac58e78dMathew Inwood                    cursor, db, projection, selection, searchClause, limit);
193a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        }
194a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
195fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        return cursor;
19620e99644372a288beabe8d9d9481f34e2f4045d8Mark Brophy    }
19720e99644372a288beabe8d9d9481f34e2f4045d8Mark Brophy
1982d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
1992d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     * Returns a search suggestions cursor for the contact bearing the provided lookup key.  If the
2002d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     * lookup key cannot be found in the database, the contact name is decoded from the lookup key
2012d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     * and used to re-identify the contact.  If the contact still cannot be found, an empty cursor
2022d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     * is returned.
2032d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     *
2042d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     * <p>Note that if {@code lookupKey} is not a valid lookup key, an empty cursor is returned
2052d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     * silently.  This would occur with old-style shortcuts that were created using the contact id
2062d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     * instead of the lookup key.
2072d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
208174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public Cursor handleSearchShortcutRefresh(SQLiteDatabase db, String[] projection,
209174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            String lookupKey, String filter) {
2102d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        long contactId;
2112d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        try {
2122d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            contactId = mContactsProvider.lookupContactIdByLookupKey(db, lookupKey);
2132d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        } catch (IllegalArgumentException e) {
2142d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            contactId = -1L;
2152d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        }
216fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        MatrixCursor cursor = new MatrixCursor(
217fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                projection == null ? SEARCH_SUGGESTIONS_COLUMNS : projection);
218fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        return addSearchSuggestionsBasedOnFilter(cursor,
219174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                db, projection, ContactsColumns.CONCRETE_ID + "=" + contactId, filter, null);
220b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
221b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
222fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood    private Cursor addSearchSuggestionsBasedOnFilter(MatrixCursor cursor, SQLiteDatabase db,
223174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            String[] projection, String selection, String filter, String limit) {
224b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
22525110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood        final boolean haveFilter = !TextUtils.isEmpty(filter);
226174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append("SELECT "
227174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        + Contacts._ID + ", "
228174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        + Contacts.LOOKUP_KEY + ", "
229174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        + Contacts.PHOTO_THUMBNAIL_URI + ", "
230174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        + Contacts.DISPLAY_NAME + ", "
23189f718673f71c71951ded3723b35a0c82d8dda34Mark Brophy                        + PRESENCE_SQL + " AS " + Contacts.CONTACT_PRESENCE + ", "
23289f718673f71c71951ded3723b35a0c82d8dda34Mark Brophy                        + Contacts.LAST_TIME_CONTACTED);
23325110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood        if (haveFilter) {
23425110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood            sb.append(", " + SearchSnippetColumns.SNIPPET);
23525110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood        }
23625110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood        sb.append(" FROM ");
237ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
238174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" AS contacts");
23925110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood        if (haveFilter) {
240174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            mContactsProvider.appendSearchIndexJoin(sb, filter, true,
241174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    String.valueOf(SNIPPET_START_MATCH), String.valueOf(SNIPPET_END_MATCH),
242b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    SNIPPET_ELLIPSIS, SNIPPET_MAX_TOKENS, false);
243174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
244174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (selection != null) {
245174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            sb.append(" WHERE ").append(selection);
246174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
247174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (limit != null) {
248174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            sb.append(" LIMIT " + limit);
2499a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
250b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        Cursor c = db.rawQuery(sb.toString(), null);
251174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        SearchSuggestion suggestion = new SearchSuggestion();
252174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        suggestion.filter = filter;
253174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        try {
254174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            while (c.moveToNext()) {
255174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                suggestion.contactId = c.getLong(0);
256174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                suggestion.lookupKey = c.getString(1);
257174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                suggestion.photoUri = c.getString(2);
258174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                suggestion.text1 = c.getString(3);
2595bd028407806015c91d863ee2bbffbaaf1c200d8Dave Santoro                suggestion.presence = c.isNull(4) ? -1 : c.getInt(4);
26089f718673f71c71951ded3723b35a0c82d8dda34Mark Brophy                suggestion.lastAccessTime = c.getString(5);
26125110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood                if (haveFilter) {
26289f718673f71c71951ded3723b35a0c82d8dda34Mark Brophy                    suggestion.text2 = shortenSnippet(c.getString(6));
26325110bb5ff8463a3047c29f535618a682d7cbfb6Mathew Inwood                }
264174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                cursor.addRow(suggestion.asList(projection));
26557e70bb3e70f9fe9b2916696fae45e686f567592Daniel Lehmann                suggestion.reset();
2669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
267174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } finally {
268174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            c.close();
2699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
270174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        return cursor;
271b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
272a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
273174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    private String shortenSnippet(final String snippet) {
274174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippet == null) {
275174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            return null;
276174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
277b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
278174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        int from = 0;
279174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        int to = snippet.length();
280174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        int start = snippet.indexOf(SNIPPET_START_MATCH);
281174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (start == -1) {
28292ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            return null;
283174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
2842d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill
285174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        int firstNl = snippet.lastIndexOf('\n', start);
286174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (firstNl != -1) {
287174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            from = firstNl + 1;
288174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
289174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        int end = snippet.lastIndexOf(SNIPPET_END_MATCH);
290174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (end != -1) {
291174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            int lastNl = snippet.indexOf('\n', end);
292174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            if (lastNl != -1) {
293174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                to = lastNl;
294a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            }
295a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        }
296a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov
297174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
298174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        for (int i = from; i < to; i++) {
299174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            char c = snippet.charAt(i);
300174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            if (c != SNIPPET_START_MATCH && c != SNIPPET_END_MATCH) {
301174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                sb.append(c);
302a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            }
303a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        }
304174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        return sb.toString();
305a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    }
306a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov}
307