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.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.model.account.AccountType;
     30 import com.android.contacts.model.account.AccountWithDataSet;
     31 import com.android.contacts.model.dataitem.DataItem;
     32 
     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     public boolean isDirty() {
    225         return getValues().getAsBoolean(RawContacts.DIRTY);
    226     }
    227 
    228     public String getSourceId() {
    229         return getValues().getAsString(RawContacts.SOURCE_ID);
    230     }
    231 
    232     public String getSync1() {
    233         return getValues().getAsString(RawContacts.SYNC1);
    234     }
    235 
    236     public String getSync2() {
    237         return getValues().getAsString(RawContacts.SYNC2);
    238     }
    239 
    240     public String getSync3() {
    241         return getValues().getAsString(RawContacts.SYNC3);
    242     }
    243 
    244     public String getSync4() {
    245         return getValues().getAsString(RawContacts.SYNC4);
    246     }
    247 
    248     public boolean isDeleted() {
    249         return getValues().getAsBoolean(RawContacts.DELETED);
    250     }
    251 
    252     public long getContactId() {
    253         return getValues().getAsLong(Contacts.Entity.CONTACT_ID);
    254     }
    255 
    256     public boolean isStarred() {
    257         return getValues().getAsBoolean(Contacts.STARRED);
    258     }
    259 
    260     public AccountType getAccountType(Context context) {
    261         return getAccountTypeManager(context).getAccountType(getAccountTypeString(), getDataSet());
    262     }
    263 
    264     /**
    265      * Sets the account name, account type, and data set strings.
    266      * Valid combinations for account-name, account-type, data-set
    267      * 1) null, null, null (local account)
    268      * 2) non-null, non-null, null (valid account without data-set)
    269      * 3) non-null, non-null, non-null (valid account with data-set)
    270      */
    271     private void setAccount(String accountName, String accountType, String dataSet) {
    272         final ContentValues values = getValues();
    273         if (accountName == null) {
    274             if (accountType == null && dataSet == null) {
    275                 // This is a local account
    276                 values.putNull(RawContacts.ACCOUNT_NAME);
    277                 values.putNull(RawContacts.ACCOUNT_TYPE);
    278                 values.putNull(RawContacts.DATA_SET);
    279                 return;
    280             }
    281         } else {
    282             if (accountType != null) {
    283                 // This is a valid account, either with or without a dataSet.
    284                 values.put(RawContacts.ACCOUNT_NAME, accountName);
    285                 values.put(RawContacts.ACCOUNT_TYPE, accountType);
    286                 if (dataSet == null) {
    287                     values.putNull(RawContacts.DATA_SET);
    288                 } else {
    289                     values.put(RawContacts.DATA_SET, dataSet);
    290                 }
    291                 return;
    292             }
    293         }
    294         throw new IllegalArgumentException(
    295                 "Not a valid combination of account name, type, and data set.");
    296     }
    297 
    298     public void setAccount(AccountWithDataSet accountWithDataSet) {
    299         if (accountWithDataSet != null) {
    300             setAccount(accountWithDataSet.name, accountWithDataSet.type,
    301                     accountWithDataSet.dataSet);
    302         } else {
    303             setAccount(null, null, null);
    304         }
    305     }
    306 
    307     public void setAccountToLocal() {
    308         setAccount(null, null, null);
    309     }
    310 
    311     /**
    312      * Creates and inserts a DataItem object that wraps the content values, and returns it.
    313      */
    314     public void addDataItemValues(ContentValues values) {
    315         addNamedDataItemValues(Data.CONTENT_URI, values);
    316     }
    317 
    318     public NamedDataItem addNamedDataItemValues(Uri uri, ContentValues values) {
    319         final NamedDataItem namedItem = new NamedDataItem(uri, values);
    320         mDataItems.add(namedItem);
    321         return namedItem;
    322     }
    323 
    324     public ArrayList<ContentValues> getContentValues() {
    325         final ArrayList<ContentValues> list = Lists.newArrayListWithCapacity(mDataItems.size());
    326         for (NamedDataItem dataItem : mDataItems) {
    327             if (Data.CONTENT_URI.equals(dataItem.mUri)) {
    328                 list.add(dataItem.mContentValues);
    329             }
    330         }
    331         return list;
    332     }
    333 
    334     public List<DataItem> getDataItems() {
    335         final ArrayList<DataItem> list = Lists.newArrayListWithCapacity(mDataItems.size());
    336         for (NamedDataItem dataItem : mDataItems) {
    337             if (Data.CONTENT_URI.equals(dataItem.mUri)) {
    338                 list.add(DataItem.createFrom(dataItem.mContentValues));
    339             }
    340         }
    341         return list;
    342     }
    343 
    344     public String toString() {
    345         final StringBuilder sb = new StringBuilder();
    346         sb.append("RawContact: ").append(mValues);
    347         for (RawContact.NamedDataItem namedDataItem : mDataItems) {
    348             sb.append("\n  ").append(namedDataItem.mUri);
    349             sb.append("\n  -> ").append(namedDataItem.mContentValues);
    350         }
    351         return sb.toString();
    352     }
    353 
    354     @Override
    355     public int hashCode() {
    356         return Objects.hashCode(mValues, mDataItems);
    357     }
    358 
    359     @Override
    360     public boolean equals(Object obj) {
    361         if (obj == null) return false;
    362         if (getClass() != obj.getClass()) return false;
    363 
    364         RawContact other = (RawContact) obj;
    365         return Objects.equal(mValues, other.mValues) &&
    366                 Objects.equal(mDataItems, other.mDataItems);
    367     }
    368 }
    369