Home | History | Annotate | Download | only in model
      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;
     18 
     19 import android.content.ContentValues;
     20 import android.content.Context;
     21 import android.content.Entity;
     22 import android.net.Uri;
     23 import android.os.Parcel;
     24 import android.os.Parcelable;
     25 import android.provider.ContactsContract.Contacts;
     26 import android.provider.ContactsContract.Data;
     27 import android.provider.ContactsContract.RawContacts;
     28 
     29 import com.android.contacts.common.model.AccountTypeManager;
     30 import com.android.contacts.common.model.account.AccountType;
     31 import com.android.contacts.common.model.account.AccountWithDataSet;
     32 import com.android.contacts.common.model.dataitem.DataItem;
     33 import com.google.common.base.Objects;
     34 import com.google.common.collect.Lists;
     35 
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 
     39 /**
     40  * RawContact represents a single raw contact in the raw contacts database.
     41  * It has specialized getters/setters for raw contact
     42  * items, and also contains a collection of DataItem objects.  A RawContact contains the information
     43  * from a single account.
     44  *
     45  * This allows RawContact objects to be thought of as a class with raw contact
     46  * fields (like account type, name, data set, sync state, etc.) and a list of
     47  * DataItem objects that represent contact information elements (like phone
     48  * numbers, email, address, etc.).
     49  */
     50 final public class RawContact implements Parcelable {
     51 
     52     private AccountTypeManager mAccountTypeManager;
     53     private final ContentValues mValues;
     54     private final ArrayList<NamedDataItem> mDataItems;
     55 
     56     final public static class NamedDataItem implements Parcelable {
     57         public final Uri mUri;
     58 
     59         // This use to be a DataItem. DataItem creation is now delayed until the point of request
     60         // since there is no benefit to storing them here due to the multiple inheritance.
     61         // Eventually instanceof still has to be used anyways to determine which sub-class of
     62         // DataItem it is. And having parent DataItem's here makes it very difficult to serialize or
     63         // parcelable.
     64         //
     65         // Instead of having a common DataItem super class, we should refactor this to be a generic
     66         // Object where the object is a concrete class that no longer relies on ContentValues.
     67         // (this will also make the classes easier to use).
     68         // Since instanceof is used later anyways, having a list of Objects won't hurt and is no
     69         // worse than having a DataItem.
     70         public final ContentValues mContentValues;
     71 
     72         public NamedDataItem(Uri uri, ContentValues values) {
     73             this.mUri = uri;
     74             this.mContentValues = values;
     75         }
     76 
     77         public NamedDataItem(Parcel parcel) {
     78             this.mUri = parcel.readParcelable(Uri.class.getClassLoader());
     79             this.mContentValues = parcel.readParcelable(ContentValues.class.getClassLoader());
     80         }
     81 
     82         @Override
     83         public int describeContents() {
     84             return 0;
     85         }
     86 
     87         @Override
     88         public void writeToParcel(Parcel parcel, int i) {
     89             parcel.writeParcelable(mUri, i);
     90             parcel.writeParcelable(mContentValues, i);
     91         }
     92 
     93         public static final Parcelable.Creator<NamedDataItem> CREATOR
     94                 = new Parcelable.Creator<NamedDataItem>() {
     95 
     96             @Override
     97             public NamedDataItem createFromParcel(Parcel parcel) {
     98                 return new NamedDataItem(parcel);
     99             }
    100 
    101             @Override
    102             public NamedDataItem[] newArray(int i) {
    103                 return new NamedDataItem[i];
    104             }
    105         };
    106 
    107         @Override
    108         public int hashCode() {
    109             return Objects.hashCode(mUri, mContentValues);
    110         }
    111 
    112         @Override
    113         public boolean equals(Object obj) {
    114             if (obj == null) return false;
    115             if (getClass() != obj.getClass()) return false;
    116 
    117             final NamedDataItem other = (NamedDataItem) obj;
    118             return Objects.equal(mUri, other.mUri) &&
    119                     Objects.equal(mContentValues, other.mContentValues);
    120         }
    121     }
    122 
    123     public static RawContact createFrom(Entity entity) {
    124         final ContentValues values = entity.getEntityValues();
    125         final ArrayList<Entity.NamedContentValues> subValues = entity.getSubValues();
    126 
    127         RawContact rawContact = new RawContact(values);
    128         for (Entity.NamedContentValues subValue : subValues) {
    129             rawContact.addNamedDataItemValues(subValue.uri, subValue.values);
    130         }
    131         return rawContact;
    132     }
    133 
    134     /**
    135      * A RawContact object can be created with or without a context.
    136      */
    137     public RawContact() {
    138         this(new ContentValues());
    139     }
    140 
    141     public RawContact(ContentValues values) {
    142         mValues = values;
    143         mDataItems = new ArrayList<NamedDataItem>();
    144     }
    145 
    146     /**
    147      * Constructor for the parcelable.
    148      *
    149      * @param parcel The parcel to de-serialize from.
    150      */
    151     private RawContact(Parcel parcel) {
    152         mValues = parcel.readParcelable(ContentValues.class.getClassLoader());
    153         mDataItems = Lists.newArrayList();
    154         parcel.readTypedList(mDataItems, NamedDataItem.CREATOR);
    155     }
    156 
    157     @Override
    158     public int describeContents() {
    159         return 0;
    160     }
    161 
    162     @Override
    163     public void writeToParcel(Parcel parcel, int i) {
    164         parcel.writeParcelable(mValues, i);
    165         parcel.writeTypedList(mDataItems);
    166     }
    167 
    168     /**
    169      * Create for building the parcelable.
    170      */
    171     public static final Parcelable.Creator<RawContact> CREATOR
    172             = new Parcelable.Creator<RawContact>() {
    173 
    174         @Override
    175         public RawContact createFromParcel(Parcel parcel) {
    176             return new RawContact(parcel);
    177         }
    178 
    179         @Override
    180         public RawContact[] newArray(int i) {
    181             return new RawContact[i];
    182         }
    183     };
    184 
    185     public AccountTypeManager getAccountTypeManager(Context context) {
    186         if (mAccountTypeManager == null) {
    187             mAccountTypeManager = AccountTypeManager.getInstance(context);
    188         }
    189         return mAccountTypeManager;
    190     }
    191 
    192     public ContentValues getValues() {
    193         return mValues;
    194     }
    195 
    196     /**
    197      * Returns the id of the raw contact.
    198      */
    199     public Long getId() {
    200         return getValues().getAsLong(RawContacts._ID);
    201     }
    202 
    203     /**
    204      * Returns the account name of the raw contact.
    205      */
    206     public String getAccountName() {
    207         return getValues().getAsString(RawContacts.ACCOUNT_NAME);
    208     }
    209 
    210     /**
    211      * Returns the account type of the raw contact.
    212      */
    213     public String getAccountTypeString() {
    214         return getValues().getAsString(RawContacts.ACCOUNT_TYPE);
    215     }
    216 
    217     /**
    218      * Returns the data set of the raw contact.
    219      */
    220     public String getDataSet() {
    221         return getValues().getAsString(RawContacts.DATA_SET);
    222     }
    223 
    224     /**
    225      * Returns the account type and data set of the raw contact.
    226      */
    227     public String getAccountTypeAndDataSetString() {
    228         return getValues().getAsString(RawContacts.ACCOUNT_TYPE_AND_DATA_SET);
    229     }
    230 
    231     public boolean isDirty() {
    232         return getValues().getAsBoolean(RawContacts.DIRTY);
    233     }
    234 
    235     public String getSourceId() {
    236         return getValues().getAsString(RawContacts.SOURCE_ID);
    237     }
    238 
    239     public String getSync1() {
    240         return getValues().getAsString(RawContacts.SYNC1);
    241     }
    242 
    243     public String getSync2() {
    244         return getValues().getAsString(RawContacts.SYNC2);
    245     }
    246 
    247     public String getSync3() {
    248         return getValues().getAsString(RawContacts.SYNC3);
    249     }
    250 
    251     public String getSync4() {
    252         return getValues().getAsString(RawContacts.SYNC4);
    253     }
    254 
    255     public boolean isDeleted() {
    256         return getValues().getAsBoolean(RawContacts.DELETED);
    257     }
    258 
    259     public boolean isNameVerified() {
    260         return getValues().getAsBoolean(RawContacts.NAME_VERIFIED);
    261     }
    262 
    263     public long getContactId() {
    264         return getValues().getAsLong(Contacts.Entity.CONTACT_ID);
    265     }
    266 
    267     public boolean isStarred() {
    268         return getValues().getAsBoolean(Contacts.STARRED);
    269     }
    270 
    271     public AccountType getAccountType(Context context) {
    272         return getAccountTypeManager(context).getAccountType(getAccountTypeString(), getDataSet());
    273     }
    274 
    275     /**
    276      * Sets the account name, account type, and data set strings.
    277      * Valid combinations for account-name, account-type, data-set
    278      * 1) null, null, null (local account)
    279      * 2) non-null, non-null, null (valid account without data-set)
    280      * 3) non-null, non-null, non-null (valid account with data-set)
    281      */
    282     private void setAccount(String accountName, String accountType, String dataSet) {
    283         final ContentValues values = getValues();
    284         if (accountName == null) {
    285             if (accountType == null && dataSet == null) {
    286                 // This is a local account
    287                 values.putNull(RawContacts.ACCOUNT_NAME);
    288                 values.putNull(RawContacts.ACCOUNT_TYPE);
    289                 values.putNull(RawContacts.DATA_SET);
    290                 return;
    291             }
    292         } else {
    293             if (accountType != null) {
    294                 // This is a valid account, either with or without a dataSet.
    295                 values.put(RawContacts.ACCOUNT_NAME, accountName);
    296                 values.put(RawContacts.ACCOUNT_TYPE, accountType);
    297                 if (dataSet == null) {
    298                     values.putNull(RawContacts.DATA_SET);
    299                 } else {
    300                     values.put(RawContacts.DATA_SET, dataSet);
    301                 }
    302                 return;
    303             }
    304         }
    305         throw new IllegalArgumentException(
    306                 "Not a valid combination of account name, type, and data set.");
    307     }
    308 
    309     public void setAccount(AccountWithDataSet accountWithDataSet) {
    310         setAccount(accountWithDataSet.name, accountWithDataSet.type, accountWithDataSet.dataSet);
    311     }
    312 
    313     public void setAccountToLocal() {
    314         setAccount(null, null, null);
    315     }
    316 
    317     /**
    318      * Creates and inserts a DataItem object that wraps the content values, and returns it.
    319      */
    320     public void addDataItemValues(ContentValues values) {
    321         addNamedDataItemValues(Data.CONTENT_URI, values);
    322     }
    323 
    324     public NamedDataItem addNamedDataItemValues(Uri uri, ContentValues values) {
    325         final NamedDataItem namedItem = new NamedDataItem(uri, values);
    326         mDataItems.add(namedItem);
    327         return namedItem;
    328     }
    329 
    330     public ArrayList<ContentValues> getContentValues() {
    331         final ArrayList<ContentValues> list = Lists.newArrayListWithCapacity(mDataItems.size());
    332         for (NamedDataItem dataItem : mDataItems) {
    333             if (Data.CONTENT_URI.equals(dataItem.mUri)) {
    334                 list.add(dataItem.mContentValues);
    335             }
    336         }
    337         return list;
    338     }
    339 
    340     public List<DataItem> getDataItems() {
    341         final ArrayList<DataItem> list = Lists.newArrayListWithCapacity(mDataItems.size());
    342         for (NamedDataItem dataItem : mDataItems) {
    343             if (Data.CONTENT_URI.equals(dataItem.mUri)) {
    344                 list.add(DataItem.createFrom(dataItem.mContentValues));
    345             }
    346         }
    347         return list;
    348     }
    349 
    350     public String toString() {
    351         final StringBuilder sb = new StringBuilder();
    352         sb.append("RawContact: ").append(mValues);
    353         for (RawContact.NamedDataItem namedDataItem : mDataItems) {
    354             sb.append("\n  ").append(namedDataItem.mUri);
    355             sb.append("\n  -> ").append(namedDataItem.mContentValues);
    356         }
    357         return sb.toString();
    358     }
    359 
    360     @Override
    361     public int hashCode() {
    362         return Objects.hashCode(mValues, mDataItems);
    363     }
    364 
    365     @Override
    366     public boolean equals(Object obj) {
    367         if (obj == null) return false;
    368         if (getClass() != obj.getClass()) return false;
    369 
    370         RawContact other = (RawContact) obj;
    371         return Objects.equal(mValues, other.mValues) &&
    372                 Objects.equal(mDataItems, other.mDataItems);
    373     }
    374 }
    375