DropdownChipLayouter.java revision 6fc1fee597ebe3c30ebe5efebb4d6ea105641e0f
1package com.android.ex.chips; 2 3import android.content.Context; 4import android.graphics.Bitmap; 5import android.graphics.BitmapFactory; 6import android.net.Uri; 7import android.support.annotation.LayoutRes; 8import android.text.TextUtils; 9import android.text.util.Rfc822Tokenizer; 10import android.view.LayoutInflater; 11import android.view.View; 12import android.view.ViewGroup; 13import android.widget.ImageView; 14import android.widget.TextView; 15 16import com.android.ex.chips.Queries.Query; 17 18/** 19 * A class that inflates and binds the views in the dropdown list from 20 * RecipientEditTextView. 21 */ 22public class DropdownChipLayouter { 23 /** 24 * The type of adapter that is requesting a chip layout. 25 */ 26 public enum AdapterType { 27 BASE_RECIPIENT, 28 RECIPIENT_ALTERNATES, 29 SINGLE_RECIPIENT 30 } 31 32 private final LayoutInflater mInflater; 33 private final Context mContext; 34 private Query mQuery; 35 36 public DropdownChipLayouter(LayoutInflater inflater, Context context) { 37 mInflater = inflater; 38 mContext = context; 39 } 40 41 public void setQuery(Query query) { 42 mQuery = query; 43 } 44 45 46 /** 47 * Layouts and binds recipient information to the view. If convertView is null, inflates a new 48 * view with getItemLaytout(). 49 * 50 * @param convertView The view to bind information to. 51 * @param parent The parent to bind the view to if we inflate a new view. 52 * @param entry The recipient entry to get information from. 53 * @param position The position in the list. 54 * @param type The adapter type that is requesting the bind. 55 * @param constraint The constraint typed in the auto complete view. 56 * 57 * @return A view ready to be shown in the drop down list. 58 */ 59 public View bindView(View convertView, ViewGroup parent, RecipientEntry entry, int position, 60 AdapterType type, String constraint) { 61 // Default to show all the information 62 String displayName = entry.getDisplayName(); 63 String destination = entry.getDestination(); 64 boolean showImage = true; 65 CharSequence destinationType = getDestinationType(entry); 66 67 // If we don't have name but have destination, show destination as the main entry. 68 if (displayName == null && destination != null) { 69 displayName = destination; 70 destination = null; 71 } 72 73 final View itemView = reuseOrInflateView(convertView, parent, type); 74 75 final ViewHolder viewHolder = new ViewHolder(itemView); 76 77 // Hide some information depending on the entry type and adapter type 78 switch (type) { 79 case BASE_RECIPIENT: 80 if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, destination)) { 81 displayName = destination; 82 83 // We only show the destination for secondary entries, so clear it only for the 84 // first level. 85 if (entry.isFirstLevel()) { 86 destination = null; 87 } 88 } 89 90 if (!entry.isFirstLevel()) { 91 displayName = null; 92 showImage = false; 93 } 94 break; 95 case RECIPIENT_ALTERNATES: 96 if (position != 0) { 97 displayName = null; 98 showImage = false; 99 } 100 break; 101 case SINGLE_RECIPIENT: 102 destination = Rfc822Tokenizer.tokenize(entry.getDestination())[0].getAddress(); 103 destinationType = null; 104 } 105 106 // Bind the information to the view 107 bindTextToView(displayName, viewHolder.displayNameView); 108 bindTextToView(destination, viewHolder.destinationView); 109 bindTextToView(destinationType, viewHolder.destinationTypeView); 110 bindIconToView(showImage, entry, viewHolder.imageView, type); 111 112 return itemView; 113 } 114 115 /** 116 * Returns a new view with {@link #getItemLayoutResId(AdapterType)}. 117 */ 118 public View newView(AdapterType type) { 119 return mInflater.inflate(getItemLayoutResId(type), null); 120 } 121 122 /** 123 * Returns the same view, or inflates a new one if the given view was null. 124 */ 125 protected View reuseOrInflateView(View convertView, ViewGroup parent, AdapterType type) { 126 int itemLayout = getItemLayoutResId(type); 127 switch (type) { 128 case BASE_RECIPIENT: 129 case RECIPIENT_ALTERNATES: 130 break; 131 case SINGLE_RECIPIENT: 132 itemLayout = getAlternateItemLayoutResId(type); 133 break; 134 } 135 return convertView != null ? convertView : mInflater.inflate(itemLayout, parent, false); 136 } 137 138 /** 139 * Binds the text to the given text view. If the text was null, hides the text view. 140 */ 141 protected void bindTextToView(CharSequence text, TextView view) { 142 if (view == null) { 143 return; 144 } 145 146 if (text != null) { 147 view.setText(text); 148 view.setVisibility(View.VISIBLE); 149 } else { 150 view.setVisibility(View.GONE); 151 } 152 } 153 154 /** 155 * Binds the avatar icon to the image view. If we don't want to show the image, hides the 156 * image view. 157 */ 158 protected void bindIconToView(boolean showImage, RecipientEntry entry, ImageView view, 159 AdapterType type) { 160 if (view == null) { 161 return; 162 } 163 164 if (showImage) { 165 switch (type) { 166 case BASE_RECIPIENT: 167 byte[] photoBytes = entry.getPhotoBytes(); 168 if (photoBytes != null && photoBytes.length > 0) { 169 final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0, 170 photoBytes.length); 171 view.setImageBitmap(photo); 172 } else { 173 view.setImageResource(getDefaultPhotoResId()); 174 } 175 break; 176 case RECIPIENT_ALTERNATES: 177 Uri thumbnailUri = entry.getPhotoThumbnailUri(); 178 if (thumbnailUri != null) { 179 // TODO: see if this needs to be done outside the main thread 180 // as it may be too slow to get immediately. 181 view.setImageURI(thumbnailUri); 182 } else { 183 view.setImageResource(getDefaultPhotoResId()); 184 } 185 break; 186 case SINGLE_RECIPIENT: 187 default: 188 break; 189 } 190 view.setVisibility(View.VISIBLE); 191 } else { 192 view.setVisibility(View.GONE); 193 } 194 } 195 196 protected CharSequence getDestinationType(RecipientEntry entry) { 197 return mQuery.getTypeLabel(mContext.getResources(), entry.getDestinationType(), 198 entry.getDestinationLabel()).toString().toUpperCase(); 199 } 200 201 /** 202 * Returns a layout id for each item inside auto-complete list. 203 * 204 * Each View must contain two TextViews (for display name and destination) and one ImageView 205 * (for photo). Ids for those should be available via {@link #getDisplayNameResId()}, 206 * {@link #getDestinationResId()}, and {@link #getPhotoResId()}. 207 */ 208 protected @LayoutRes int getItemLayoutResId(AdapterType type) { 209 switch (type) { 210 case BASE_RECIPIENT: 211 return R.layout.chips_autocomplete_recipient_dropdown_item; 212 case RECIPIENT_ALTERNATES: 213 return R.layout.chips_recipient_dropdown_item; 214 default: 215 return R.layout.chips_recipient_dropdown_item; 216 } 217 } 218 219 /** 220 * Returns a layout id for each item inside alternate auto-complete list. 221 * 222 * Each View must contain two TextViews (for display name and destination) and one ImageView 223 * (for photo). Ids for those should be available via {@link #getDisplayNameResId()}, 224 * {@link #getDestinationResId()}, and {@link #getPhotoResId()}. 225 */ 226 protected @LayoutRes int getAlternateItemLayoutResId(AdapterType type) { 227 switch (type) { 228 case BASE_RECIPIENT: 229 return R.layout.chips_autocomplete_recipient_dropdown_item; 230 case RECIPIENT_ALTERNATES: 231 return R.layout.chips_recipient_dropdown_item; 232 default: 233 return R.layout.chips_recipient_dropdown_item; 234 } 235 } 236 237 /** 238 * Returns a resource ID representing an image which should be shown when ther's no relevant 239 * photo is available. 240 */ 241 protected int getDefaultPhotoResId() { 242 return R.drawable.ic_contact_picture; 243 } 244 245 /** 246 * Returns an id for TextView in an item View for showing a display name. By default 247 * {@link android.R.id#title} is returned. 248 */ 249 protected int getDisplayNameResId() { 250 return android.R.id.title; 251 } 252 253 /** 254 * Returns an id for TextView in an item View for showing a destination 255 * (an email address or a phone number). 256 * By default {@link android.R.id#text1} is returned. 257 */ 258 protected int getDestinationResId() { 259 return android.R.id.text1; 260 } 261 262 /** 263 * Returns an id for TextView in an item View for showing the type of the destination. 264 * By default {@link android.R.id#text2} is returned. 265 */ 266 protected int getDestinationTypeResId() { 267 return android.R.id.text2; 268 } 269 270 /** 271 * Returns an id for ImageView in an item View for showing photo image for a person. In default 272 * {@link android.R.id#icon} is returned. 273 */ 274 protected int getPhotoResId() { 275 return android.R.id.icon; 276 } 277 278 /** 279 * A holder class the view. Uses the getters in DropdownChipLayouter to find the id of the 280 * corresponding views. 281 */ 282 protected class ViewHolder { 283 public final TextView displayNameView; 284 public final TextView destinationView; 285 public final TextView destinationTypeView; 286 public final ImageView imageView; 287 288 public ViewHolder(View view) { 289 displayNameView = (TextView) view.findViewById(getDisplayNameResId()); 290 destinationView = (TextView) view.findViewById(getDestinationResId()); 291 destinationTypeView = (TextView) view.findViewById(getDestinationTypeResId()); 292 imageView = (ImageView) view.findViewById(getPhotoResId()); 293 } 294 } 295} 296