Home | History | Annotate | Download | only in model
      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 
     83     public synchronized boolean isInflated(int inflateLevel) {
     84         return mInflatedLevel >= inflateLevel;
     85     }
     86 
     87     /** @hide exposed for unit tests */
     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      */
     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      */
    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      */
    113     public synchronized void invalidateCache() {
    114         this.mKinds.clear();
    115         this.mMimeKinds.clear();
    116         setInflatedLevel(LEVEL_NONE);
    117     }
    118 
    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 
    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 
    141     abstract public int getHeaderColor(Context context);
    142 
    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      */
    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      */
    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      */
    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         /**
    200          * If this is true (default), the user can add and remove values.
    201          * If false, the editor will always show a single field (which might be empty).
    202          */
    203         public boolean isList;
    204 
    205         public StringInflater actionHeader;
    206         public StringInflater actionAltHeader;
    207         public StringInflater actionBody;
    208 
    209         public boolean actionBodySocial = false;
    210 
    211         public String typeColumn;
    212 
    213         /**
    214          * Maximum number of values allowed in the list. -1 represents infinity.
    215          * If {@link DataKind#isList} is false, this value is ignored.
    216          */
    217         public int typeOverallMax;
    218 
    219         public List<EditType> typeList;
    220         public List<EditField> fieldList;
    221 
    222         public ContentValues defaultValues;
    223 
    224         public DataKind() {
    225         }
    226 
    227         public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable) {
    228             this.mimeType = mimeType;
    229             this.titleRes = titleRes;
    230             this.iconRes = iconRes;
    231             this.weight = weight;
    232             this.editable = editable;
    233             this.isList = true;
    234             this.typeOverallMax = -1;
    235         }
    236     }
    237 
    238     /**
    239      * Description of a specific "type" or "label" of a {@link DataKind} row,
    240      * such as {@link Phone#TYPE_WORK}. Includes constraints on total number of
    241      * rows a {@link Contacts} may have of this type, and details on how
    242      * user-defined labels are stored.
    243      */
    244     public static class EditType {
    245         public int rawValue;
    246         public int labelRes;
    247 //        public int actionRes;
    248 //        public int actionAltRes;
    249         public boolean secondary;
    250         public int specificMax;
    251         public String customColumn;
    252 
    253         public EditType(int rawValue, int labelRes) {
    254             this.rawValue = rawValue;
    255             this.labelRes = labelRes;
    256             this.specificMax = -1;
    257         }
    258 
    259         public EditType setSecondary(boolean secondary) {
    260             this.secondary = secondary;
    261             return this;
    262         }
    263 
    264         public EditType setSpecificMax(int specificMax) {
    265             this.specificMax = specificMax;
    266             return this;
    267         }
    268 
    269         public EditType setCustomColumn(String customColumn) {
    270             this.customColumn = customColumn;
    271             return this;
    272         }
    273 
    274         @Override
    275         public boolean equals(Object object) {
    276             if (object instanceof EditType) {
    277                 final EditType other = (EditType)object;
    278                 return other.rawValue == rawValue;
    279             }
    280             return false;
    281         }
    282 
    283         @Override
    284         public int hashCode() {
    285             return rawValue;
    286         }
    287     }
    288 
    289     /**
    290      * Description of a user-editable field on a {@link DataKind} row, such as
    291      * {@link Phone#NUMBER}. Includes flags to apply to an {@link EditText}, and
    292      * the column where this field is stored.
    293      */
    294     public static class EditField {
    295         public String column;
    296         public int titleRes;
    297         public int inputType;
    298         public int minLines;
    299         public boolean optional;
    300 
    301         public EditField(String column, int titleRes) {
    302             this.column = column;
    303             this.titleRes = titleRes;
    304         }
    305 
    306         public EditField(String column, int titleRes, int inputType) {
    307             this(column, titleRes);
    308             this.inputType = inputType;
    309         }
    310 
    311         public EditField setOptional(boolean optional) {
    312             this.optional = optional;
    313             return this;
    314         }
    315     }
    316 
    317     /**
    318      * Generic method of inflating a given {@link Cursor} into a user-readable
    319      * {@link CharSequence}. For example, an inflater could combine the multiple
    320      * columns of {@link StructuredPostal} together using a string resource
    321      * before presenting to the user.
    322      */
    323     public interface StringInflater {
    324         public CharSequence inflateUsing(Context context, Cursor cursor);
    325         public CharSequence inflateUsing(Context context, ContentValues values);
    326     }
    327 
    328 }
    329