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