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