1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.contacts.model.dataitem; 18 19 import android.content.ContentValues; 20 import android.content.Context; 21 import android.provider.ContactsContract.CommonDataKinds.Email; 22 import android.provider.ContactsContract.CommonDataKinds.Event; 23 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 24 import android.provider.ContactsContract.CommonDataKinds.Identity; 25 import android.provider.ContactsContract.CommonDataKinds.Im; 26 import android.provider.ContactsContract.CommonDataKinds.Nickname; 27 import android.provider.ContactsContract.CommonDataKinds.Note; 28 import android.provider.ContactsContract.CommonDataKinds.Organization; 29 import android.provider.ContactsContract.CommonDataKinds.Phone; 30 import android.provider.ContactsContract.CommonDataKinds.Photo; 31 import android.provider.ContactsContract.CommonDataKinds.Relation; 32 import android.provider.ContactsContract.CommonDataKinds.SipAddress; 33 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 34 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 35 import android.provider.ContactsContract.CommonDataKinds.Website; 36 import android.provider.ContactsContract.Contacts.Data; 37 import android.provider.ContactsContract.Contacts.Entity; 38 39 import com.android.contacts.Collapser; 40 import com.android.contacts.MoreContactUtils; 41 import com.android.contacts.model.RawContactModifier; 42 43 /** 44 * This is the base class for data items, which represents a row from the Data table. 45 */ 46 public class DataItem implements Collapser.Collapsible<DataItem> { 47 48 private final ContentValues mContentValues; 49 protected DataKind mKind; 50 51 protected DataItem(ContentValues values) { 52 mContentValues = values; 53 } 54 55 /** 56 * Factory for creating subclasses of DataItem objects based on the mimetype in the 57 * content values. Raw contact is the raw contact that this data item is associated with. 58 */ 59 public static DataItem createFrom(ContentValues values) { 60 final String mimeType = values.getAsString(Data.MIMETYPE); 61 if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) { 62 return new GroupMembershipDataItem(values); 63 } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { 64 return new StructuredNameDataItem(values); 65 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) { 66 return new PhoneDataItem(values); 67 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) { 68 return new EmailDataItem(values); 69 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) { 70 return new StructuredPostalDataItem(values); 71 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) { 72 return new ImDataItem(values); 73 } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) { 74 return new OrganizationDataItem(values); 75 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) { 76 return new NicknameDataItem(values); 77 } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) { 78 return new NoteDataItem(values); 79 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) { 80 return new WebsiteDataItem(values); 81 } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) { 82 return new SipAddressDataItem(values); 83 } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) { 84 return new EventDataItem(values); 85 } else if (Relation.CONTENT_ITEM_TYPE.equals(mimeType)) { 86 return new RelationDataItem(values); 87 } else if (Identity.CONTENT_ITEM_TYPE.equals(mimeType)) { 88 return new IdentityDataItem(values); 89 } else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) { 90 return new PhotoDataItem(values); 91 } else if (CustomDataItem.MIMETYPE_CUSTOM_FIELD.equals(mimeType)) { 92 return new CustomDataItem(values); 93 } 94 95 // generic 96 return new DataItem(values); 97 } 98 99 public ContentValues getContentValues() { 100 return mContentValues; 101 } 102 103 public void setRawContactId(long rawContactId) { 104 mContentValues.put(Data.RAW_CONTACT_ID, rawContactId); 105 } 106 107 public Long getRawContactId() { 108 return mContentValues.getAsLong(Data.RAW_CONTACT_ID); 109 } 110 111 /** 112 * Returns the data id. 113 */ 114 public long getId() { 115 return mContentValues.getAsLong(Data._ID); 116 } 117 118 /** 119 * Returns the mimetype of the data. 120 */ 121 public String getMimeType() { 122 return mContentValues.getAsString(Data.MIMETYPE); 123 } 124 125 public void setMimeType(String mimeType) { 126 mContentValues.put(Data.MIMETYPE, mimeType); 127 } 128 129 public boolean isPrimary() { 130 Integer primary = mContentValues.getAsInteger(Data.IS_PRIMARY); 131 return primary != null && primary != 0; 132 } 133 134 public boolean isSuperPrimary() { 135 Integer superPrimary = mContentValues.getAsInteger(Data.IS_SUPER_PRIMARY); 136 return superPrimary != null && superPrimary != 0; 137 } 138 139 public boolean hasKindTypeColumn(DataKind kind) { 140 final String key = kind.typeColumn; 141 return key != null && mContentValues.containsKey(key) && 142 mContentValues.getAsInteger(key) != null; 143 } 144 145 public int getKindTypeColumn(DataKind kind) { 146 final String key = kind.typeColumn; 147 return mContentValues.getAsInteger(key); 148 } 149 150 /** 151 * Indicates the carrier presence value for the current {@link DataItem}. 152 * 153 * @return {@link Data#CARRIER_PRESENCE_VT_CAPABLE} if the {@link DataItem} supports carrier 154 * video calling, {@code 0} otherwise. 155 */ 156 public int getCarrierPresence() { 157 final Integer value = mContentValues.getAsInteger(Data.CARRIER_PRESENCE); 158 return value != null ? value.intValue() : 0; 159 } 160 161 /** 162 * This builds the data string depending on the type of data item by using the generic 163 * DataKind object underneath. 164 */ 165 public String buildDataString(Context context, DataKind kind) { 166 if (kind.actionBody == null) { 167 return null; 168 } 169 CharSequence actionBody = kind.actionBody.inflateUsing(context, mContentValues); 170 return actionBody == null ? null : actionBody.toString(); 171 } 172 173 /** 174 * This builds the data string(intended for display) depending on the type of data item. It 175 * returns the same value as {@link #buildDataString} by default, but certain data items can 176 * override it to provide their version of formatted data strings. 177 * 178 * @return Data string representing the data item, possibly formatted for display 179 */ 180 public String buildDataStringForDisplay(Context context, DataKind kind) { 181 return buildDataString(context, kind); 182 } 183 184 public void setDataKind(DataKind kind) { 185 mKind = kind; 186 } 187 188 public DataKind getDataKind() { 189 return mKind; 190 } 191 192 public Integer getTimesUsed() { 193 return mContentValues.getAsInteger(Entity.TIMES_USED); 194 } 195 196 public Long getLastTimeUsed() { 197 return mContentValues.getAsLong(Entity.LAST_TIME_USED); 198 } 199 200 @Override 201 public void collapseWith(DataItem that) { 202 DataKind thisKind = getDataKind(); 203 DataKind thatKind = that.getDataKind(); 204 // If this does not have a type and that does, or if that's type is higher precedence, 205 // use that's type 206 if ((!hasKindTypeColumn(thisKind) && that.hasKindTypeColumn(thatKind)) || 207 that.hasKindTypeColumn(thatKind) && 208 RawContactModifier.getTypePrecedence(thisKind, getKindTypeColumn(thisKind)) 209 > 210 RawContactModifier.getTypePrecedence(thatKind, that.getKindTypeColumn(thatKind))) { 211 mContentValues.put(thatKind.typeColumn, that.getKindTypeColumn(thatKind)); 212 mKind = thatKind; 213 } 214 215 // Choose the max of the maxLines and maxLabelLines values. 216 mKind.maxLinesForDisplay = Math.max(thisKind.maxLinesForDisplay, 217 thatKind.maxLinesForDisplay); 218 219 // If any of the collapsed entries are super primary make the whole thing super primary. 220 if (isSuperPrimary() || that.isSuperPrimary()) { 221 mContentValues.put(Data.IS_SUPER_PRIMARY, 1); 222 mContentValues.put(Data.IS_PRIMARY, 1); 223 } 224 225 // If any of the collapsed entries are primary make the whole thing primary. 226 if (isPrimary() || that.isPrimary()) { 227 mContentValues.put(Data.IS_PRIMARY, 1); 228 } 229 230 // Add up the times used 231 mContentValues.put(Entity.TIMES_USED, (getTimesUsed() == null ? 0 : getTimesUsed()) + 232 (that.getTimesUsed() == null ? 0 : that.getTimesUsed())); 233 234 // Use the most recent time 235 mContentValues.put(Entity.LAST_TIME_USED, 236 Math.max(getLastTimeUsed() == null ? 0 : getLastTimeUsed(), 237 that.getLastTimeUsed() == null ? 0 : that.getLastTimeUsed())); 238 } 239 240 @Override 241 public boolean shouldCollapseWith(DataItem t, Context context) { 242 if (mKind == null || t.getDataKind() == null) { 243 return false; 244 } 245 return MoreContactUtils.shouldCollapse(getMimeType(), buildDataString(context, mKind), 246 t.getMimeType(), t.buildDataString(context, t.getDataKind())); 247 } 248 } 249