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