summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorDaisuke Miyakawa <dmiyakawa@google.com>2011-06-24 18:52:01 -0700
committerDaisuke Miyakawa <dmiyakawa@google.com>2011-06-28 12:13:43 -0700
commit2f830d3bb66f780937203e9738e046841a070e73 (patch)
tree655183ad09235d4321332de3e2aa588949462013 /src/com
parent42e8dddb940a502e97151a08f16b87aed7b30de5 (diff)
downloadpackages_providers_ContactsProvider-2f830d3bb66f780937203e9738e046841a070e73.zip
packages_providers_ContactsProvider-2f830d3bb66f780937203e9738e046841a070e73.tar.gz
packages_providers_ContactsProvider-2f830d3bb66f780937203e9738e046841a070e73.tar.bz2
Use new data usage stat for strequent contacts
Must be with Ie193bb91ee49b18f4a546a1f52be780bb514301d - use phone-only query parameter in strequent mode - introduce data_usage_stat View for combining the table with some of data/raw_contacts columns, which are needed for strequent uris. - modify strequent impl for supporting phone-only search - modify a test for strequent uri handling Checked performance. We need UNION ALL there and some nasty hacks with two sub queries. Bug: 4371572 Change-Id: I8c81747d8a8ae47ce551067fc4dbe2c48f4f48ae
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/providers/contacts/ContactsDatabaseHelper.java44
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java139
2 files changed, 144 insertions, 39 deletions
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index b14f422..2e2af94 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -97,7 +97,7 @@ import java.util.Locale;
* 600-699 Ice Cream Sandwich
* </pre>
*/
- static final int DATABASE_VERSION = 602;
+ static final int DATABASE_VERSION = 603;
private static final String DATABASE_NAME = "contacts2.db";
private static final String DATABASE_PRESENCE = "presence_db";
@@ -211,6 +211,10 @@ import java.util.Locale;
public static final String RAW_ENTITIES_RESTRICTED = "view_raw_entities_restricted";
public static final String GROUPS_ALL = "view_groups";
+
+ public static final String DATA_USAGE_STAT_ALL = "view_data_usage_stat";
+ public static final String DATA_USAGE_STAT_RESTRICTED =
+ "view_data_usage_stat_restricted";
}
public interface Clauses {
@@ -1323,6 +1327,8 @@ import java.util.Locale;
db.execSQL("DROP VIEW IF EXISTS " + Views.RAW_ENTITIES_RESTRICTED + ";");
db.execSQL("DROP VIEW IF EXISTS " + Views.ENTITIES + ";");
db.execSQL("DROP VIEW IF EXISTS " + Views.ENTITIES_RESTRICTED + ";");
+ db.execSQL("DROP VIEW IF EXISTS " + Views.DATA_USAGE_STAT_ALL + ";");
+ db.execSQL("DROP VIEW IF EXISTS " + Views.DATA_USAGE_STAT_RESTRICTED + ";");
String dataColumns =
Data.IS_PRIMARY + ", "
@@ -1585,6 +1591,28 @@ import java.util.Locale;
+ entitiesSelect);
db.execSQL("CREATE VIEW " + Views.ENTITIES_RESTRICTED + " AS "
+ entitiesSelect + " WHERE " + RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0");
+
+ String dataUsageStatSelect = "SELECT "
+ + DataUsageStatColumns.CONCRETE_ID + " AS " + DataUsageStatColumns._ID + ", "
+ + DataUsageStatColumns.DATA_ID + ", "
+ + RawContactsColumns.CONCRETE_CONTACT_ID + " AS " + RawContacts.CONTACT_ID + ", "
+ + MimetypesColumns.CONCRETE_MIMETYPE + " AS " + Data.MIMETYPE + ", "
+ + DataUsageStatColumns.USAGE_TYPE_INT + ", "
+ + DataUsageStatColumns.TIMES_USED + ", "
+ + DataUsageStatColumns.LAST_TIME_USED
+ + " FROM " + Tables.DATA_USAGE_STAT
+ + " JOIN " + Tables.DATA + " ON ("
+ + DataColumns.CONCRETE_ID + "=" + DataUsageStatColumns.CONCRETE_DATA_ID + ")"
+ + " JOIN " + Tables.RAW_CONTACTS + " ON ("
+ + RawContactsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_RAW_CONTACT_ID
+ + " )"
+ + " JOIN " + Tables.MIMETYPES + " ON ("
+ + MimetypesColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_MIMETYPE_ID + ")";
+
+ db.execSQL("CREATE VIEW " + Views.DATA_USAGE_STAT_ALL + " AS " + dataUsageStatSelect);
+ db.execSQL("CREATE VIEW " + Views.DATA_USAGE_STAT_RESTRICTED + " AS "
+ + dataUsageStatSelect + " WHERE "
+ + RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0");
}
private static String buildPhotoUriAlias(String contactIdColumn, String alias) {
@@ -1936,6 +1964,11 @@ import java.util.Locale;
oldVersion = 602;
}
+ if (oldVersion < 603) {
+ upgradeViewsAndTriggers = true;
+ oldVersion = 603;
+ }
+
if (upgradeViewsAndTriggers) {
createContactsViews(db);
createGroupsView(db);
@@ -3766,6 +3799,15 @@ import java.util.Locale;
Views.ENTITIES : Views.ENTITIES_RESTRICTED;
}
+ public String getDataUsageStatView() {
+ return getDataUsageStatView(false);
+ }
+
+ public String getDataUsageStatView(boolean requireRestrictedView) {
+ return (hasAccessToRestrictedData() && !requireRestrictedView) ?
+ Views.DATA_USAGE_STAT_ALL : Views.DATA_USAGE_STAT_RESTRICTED;
+ }
+
/**
* Test if any of the columns appear in the given projection.
*/
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 610fbd6..05edce5 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -29,6 +29,7 @@ import com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdat
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
@@ -39,6 +40,7 @@ import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.util.DbQueryUtils;
import com.android.vcard.VCardComposer;
import com.android.vcard.VCardConfig;
import com.google.android.collect.Lists;
@@ -184,10 +186,14 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- private static final String TIMES_CONTACTED_SORT_COLUMN = "times_contacted_sort";
+ /**
+ * Used to insert a column into strequent results, which enables SQL to sort the list using
+ * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
+ */
+ private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
- + TIMES_CONTACTED_SORT_COLUMN + " DESC, "
+ + TIMES_USED_SORT_COLUMN + " DESC, "
+ Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
private static final String STREQUENT_LIMIT =
"(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
@@ -601,12 +607,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
/** Used for pushing starred contacts to the top of a times contacted list **/
private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
.addAll(sContactsProjectionMap)
- .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
+ .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
.build();
private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
.addAll(sContactsProjectionMap)
- .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
+ .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
.build();
/** Contains just the contacts vCard columns */
@@ -3680,58 +3686,81 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
case CONTACTS_STREQUENT_FILTER:
case CONTACTS_STREQUENT: {
- String filterSql = null;
- if (match == CONTACTS_STREQUENT_FILTER
- && uri.getPathSegments().size() > 3) {
+ // Basically the resultant SQL should look like this:
+ // (SQL for listing starred items)
+ // UNION ALL
+ // (SQL for listing frequently contacted items)
+ // ORDER BY ...
+
+ final boolean phoneOnly = readBooleanQueryParameter(
+ uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
+ if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
String filterParam = uri.getLastPathSegment();
StringBuilder sb = new StringBuilder();
sb.append(Contacts._ID + " IN ");
appendContactFilterAsNestedQuery(sb, filterParam);
- filterSql = sb.toString();
+ selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
}
- setTablesAndProjectionMapForContacts(qb, uri, projection);
-
- String[] starredProjection = null;
- String[] frequentProjection = null;
+ String[] subProjection = null;
if (projection != null) {
- starredProjection =
- appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
- frequentProjection =
- appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
+ subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
}
- qb.setProjectionMap(sStrequentStarredProjectionMap);
// Build the first query for starred
- if (filterSql != null) {
- qb.appendWhere(filterSql + " AND ");
- }
- qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
- final String starredQuery = qb.buildQuery(starredProjection,
+ setTablesAndProjectionMapForContacts(qb, uri, projection,
+ false /* for frequent */, phoneOnly);
+ qb.setProjectionMap(sStrequentStarredProjectionMap);
+ qb.appendWhere(DbQueryUtils.concatenateClauses(
+ selection, Contacts.IS_USER_PROFILE + "=0"));
+ qb.setStrict(true);
+ final String starredQuery = qb.buildQuery(subProjection,
Contacts.STARRED + "=1", Contacts._ID, null, null, null);
- // Build the second query for frequent
+ // Reset the builder.
qb = new SQLiteQueryBuilder();
- setTablesAndProjectionMapForContacts(qb, uri, projection);
+
+ // Build the second query for frequent
+ setTablesAndProjectionMapForContacts(qb, uri, projection,
+ true /* for frequent */, phoneOnly);
qb.setProjectionMap(sStrequentFrequentProjectionMap);
- if (filterSql != null) {
- qb.appendWhere(filterSql + " AND ");
- }
- qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
- final String frequentQuery = qb.buildQuery(frequentProjection,
- Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
- + " = 0 OR " + Contacts.STARRED + " IS NULL)",
+ qb.appendWhere(DbQueryUtils.concatenateClauses(
+ selection, Contacts.IS_USER_PROFILE + "=0"));
+ qb.setStrict(true);
+ final String frequentQuery = qb.buildQuery(subProjection,
+ "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)",
Contacts._ID, null, null, null);
// Put them together
- final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
- STREQUENT_ORDER_BY, STREQUENT_LIMIT);
- Cursor c = db.rawQuery(query, null);
- if (c != null) {
- c.setNotificationUri(getContext().getContentResolver(),
+ final String unionQuery =
+ qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
+ STREQUENT_ORDER_BY, STREQUENT_LIMIT);
+
+ // Here, we need to use selection / selectionArgs (supplied from users) "twice",
+ // as we want them both for starred items and for frequently contacted items.
+ //
+ // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
+ // the resultant SQL should be like:
+ // SELECT ... WHERE starred =? AND ...
+ // UNION ALL
+ // SELECT ... WHERE starred =? AND ...
+ String[] doubledSelectionArgs = null;
+ if (selectionArgs != null) {
+ final int length = selectionArgs.length;
+ doubledSelectionArgs = new String[length * 2];
+ for (int i = 0; i < length; i++) {
+ final String arg = selectionArgs[i];
+ doubledSelectionArgs[i] = arg;
+ doubledSelectionArgs[length + i] = arg;
+ }
+ }
+
+ Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
+ if (cursor != null) {
+ cursor.setNotificationUri(getContext().getContentResolver(),
ContactsContract.AUTHORITY_URI);
}
- return c;
+ return cursor;
}
case CONTACTS_GROUP: {
@@ -4753,8 +4782,42 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
String[] projection) {
+ setTablesAndProjectionMapForContacts(qb, uri, projection, false, false);
+ }
+
+ /**
+ * @param forStrequentFrequent Should be used only in strequent handling.
+ * true when this is for frequently contacted listing (not starred)
+ * @param strequentPhoneCallOnly Should be used only in strequent handling.
+ * true when this is for phone-only results. See also
+ * {@link ContactsContract#STREQUENT_PHONE_ONLY}.
+ */
+ private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
+ String[] projection, boolean forStrequentFrequent, boolean strequentPhoneCallOnly) {
StringBuilder sb = new StringBuilder();
- sb.append(mDbHelper.getContactView(shouldExcludeRestrictedData(uri)));
+ String viewName = mDbHelper.getContactView(shouldExcludeRestrictedData(uri));
+ sb.append(viewName);
+
+ // Just for frequently contacted contacts in Strequent Uri handling.
+ if (forStrequentFrequent) {
+ final String strequentPhoneCallOnlyClause =
+ (strequentPhoneCallOnly ? DbQueryUtils.concatenateClauses(
+ MimetypesColumns.MIMETYPE + "=\'" + Phone.CONTENT_ITEM_TYPE + "'",
+ DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" +
+ DataUsageStatColumns.USAGE_TYPE_INT_CALL)
+ : "");
+ // Use INNER JOIN for maximum performance, ommiting unnecessary rows as much as
+ // possible.
+ sb.append(" INNER JOIN " +
+ mDbHelper.getDataUsageStatView() + " AS " + Tables.DATA_USAGE_STAT +
+ " ON (" +
+ DbQueryUtils.concatenateClauses(
+ DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
+ RawContacts.CONTACT_ID + "=" + viewName + "." + Contacts._ID,
+ strequentPhoneCallOnlyClause) +
+ ")");
+ }
+
appendContactPresenceJoin(sb, projection, Contacts._ID);
appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
qb.setTables(sb.toString());