• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 com.google.android.collect.Lists;
20 import com.google.android.collect.Maps;
21 
22 import android.accounts.Account;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.database.Cursor;
27 import android.graphics.drawable.Drawable;
28 import android.provider.ContactsContract.Contacts;
29 import android.provider.ContactsContract.Data;
30 import android.provider.ContactsContract.RawContacts;
31 import android.provider.ContactsContract.CommonDataKinds.Phone;
32 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
33 import android.widget.EditText;
34 
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.HashMap;
39 import java.util.List;
40 
41 /**
42  * Internal structure that represents constraints and styles for a specific data
43  * source, such as the various data types they support, including details on how
44  * those types should be rendered and edited.
45  * <p>
46  * In the future this may be inflated from XML defined by a data source.
47  */
48 public abstract class ContactsSource {
49     /**
50      * The {@link RawContacts#ACCOUNT_TYPE} these constraints apply to.
51      */
52     public String accountType = null;
53 
54     /**
55      * Package that resources should be loaded from, either defined through an
56      * {@link Account} or for matching against {@link Data#RES_PACKAGE}.
57      */
58     public String resPackageName;
59     public String summaryResPackageName;
60 
61     public int titleRes;
62     public int iconRes;
63 
64     public boolean readOnly;
65 
66     /**
67      * Set of {@link DataKind} supported by this source.
68      */
69     private ArrayList<DataKind> mKinds = Lists.newArrayList();
70 
71     /**
72      * Lookup map of {@link #mKinds} on {@link DataKind#mimeType}.
73      */
74     private HashMap<String, DataKind> mMimeKinds = Maps.newHashMap();
75 
76     public static final int LEVEL_NONE = 0;
77     public static final int LEVEL_SUMMARY = 1;
78     public static final int LEVEL_MIMETYPES = 2;
79     public static final int LEVEL_CONSTRAINTS = 3;
80 
81     private int mInflatedLevel = LEVEL_NONE;
82 
isInflated(int inflateLevel)83     public synchronized boolean isInflated(int inflateLevel) {
84         return mInflatedLevel >= inflateLevel;
85     }
86 
87     /** @hide exposed for unit tests */
setInflatedLevel(int inflateLevel)88     public void setInflatedLevel(int inflateLevel) {
89         mInflatedLevel = inflateLevel;
90     }
91 
92     /**
93      * Ensure that this {@link ContactsSource} has been inflated to the
94      * requested level.
95      */
ensureInflated(Context context, int inflateLevel)96     public synchronized void ensureInflated(Context context, int inflateLevel) {
97         if (!isInflated(inflateLevel)) {
98             inflate(context, inflateLevel);
99         }
100     }
101 
102     /**
103      * Perform the actual inflation to the requested level. Called by
104      * {@link #ensureInflated(Context, int)} when inflation is needed.
105      */
inflate(Context context, int inflateLevel)106     protected abstract void inflate(Context context, int inflateLevel);
107 
108     /**
109      * Invalidate any cache for this {@link ContactsSource}, removing all
110      * inflated data. Calling {@link #ensureInflated(Context, int)} will
111      * populate again from scratch.
112      */
invalidateCache()113     public synchronized void invalidateCache() {
114         this.mKinds.clear();
115         this.mMimeKinds.clear();
116         setInflatedLevel(LEVEL_NONE);
117     }
118 
getDisplayLabel(Context context)119     public CharSequence getDisplayLabel(Context context) {
120         if (this.titleRes != -1 && this.summaryResPackageName != null) {
121             final PackageManager pm = context.getPackageManager();
122             return pm.getText(this.summaryResPackageName, this.titleRes, null);
123         } else if (this.titleRes != -1) {
124             return context.getText(this.titleRes);
125         } else {
126             return this.accountType;
127         }
128     }
129 
getDisplayIcon(Context context)130     public Drawable getDisplayIcon(Context context) {
131         if (this.titleRes != -1 && this.summaryResPackageName != null) {
132             final PackageManager pm = context.getPackageManager();
133             return pm.getDrawable(this.summaryResPackageName, this.iconRes, null);
134         } else if (this.titleRes != -1) {
135             return context.getResources().getDrawable(this.iconRes);
136         } else {
137             return null;
138         }
139     }
140 
getHeaderColor(Context context)141     abstract public int getHeaderColor(Context context);
142 
getSideBarColor(Context context)143     abstract public int getSideBarColor(Context context);
144 
145     /**
146      * {@link Comparator} to sort by {@link DataKind#weight}.
147      */
148     private static Comparator<DataKind> sWeightComparator = new Comparator<DataKind>() {
149         public int compare(DataKind object1, DataKind object2) {
150             return object1.weight - object2.weight;
151         }
152     };
153 
154     /**
155      * Return list of {@link DataKind} supported, sorted by
156      * {@link DataKind#weight}.
157      */
getSortedDataKinds()158     public ArrayList<DataKind> getSortedDataKinds() {
159         // TODO: optimize by marking if already sorted
160         Collections.sort(mKinds, sWeightComparator);
161         return mKinds;
162     }
163 
164     /**
165      * Find the {@link DataKind} for a specific MIME-type, if it's handled by
166      * this data source. If you may need a fallback {@link DataKind}, use
167      * {@link Sources#getKindOrFallback(String, String, Context, int)}.
168      */
getKindForMimetype(String mimeType)169     public DataKind getKindForMimetype(String mimeType) {
170         return this.mMimeKinds.get(mimeType);
171     }
172 
173     /**
174      * Add given {@link DataKind} to list of those provided by this source.
175      */
addKind(DataKind kind)176     public DataKind addKind(DataKind kind) {
177         kind.resPackageName = this.resPackageName;
178         this.mKinds.add(kind);
179         this.mMimeKinds.put(kind.mimeType, kind);
180         return kind;
181     }
182 
183     /**
184      * Description of a specific data type, usually marked by a unique
185      * {@link Data#MIMETYPE}. Includes details about how to view and edit
186      * {@link Data} rows of this kind, including the possible {@link EditType}
187      * labels and editable {@link EditField}.
188      */
189     public static class DataKind {
190         public String resPackageName;
191         public String mimeType;
192         public int titleRes;
193         public int iconRes;
194         public int iconAltRes;
195         public int weight;
196         public boolean secondary;
197         public boolean editable;
198 
199         public StringInflater actionHeader;
200         public StringInflater actionAltHeader;
201         public StringInflater actionBody;
202 
203         public boolean actionBodySocial = false;
204 
205         public String typeColumn;
206         public int typeOverallMax;
207 
208         public List<EditType> typeList;
209         public List<EditField> fieldList;
210 
211         public ContentValues defaultValues;
212 
DataKind()213         public DataKind() {
214         }
215 
DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable)216         public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable) {
217             this.mimeType = mimeType;
218             this.titleRes = titleRes;
219             this.iconRes = iconRes;
220             this.weight = weight;
221             this.editable = editable;
222             this.typeOverallMax = -1;
223         }
224     }
225 
226     /**
227      * Description of a specific "type" or "label" of a {@link DataKind} row,
228      * such as {@link Phone#TYPE_WORK}. Includes constraints on total number of
229      * rows a {@link Contacts} may have of this type, and details on how
230      * user-defined labels are stored.
231      */
232     public static class EditType {
233         public int rawValue;
234         public int labelRes;
235 //        public int actionRes;
236 //        public int actionAltRes;
237         public boolean secondary;
238         public int specificMax;
239         public String customColumn;
240 
EditType(int rawValue, int labelRes)241         public EditType(int rawValue, int labelRes) {
242             this.rawValue = rawValue;
243             this.labelRes = labelRes;
244             this.specificMax = -1;
245         }
246 
setSecondary(boolean secondary)247         public EditType setSecondary(boolean secondary) {
248             this.secondary = secondary;
249             return this;
250         }
251 
setSpecificMax(int specificMax)252         public EditType setSpecificMax(int specificMax) {
253             this.specificMax = specificMax;
254             return this;
255         }
256 
setCustomColumn(String customColumn)257         public EditType setCustomColumn(String customColumn) {
258             this.customColumn = customColumn;
259             return this;
260         }
261 
262         @Override
equals(Object object)263         public boolean equals(Object object) {
264             if (object instanceof EditType) {
265                 final EditType other = (EditType)object;
266                 return other.rawValue == rawValue;
267             }
268             return false;
269         }
270 
271         @Override
hashCode()272         public int hashCode() {
273             return rawValue;
274         }
275     }
276 
277     /**
278      * Description of a user-editable field on a {@link DataKind} row, such as
279      * {@link Phone#NUMBER}. Includes flags to apply to an {@link EditText}, and
280      * the column where this field is stored.
281      */
282     public static class EditField {
283         public String column;
284         public int titleRes;
285         public int inputType;
286         public int minLines;
287         public boolean optional;
288 
EditField(String column, int titleRes)289         public EditField(String column, int titleRes) {
290             this.column = column;
291             this.titleRes = titleRes;
292         }
293 
EditField(String column, int titleRes, int inputType)294         public EditField(String column, int titleRes, int inputType) {
295             this(column, titleRes);
296             this.inputType = inputType;
297         }
298 
setOptional(boolean optional)299         public EditField setOptional(boolean optional) {
300             this.optional = optional;
301             return this;
302         }
303     }
304 
305     /**
306      * Generic method of inflating a given {@link Cursor} into a user-readable
307      * {@link CharSequence}. For example, an inflater could combine the multiple
308      * columns of {@link StructuredPostal} together using a string resource
309      * before presenting to the user.
310      */
311     public interface StringInflater {
inflateUsing(Context context, Cursor cursor)312         public CharSequence inflateUsing(Context context, Cursor cursor);
inflateUsing(Context context, ContentValues values)313         public CharSequence inflateUsing(Context context, ContentValues values);
314     }
315 
316 }
317