/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.providers.contacts;
import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import android.app.SearchManager;
import android.content.ContentUris;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Contacts.Photo;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.StatusUpdates;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
/**
* Support for global search integration for Contacts.
*/
public class GlobalSearchSupport {
private static final String[] SEARCH_SUGGESTIONS_BASED_ON_PHONE_NUMBER_COLUMNS = {
"_id",
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_ICON_1,
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
};
private static final String[] SEARCH_SUGGESTIONS_BASED_ON_NAME_COLUMNS = {
"_id",
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_ICON_1,
SearchManager.SUGGEST_COLUMN_ICON_2,
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
};
private interface SearchSuggestionQuery {
public static final String TABLE = "data "
+ " JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
+ " JOIN default_directory on (raw_contacts.contact_id = default_directory._id) "
+ " JOIN contacts ON (default_directory._id = contacts._id) "
+ " JOIN " + Tables.RAW_CONTACTS + " AS name_raw_contact ON ("
+ Contacts.NAME_RAW_CONTACT_ID + "=name_raw_contact." + RawContacts._ID + ")";
public static final String PRESENCE_SQL =
"(SELECT " + StatusUpdates.PRESENCE +
" FROM " + Tables.AGGREGATED_PRESENCE +
" WHERE " + AggregatedPresenceColumns.CONTACT_ID
+ "=" + ContactsColumns.CONCRETE_ID + ")";
public static final String[] COLUMNS = {
ContactsColumns.CONCRETE_ID + " AS " + Contacts._ID,
"name_raw_contact." + RawContactsColumns.DISPLAY_NAME
+ " AS " + Contacts.DISPLAY_NAME,
PRESENCE_SQL + " AS " + Contacts.CONTACT_PRESENCE,
DataColumns.CONCRETE_ID + " AS data_id",
DataColumns.MIMETYPE_ID,
Data.IS_SUPER_PRIMARY,
Data.DATA1,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
public static final int CONTACT_ID = 0;
public static final int DISPLAY_NAME = 1;
public static final int PRESENCE_STATUS = 2;
public static final int DATA_ID = 3;
public static final int MIMETYPE_ID = 4;
public static final int IS_SUPER_PRIMARY = 5;
public static final int ORGANIZATION = 6;
public static final int EMAIL = 6;
public static final int PHONE = 6;
public static final int PHOTO_ID = 7;
public static final int LOOKUP_KEY = 8;
// Current contacts - those contacted within the last 3 days (in seconds)
public static final long CURRENT_CONTACTS = 3 * 24 * 60 * 60;
// Recent contacts - those contacted within the last 30 days (in seconds)
public static final long RECENT_CONTACTS = 30 * 24 * 60 * 60;
public static final String TIME_SINCE_LAST_CONTACTED =
"(strftime('%s', 'now') - contacts." + Contacts.LAST_TIME_CONTACTED + "/1000)";
/*
* See {@link ContactsProvider2#EMAIL_FILTER_SORT_ORDER} for the discussion of this
* sorting order.
*/
public static final String SORT_ORDER =
"(CASE WHEN contacts." + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), "
+ "(CASE WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + CURRENT_CONTACTS + " THEN 0 "
+ " WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + RECENT_CONTACTS + " THEN 1 "
+ " ELSE 2 END),"
+ "contacts." + Contacts.TIMES_CONTACTED + " DESC, "
+ "name_raw_contact." + RawContacts.DISPLAY_NAME_PRIMARY + ", "
+ "contacts." + Contacts._ID;
}
private static class SearchSuggestion {
long contactId;
boolean titleIsName;
String organization;
String email;
String phoneNumber;
Uri photoUri;
String lookupKey;
String normalizedName;
int presence = -1;
boolean processed;
String text1;
String text2;
String icon1;
String icon2;
public SearchSuggestion(long contactId) {
this.contactId = contactId;
}
private void process() {
if (processed) {
return;
}
boolean hasOrganization = !TextUtils.isEmpty(organization);
boolean hasEmail = !TextUtils.isEmpty(email);
boolean hasPhone = !TextUtils.isEmpty(phoneNumber);
boolean titleIsOrganization = !titleIsName && hasOrganization;
boolean titleIsEmail = !titleIsName && !titleIsOrganization && hasEmail;
boolean titleIsPhone = !titleIsName && !titleIsOrganization && !titleIsEmail
&& hasPhone;
if (!titleIsOrganization && hasOrganization) {
text2 = organization;
} else if (!titleIsPhone && hasPhone) {
text2 = phoneNumber;
} else if (!titleIsEmail && hasEmail) {
text2 = email;
}
if (photoUri != null) {
icon1 = photoUri.toString();
} else {
icon1 = String.valueOf(com.android.internal.R.drawable.ic_contact_picture);
}
if (presence != -1) {
icon2 = String.valueOf(StatusUpdates.getPresenceIconResourceId(presence));
}
processed = true;
}
/**
* Returns key for sorting search suggestions.
*
*
TODO: switch to new sort key
*/
public String getSortKey() {
if (normalizedName == null) {
process();
normalizedName = text1 == null ? "" : NameNormalizer.normalize(text1);
}
return normalizedName;
}
@SuppressWarnings({"unchecked"})
public ArrayList asList(String[] projection) {
process();
ArrayList