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
17package com.android.contacts.model;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.content.Entity;
22import android.net.Uri;
23import android.provider.ContactsContract.Contacts;
24import android.provider.ContactsContract.Data;
25import android.provider.ContactsContract.RawContacts;
26
27import com.android.contacts.model.account.AccountType;
28import com.android.contacts.model.account.AccountWithDataSet;
29import com.android.contacts.model.dataitem.DataItem;
30
31import java.util.ArrayList;
32import java.util.List;
33
34/**
35 * RawContact represents a single raw contact in the raw contacts database.
36 * It has specialized getters/setters for raw contact
37 * items, and also contains a collection of DataItem objects.  A RawContact contains the information
38 * from a single account.
39 *
40 * This allows RawContact objects to be thought of as a class with raw contact
41 * fields (like account type, name, data set, sync state, etc.) and a list of
42 * DataItem objects that represent contact information elements (like phone
43 * numbers, email, address, etc.).
44 */
45public class RawContact {
46
47    private final Context mContext;
48    private AccountTypeManager mAccountTypeManager;
49    private final ContentValues mValues;
50    private final ArrayList<NamedDataItem> mDataItems;
51
52    public static class NamedDataItem {
53        public final Uri uri;
54        public final DataItem dataItem;
55
56        public NamedDataItem(Uri uri, DataItem dataItem) {
57            this.uri = uri;
58            this.dataItem = dataItem;
59        }
60    }
61
62    public static RawContact createFrom(Entity entity) {
63        final ContentValues values = entity.getEntityValues();
64        final ArrayList<Entity.NamedContentValues> subValues = entity.getSubValues();
65
66        RawContact rawContact = new RawContact(null, values);
67        for (Entity.NamedContentValues subValue : subValues) {
68            rawContact.addNamedDataItemValues(subValue.uri, subValue.values);
69        }
70        return rawContact;
71    }
72
73    /**
74     * A RawContact object can be created with or without a context.
75     *
76     * The context is used for the buildString() member function in DataItem objects,
77     * specifically for retrieving an instance of AccountTypeManager.  It is okay to
78     * pass in null for the context in which case, you will not be able to call buildString(),
79     * getDataKind(), or getAccountType() from a DataItem object.
80     */
81    public RawContact(Context context) {
82        this(context, new ContentValues());
83    }
84
85    public RawContact(Context context, ContentValues values) {
86        mContext = context;
87        mValues = values;
88        mDataItems = new ArrayList<NamedDataItem>();
89    }
90
91    public AccountTypeManager getAccountTypeManager() {
92        if (mAccountTypeManager == null) {
93            mAccountTypeManager = AccountTypeManager.getInstance(mContext);
94        }
95        return mAccountTypeManager;
96    }
97
98    public Context getContext() {
99        return mContext;
100    }
101
102    public ContentValues getValues() {
103        return mValues;
104    }
105
106    /**
107     * Returns the id of the raw contact.
108     */
109    public Long getId() {
110        return getValues().getAsLong(RawContacts._ID);
111    }
112
113    /**
114     * Returns the account name of the raw contact.
115     */
116    public String getAccountName() {
117        return getValues().getAsString(RawContacts.ACCOUNT_NAME);
118    }
119
120    /**
121     * Returns the account type of the raw contact.
122     */
123    public String getAccountTypeString() {
124        return getValues().getAsString(RawContacts.ACCOUNT_TYPE);
125    }
126
127    /**
128     * Returns the data set of the raw contact.
129     */
130    public String getDataSet() {
131        return getValues().getAsString(RawContacts.DATA_SET);
132    }
133
134    /**
135     * Returns the account type and data set of the raw contact.
136     */
137    public String getAccountTypeAndDataSetString() {
138        return getValues().getAsString(RawContacts.ACCOUNT_TYPE_AND_DATA_SET);
139    }
140
141    public boolean isDirty() {
142        return getValues().getAsBoolean(RawContacts.DIRTY);
143    }
144
145    public long getVersion() {
146        return getValues().getAsLong(RawContacts.DIRTY);
147    }
148
149    public String getSourceId() {
150        return getValues().getAsString(RawContacts.SOURCE_ID);
151    }
152
153    public String getSync1() {
154        return getValues().getAsString(RawContacts.SYNC1);
155    }
156
157    public String getSync2() {
158        return getValues().getAsString(RawContacts.SYNC2);
159    }
160
161    public String getSync3() {
162        return getValues().getAsString(RawContacts.SYNC3);
163    }
164
165    public String getSync4() {
166        return getValues().getAsString(RawContacts.SYNC4);
167    }
168
169    public boolean isDeleted() {
170        return getValues().getAsBoolean(RawContacts.DELETED);
171    }
172
173    public boolean isNameVerified() {
174        return getValues().getAsBoolean(RawContacts.NAME_VERIFIED);
175    }
176
177    public long getContactId() {
178        return getValues().getAsLong(Contacts.Entity.CONTACT_ID);
179    }
180
181    public boolean isStarred() {
182        return getValues().getAsBoolean(Contacts.STARRED);
183    }
184
185    public AccountType getAccountType() {
186        return getAccountTypeManager().getAccountType(getAccountTypeString(), getDataSet());
187    }
188
189    /**
190     * Sets the account name, account type, and data set strings.
191     * Valid combinations for account-name, account-type, data-set
192     * 1) null, null, null (local account)
193     * 2) non-null, non-null, null (valid account without data-set)
194     * 3) non-null, non-null, non-null (valid account with data-set)
195     */
196    private void setAccount(String accountName, String accountType, String dataSet) {
197        final ContentValues values = getValues();
198        if (accountName == null) {
199            if (accountType == null && dataSet == null) {
200                // This is a local account
201                values.putNull(RawContacts.ACCOUNT_NAME);
202                values.putNull(RawContacts.ACCOUNT_TYPE);
203                values.putNull(RawContacts.DATA_SET);
204                return;
205            }
206        } else {
207            if (accountType != null) {
208                // This is a valid account, either with or without a dataSet.
209                values.put(RawContacts.ACCOUNT_NAME, accountName);
210                values.put(RawContacts.ACCOUNT_TYPE, accountType);
211                if (dataSet == null) {
212                    values.putNull(RawContacts.DATA_SET);
213                } else {
214                    values.put(RawContacts.DATA_SET, dataSet);
215                }
216                return;
217            }
218        }
219        throw new IllegalArgumentException(
220                "Not a valid combination of account name, type, and data set.");
221    }
222
223    public void setAccount(AccountWithDataSet accountWithDataSet) {
224        setAccount(accountWithDataSet.name, accountWithDataSet.type, accountWithDataSet.dataSet);
225    }
226
227    public void setAccountToLocal() {
228        setAccount(null, null, null);
229    }
230
231    /**
232     * Creates and inserts a DataItem object that wraps the content values, and returns it.
233     */
234    public DataItem addDataItemValues(ContentValues values) {
235        final NamedDataItem namedItem = addNamedDataItemValues(Data.CONTENT_URI, values);
236        return namedItem.dataItem;
237    }
238
239    public NamedDataItem addNamedDataItemValues(Uri uri, ContentValues values) {
240        final NamedDataItem namedItem = new NamedDataItem(uri, DataItem.createFrom(this, values));
241        mDataItems.add(namedItem);
242        return namedItem;
243    }
244
245    public List<DataItem> getDataItems() {
246        final ArrayList<DataItem> list = new ArrayList<DataItem>();
247        for (NamedDataItem dataItem : mDataItems) {
248            if (Data.CONTENT_URI.equals(dataItem.uri)) {
249                list.add(dataItem.dataItem);
250            }
251        }
252        return list;
253    }
254
255    public List<NamedDataItem> getNamedDataItems() {
256        return mDataItems;
257    }
258
259    public String toString() {
260        final StringBuilder sb = new StringBuilder();
261        sb.append("RawContact: ").append(mValues);
262        for (RawContact.NamedDataItem namedDataItem : mDataItems) {
263            sb.append("\n  ").append(namedDataItem.uri);
264            sb.append("\n  -> ").append(namedDataItem.dataItem.getContentValues());
265        }
266        return sb.toString();
267    }
268}
269