diff options
author | Daisuke Miyakawa <dmiyakawa@google.com> | 2011-06-06 17:30:07 -0700 |
---|---|---|
committer | Daisuke Miyakawa <dmiyakawa@google.com> | 2011-06-14 14:55:29 -0700 |
commit | 46abbb56764add30cb6e6506f55d8dededc88113 (patch) | |
tree | a5cf873e600a8c79503754e85e3025d9ddb02f12 /src/com | |
parent | 24c1d384b45a6d3c1cc959062a9d4308335fabbf (diff) | |
download | packages_providers_ContactsProvider-46abbb56764add30cb6e6506f55d8dededc88113.zip packages_providers_ContactsProvider-46abbb56764add30cb6e6506f55d8dededc88113.tar.gz packages_providers_ContactsProvider-46abbb56764add30cb6e6506f55d8dededc88113.tar.bz2 |
Introduce data usage table for per-method ranking.
- have a hidden table for per-method promotion
- make filter API use it (phone, email only)
- add a unit test
- remove an old test using previous API
Must be after: I602c0b83afca674904946f59bbdfc4dca07d46e4
Bug: 4371572
Change-Id: I82052953d5dad42ac171df29248ed25e9b4a2434
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/providers/contacts/ContactsDatabaseHelper.java | 77 | ||||
-rw-r--r-- | src/com/android/providers/contacts/ContactsProvider2.java | 241 |
2 files changed, 283 insertions, 35 deletions
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java index 5eb9243..473b30a 100644 --- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java +++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java @@ -96,7 +96,7 @@ import java.util.Locale; * 600-699 Ice Cream Sandwich * </pre> */ - static final int DATABASE_VERSION = 600; + static final int DATABASE_VERSION = 601; private static final String DATABASE_NAME = "contacts2.db"; private static final String DATABASE_PRESENCE = "presence_db"; @@ -124,6 +124,12 @@ import java.util.Locale; public static final String DEFAULT_DIRECTORY = "default_directory"; public static final String SEARCH_INDEX = "search_index"; + /** + * For {@link ContactsContract.DataUsageFeedback}. The table structure itself + * is not exposed outside. + */ + public static final String DATA_USAGE_STAT = "data_usage_stat"; + public static final String DATA_JOIN_MIMETYPES = "data " + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id)"; @@ -507,6 +513,43 @@ import java.util.Locale; public static final String TOKENS = "tokens"; } + /** + * Private table for calculating per-contact-method ranking. + */ + public static final class DataUsageStatColumns { + /** type: INTEGER (long) */ + public static final String _ID = "stat_id"; + public static final String CONCRETE_ID = Tables.DATA_USAGE_STAT + "." + _ID; + + /** type: INTEGER (long) */ + public static final String DATA_ID = "data_id"; + public static final String CONCRETE_DATA_ID = Tables.DATA_USAGE_STAT + "." + DATA_ID; + + /** type: INTEGER (long) */ + public static final String LAST_TIME_USED = "last_time_used"; + public static final String CONCRETE_LAST_TIME_USED = + Tables.DATA_USAGE_STAT + "." + LAST_TIME_USED; + + /** type: INTEGER */ + public static final String TIMES_USED = "times_used"; + public static final String CONCRETE_TIMES_USED = + Tables.DATA_USAGE_STAT + "." + TIMES_USED; + + /** type: INTEGER */ + public static final String USAGE_TYPE_INT = "usage_type"; + public static final String CONCRETE_USAGE_TYPE = + Tables.DATA_USAGE_STAT + "." + USAGE_TYPE_INT; + + /** + * Integer values for USAGE_TYPE. + * + * @see ContactsContract.DataUsageFeedback#USAGE_TYPE + */ + public static final int USAGE_TYPE_INT_CALL = 0; + public static final int USAGE_TYPE_INT_LONG_TEXT = 1; + public static final int USAGE_TYPE_INT_SHORT_TEXT = 2; + } + /** In-memory cache of previously found MIME-type mappings */ private final HashMap<String, Long> mMimetypeCache = new HashMap<String, Long>(); /** In-memory cache of previously found package name mappings */ @@ -1075,6 +1118,21 @@ import java.util.Locale; createDirectoriesTable(db); createSearchIndexTable(db); + db.execSQL("CREATE TABLE " + Tables.DATA_USAGE_STAT + "(" + + DataUsageStatColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + DataUsageStatColumns.DATA_ID + " INTEGER NOT NULL, " + + DataUsageStatColumns.USAGE_TYPE_INT + " INTEGER NOT NULL DEFAULT 0, " + + DataUsageStatColumns.TIMES_USED + " INTEGER NOT NULL DEFAULT 0, " + + DataUsageStatColumns.LAST_TIME_USED + " INTERGER NOT NULL DEFAULT 0, " + + "FOREIGN KEY(" + DataUsageStatColumns.DATA_ID + ") REFERENCES " + + Tables.DATA + "(" + Data._ID + ")" + + ");"); + db.execSQL("CREATE UNIQUE INDEX data_usage_stat_index ON " + + Tables.DATA_USAGE_STAT + " (" + + DataUsageStatColumns.DATA_ID + ", " + + DataUsageStatColumns.USAGE_TYPE_INT + + ");"); + createContactsViews(db); createGroupsView(db); createContactsTriggers(db); @@ -1859,6 +1917,11 @@ import java.util.Locale; oldVersion = 600; } + if (oldVersion < 601) { + upgradeToVersion601(db); + oldVersion = 601; + } + if (upgradeViewsAndTriggers) { createContactsViews(db); createGroupsView(db); @@ -2911,6 +2974,18 @@ import java.util.Locale; " (profile_raw_contact_id);"); } + private void upgradeToVersion601(SQLiteDatabase db) { + db.execSQL("CREATE TABLE data_usage_stat(" + + "stat_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "data_id INTEGER NOT NULL, " + + "usage_type INTEGER NOT NULL DEFAULT 0, " + + "times_used INTEGER NOT NULL DEFAULT 0, " + + "last_time_used INTERGER NOT NULL DEFAULT 0, " + + "FOREIGN KEY(data_id) REFERENCES data(_id));"); + db.execSQL("CREATE UNIQUE INDEX data_usage_stat_index ON " + + "data_usage_stat (data_id, usage_type)"); + } + public String extractHandleFromEmailAddress(String email) { Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email); if (tokens.length == 0) { diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 8073177..054f18a 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -34,6 +34,7 @@ import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns; import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns; import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns; import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns; import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns; import com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns; @@ -111,6 +112,7 @@ import android.provider.ContactsContract.Intents; import android.provider.ContactsContract.PhoneLookup; import android.provider.ContactsContract.ProviderStatus; import android.provider.ContactsContract.RawContacts; +import android.provider.ContactsContract.DataUsageFeedback; import android.provider.ContactsContract.SearchSnippetColumns; import android.provider.ContactsContract.Settings; import android.provider.ContactsContract.StatusUpdates; @@ -131,6 +133,7 @@ import java.io.OutputStreamWriter; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -287,6 +290,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007; private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008; + private static final int DATA_USAGE_FEEDBACK_ID = 20001; + private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID = RawContactsColumns.CONCRETE_ID + "=? AND " + GroupsColumns.CONCRETE_ACCOUNT_NAME @@ -411,25 +416,27 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // Recent contacts - those contacted within the last 30 days (in seconds) private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60; - private static final String TIME_SINCE_LAST_CONTACTED = - "(strftime('%s', 'now') - " + Contacts.LAST_TIME_CONTACTED + "/1000)"; - /* * Sorting order for email address suggestions: first starred, then the rest. * Within the starred/unstarred groups - three buckets: very recently contacted, then fairly * recently contacted, then the rest. Within each of the bucket - descending count - * of times contacted. If all else fails, alphabetical. (Super)primary email - * address is returned before other addresses for the same contact. + * of times contacted (both for data row and for contact row). If all else fails, alphabetical. + * (Super)primary email address is returned before other addresses for the same contact. */ private static final String EMAIL_FILTER_SORT_ORDER = - "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), " - + "(CASE WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_CURRENT + " THEN 0 " - + " WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_RECENT + " THEN 1 " - + " ELSE 2 END)," - + Contacts.TIMES_CONTACTED + " DESC, " - + Contacts.DISPLAY_NAME + ", " - + Data.CONTACT_ID + ", " - + Data.IS_SUPER_PRIMARY + " DESC"; + "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), " + + "(CASE WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_CURRENT + + " THEN 0 " + + " WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_RECENT + + " THEN 1 " + + " ELSE 2 END), " + + DataUsageStatColumns.TIMES_USED + " DESC, " + + Contacts.DISPLAY_NAME + ", " + + Data.CONTACT_ID + ", " + + Data.IS_SUPER_PRIMARY + " DESC"; + + /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */ + private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER; /** Name lookup types used for contact filtering */ private static final String CONTACT_LOOKUP_NAME_TYPES = @@ -866,6 +873,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private Account mAccount; + /** + * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to + * type integers in {@link DataUsageStatColumns}. + */ + private static final Map<String, Integer> sDataUsageTypeMap; + static { // Contacts URI matching table final UriMatcher matcher = sUriMatcher; @@ -918,6 +931,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS); matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID); + /** "*" is in CSV form with data ids ("123,456,789") */ + matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID); matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS); matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID); @@ -973,6 +988,14 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun PROFILE_RAW_CONTACTS_ID_DATA); matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity", PROFILE_RAW_CONTACTS_ID_ENTITIES); + + HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>(); + tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL); + tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, + DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT); + tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, + DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT); + sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap); } private static class DirectoryInfo { @@ -2754,6 +2777,15 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun break; } + case DATA_USAGE_FEEDBACK_ID: { + if (handleDataUsageFeedback(uri)) { + count = 1; + } else { + count = 0; + } + break; + } + default: { mSyncToNetwork = true; return mLegacyApiSupport.update(uri, values, selection, selectionArgs); @@ -3816,7 +3848,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } case PHONES_FILTER: { - setTablesAndProjectionMapForData(qb, uri, projection, true); + String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE); + Integer typeInt = sDataUsageTypeMap.get(typeParam); + if (typeInt == null) { + typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL; + } + setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt); qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'"); if (uri.getPathSegments().size() > 2) { String filterParam = uri.getLastPathSegment(); @@ -3864,7 +3901,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID; if (sortOrder == null) { - sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID; + final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri); + if (!TextUtils.isEmpty(accountPromotionSortOrder)) { + sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER; + } else { + sortOrder = PHONE_FILTER_SORT_ORDER; + } } break; } @@ -3896,14 +3938,14 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } case EMAILS_FILTER: { - setTablesAndProjectionMapForData(qb, uri, projection, true); + String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE); + Integer typeInt = sDataUsageTypeMap.get(typeParam); + if (typeInt == null) { + typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT; + } + setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt); String filterParam = null; - String primaryAccountName = - uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME); - String primaryAccountType = - uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE); - if (uri.getPathSegments().size() > 3) { filterParam = uri.getLastPathSegment(); if (TextUtils.isEmpty(filterParam)) { @@ -3945,19 +3987,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } groupBy = Email.DATA + "," + RawContacts.CONTACT_ID; if (sortOrder == null) { - // Addresses associated with primary account should be promoted. - if (!TextUtils.isEmpty(primaryAccountName)) { - StringBuilder sb2 = new StringBuilder(); - sb2.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "="); - DatabaseUtils.appendEscapedSQLString(sb2, primaryAccountName); - if (!TextUtils.isEmpty(primaryAccountType)) { - sb2.append(" AND " + RawContacts.ACCOUNT_TYPE + "="); - DatabaseUtils.appendEscapedSQLString(sb2, primaryAccountType); - } - sb2.append(" THEN 0 ELSE 1 END), "); - sb2.append(EMAIL_FILTER_SORT_ORDER); - - sortOrder = sb2.toString(); + final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri); + if (!TextUtils.isEmpty(accountPromotionSortOrder)) { + sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER; } else { sortOrder = EMAIL_FILTER_SORT_ORDER; } @@ -4931,6 +4963,15 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri, String[] projection, boolean distinct) { + setTablesAndProjectionMapForData(qb, uri, projection, distinct, null); + } + + /** + * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified + * type. + */ + private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri, + String[] projection, boolean distinct, Integer usageType) { StringBuilder sb = new StringBuilder(); sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri))); sb.append(" data"); @@ -4940,6 +4981,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID); appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID); + if (usageType != null) { + appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID); + } + qb.setTables(sb.toString()); boolean useDistinct = distinct @@ -5006,6 +5051,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } } + private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) { + sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT + + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn + + " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")"); + } + private void appendContactPresenceJoin(StringBuilder sb, String[] projection, String contactIdColumn) { if (mDbHelper.isInProjection(projection, @@ -5769,4 +5820,126 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } return sIsPhone; } + + private boolean handleDataUsageFeedback(Uri uri) { + final long currentTimeMillis = System.currentTimeMillis(); + final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE); + final String[] ids = uri.getLastPathSegment().trim().split(","); + final ArrayList<Long> dataIds = new ArrayList<Long>(); + + for (String id : ids) { + dataIds.add(Long.valueOf(id)); + } + final boolean successful; + if (TextUtils.isEmpty(usageType)) { + Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring."); + successful = false; + } else { + successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0; + } + + // Handle old API. This doesn't affect the result of this entire method. + final String[] questionMarks = new String[ids.length]; + Arrays.fill(questionMarks, "?"); + final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")"; + final Cursor cursor = mDb.query( + mDbHelper.getDataView(shouldExcludeRestrictedData(uri)), + new String[] { Data.CONTACT_ID }, + where, ids, null, null, null); + try { + while (cursor.moveToNext()) { + mSelectionArgs1[0] = cursor.getString(0); + ContentValues values2 = new ContentValues(); + values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis); + mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1); + mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1); + mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1); + } + } finally { + cursor.close(); + } + + return successful; + } + + /** + * Update {@link Tables#DATA_USAGE_STAT}. + * + * @return the number of rows affected. + */ + private int updateDataUsageStat( + ArrayList<Long> dataIds, String type, long currentTimeMillis) { + final int typeInt = sDataUsageTypeMap.get(type); + final String where = DataUsageStatColumns.DATA_ID + " =? AND " + + DataUsageStatColumns.USAGE_TYPE_INT + " =?"; + final String[] columns = + new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED }; + final ContentValues values = new ContentValues(); + for (Long dataId : dataIds) { + final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) }; + mDb.beginTransaction(); + try { + final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args, + null, null, null); + try { + if (cursor.getCount() > 0) { + if (!cursor.moveToFirst()) { + Log.e(TAG, + "moveToFirst() failed while getAccount() returned non-zero."); + } else { + values.clear(); + values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1); + values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis); + mDb.update(Tables.DATA_USAGE_STAT, values, + DataUsageStatColumns._ID + " =?", + new String[] { cursor.getString(0) }); + } + } else { + values.clear(); + values.put(DataUsageStatColumns.DATA_ID, dataId); + values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt); + values.put(DataUsageStatColumns.TIMES_USED, 1); + values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis); + mDb.insert(Tables.DATA_USAGE_STAT, null, values); + } + mDb.setTransactionSuccessful(); + } finally { + cursor.close(); + } + } finally { + mDb.endTransaction(); + } + } + + return dataIds.size(); + } + + /** + * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.) + * associated with a primary account. The primary account should be supplied from applications + * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and + * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary + * account isn't available. + */ + private String getAccountPromotionSortOrder(Uri uri) { + final String primaryAccountName = + uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME); + final String primaryAccountType = + uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE); + + // Data rows associated with primary account should be promoted. + if (!TextUtils.isEmpty(primaryAccountName)) { + StringBuilder sb = new StringBuilder(); + sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "="); + DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName); + if (!TextUtils.isEmpty(primaryAccountType)) { + sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "="); + DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType); + } + sb.append(" THEN 0 ELSE 1 END)"); + return sb.toString(); + } else { + return null; + } + } } |