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.common.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 import com.android.contacts.common.Collapser; 39 import com.android.contacts.common.MoreContactUtils; 40 import com.android.contacts.common.model.account.AccountType.EditType; 41 42 /** This is the base class for data items, which represents a row from the Data table. */ 43 public class DataItem implements Collapser.Collapsible<DataItem> { 44 45 private final ContentValues mContentValues; 46 protected DataKind mKind; 47 DataItem(ContentValues values)48 protected DataItem(ContentValues values) { 49 mContentValues = values; 50 } 51 52 /** 53 * Factory for creating subclasses of DataItem objects based on the mimetype in the content 54 * values. Raw contact is the raw contact that this data item is associated with. 55 */ createFrom(ContentValues values)56 public static DataItem createFrom(ContentValues values) { 57 final String mimeType = values.getAsString(Data.MIMETYPE); 58 if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) { 59 return new GroupMembershipDataItem(values); 60 } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { 61 return new StructuredNameDataItem(values); 62 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) { 63 return new PhoneDataItem(values); 64 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) { 65 return new EmailDataItem(values); 66 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) { 67 return new StructuredPostalDataItem(values); 68 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) { 69 return new ImDataItem(values); 70 } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) { 71 return new OrganizationDataItem(values); 72 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) { 73 return new NicknameDataItem(values); 74 } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) { 75 return new NoteDataItem(values); 76 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) { 77 return new WebsiteDataItem(values); 78 } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) { 79 return new SipAddressDataItem(values); 80 } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) { 81 return new EventDataItem(values); 82 } else if (Relation.CONTENT_ITEM_TYPE.equals(mimeType)) { 83 return new RelationDataItem(values); 84 } else if (Identity.CONTENT_ITEM_TYPE.equals(mimeType)) { 85 return new IdentityDataItem(values); 86 } else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) { 87 return new PhotoDataItem(values); 88 } 89 90 // generic 91 return new DataItem(values); 92 } 93 getContentValues()94 public ContentValues getContentValues() { 95 return mContentValues; 96 } 97 getRawContactId()98 public Long getRawContactId() { 99 return mContentValues.getAsLong(Data.RAW_CONTACT_ID); 100 } 101 setRawContactId(long rawContactId)102 public void setRawContactId(long rawContactId) { 103 mContentValues.put(Data.RAW_CONTACT_ID, rawContactId); 104 } 105 106 /** Returns the data id. */ getId()107 public long getId() { 108 return mContentValues.getAsLong(Data._ID); 109 } 110 111 /** Returns the mimetype of the data. */ getMimeType()112 public String getMimeType() { 113 return mContentValues.getAsString(Data.MIMETYPE); 114 } 115 setMimeType(String mimeType)116 public void setMimeType(String mimeType) { 117 mContentValues.put(Data.MIMETYPE, mimeType); 118 } 119 isPrimary()120 public boolean isPrimary() { 121 Integer primary = mContentValues.getAsInteger(Data.IS_PRIMARY); 122 return primary != null && primary != 0; 123 } 124 isSuperPrimary()125 public boolean isSuperPrimary() { 126 Integer superPrimary = mContentValues.getAsInteger(Data.IS_SUPER_PRIMARY); 127 return superPrimary != null && superPrimary != 0; 128 } 129 hasKindTypeColumn(DataKind kind)130 public boolean hasKindTypeColumn(DataKind kind) { 131 final String key = kind.typeColumn; 132 return key != null 133 && mContentValues.containsKey(key) 134 && mContentValues.getAsInteger(key) != null; 135 } 136 getKindTypeColumn(DataKind kind)137 public int getKindTypeColumn(DataKind kind) { 138 final String key = kind.typeColumn; 139 return mContentValues.getAsInteger(key); 140 } 141 142 /** 143 * Indicates the carrier presence value for the current {@link DataItem}. 144 * 145 * @return {@link Data#CARRIER_PRESENCE_VT_CAPABLE} if the {@link DataItem} supports carrier video 146 * calling, {@code 0} otherwise. 147 */ getCarrierPresence()148 public int getCarrierPresence() { 149 return mContentValues.getAsInteger(Data.CARRIER_PRESENCE); 150 } 151 152 /** 153 * This builds the data string depending on the type of data item by using the generic DataKind 154 * object underneath. 155 */ buildDataString(Context context, DataKind kind)156 public String buildDataString(Context context, DataKind kind) { 157 if (kind.actionBody == null) { 158 return null; 159 } 160 CharSequence actionBody = kind.actionBody.inflateUsing(context, mContentValues); 161 return actionBody == null ? null : actionBody.toString(); 162 } 163 164 /** 165 * This builds the data string(intended for display) depending on the type of data item. It 166 * returns the same value as {@link #buildDataString} by default, but certain data items can 167 * override it to provide their version of formatted data strings. 168 * 169 * @return Data string representing the data item, possibly formatted for display 170 */ buildDataStringForDisplay(Context context, DataKind kind)171 public String buildDataStringForDisplay(Context context, DataKind kind) { 172 return buildDataString(context, kind); 173 } 174 getDataKind()175 public DataKind getDataKind() { 176 return mKind; 177 } 178 setDataKind(DataKind kind)179 public void setDataKind(DataKind kind) { 180 mKind = kind; 181 } 182 getTimesUsed()183 public Integer getTimesUsed() { 184 return mContentValues.getAsInteger(Entity.TIMES_USED); 185 } 186 getLastTimeUsed()187 public Long getLastTimeUsed() { 188 return mContentValues.getAsLong(Entity.LAST_TIME_USED); 189 } 190 191 @Override collapseWith(DataItem that)192 public void collapseWith(DataItem that) { 193 DataKind thisKind = getDataKind(); 194 DataKind thatKind = that.getDataKind(); 195 // If this does not have a type and that does, or if that's type is higher precedence, 196 // use that's type 197 if ((!hasKindTypeColumn(thisKind) && that.hasKindTypeColumn(thatKind)) 198 || (that.hasKindTypeColumn(thatKind) 199 && getTypePrecedence(thisKind, getKindTypeColumn(thisKind)) 200 > getTypePrecedence(thatKind, that.getKindTypeColumn(thatKind)))) { 201 mContentValues.put(thatKind.typeColumn, that.getKindTypeColumn(thatKind)); 202 mKind = thatKind; 203 } 204 205 // Choose the max of the maxLines and maxLabelLines values. 206 mKind.maxLinesForDisplay = Math.max(thisKind.maxLinesForDisplay, thatKind.maxLinesForDisplay); 207 208 // If any of the collapsed entries are super primary make the whole thing super primary. 209 if (isSuperPrimary() || that.isSuperPrimary()) { 210 mContentValues.put(Data.IS_SUPER_PRIMARY, 1); 211 mContentValues.put(Data.IS_PRIMARY, 1); 212 } 213 214 // If any of the collapsed entries are primary make the whole thing primary. 215 if (isPrimary() || that.isPrimary()) { 216 mContentValues.put(Data.IS_PRIMARY, 1); 217 } 218 219 // Add up the times used 220 mContentValues.put( 221 Entity.TIMES_USED, 222 (getTimesUsed() == null ? 0 : getTimesUsed()) 223 + (that.getTimesUsed() == null ? 0 : that.getTimesUsed())); 224 225 // Use the most recent time 226 mContentValues.put( 227 Entity.LAST_TIME_USED, 228 Math.max( 229 getLastTimeUsed() == null ? 0 : getLastTimeUsed(), 230 that.getLastTimeUsed() == null ? 0 : that.getLastTimeUsed())); 231 } 232 233 @Override shouldCollapseWith(DataItem t, Context context)234 public boolean shouldCollapseWith(DataItem t, Context context) { 235 if (mKind == null || t.getDataKind() == null) { 236 return false; 237 } 238 return MoreContactUtils.shouldCollapse( 239 getMimeType(), 240 buildDataString(context, mKind), 241 t.getMimeType(), 242 t.buildDataString(context, t.getDataKind())); 243 } 244 245 /** 246 * Return the precedence for the the given {@link EditType#rawValue}, where lower numbers are 247 * higher precedence. 248 */ getTypePrecedence(DataKind kind, int rawValue)249 private static int getTypePrecedence(DataKind kind, int rawValue) { 250 for (int i = 0; i < kind.typeList.size(); i++) { 251 final EditType type = kind.typeList.get(i); 252 if (type.rawValue == rawValue) { 253 return i; 254 } 255 } 256 return Integer.MAX_VALUE; 257 } 258 } 259