DropdownChipLayouter.java revision 4ddcdaed9ef5ea83910a0513e87538130270e2e4
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        final View itemView = reuseOrInflateView(convertView, parent, type);
68
69        final ViewHolder viewHolder = new ViewHolder(itemView);
70
71        // Hide some information depending on the entry type and adapter type
72        switch (type) {
73            case BASE_RECIPIENT:
74                if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, destination)) {
75                    displayName = destination;
76
77                    // We only show the destination for secondary entries, so clear it only for the
78                    // first level.
79                    if (entry.isFirstLevel()) {
80                        destination = null;
81                    }
82                }
83
84                if (!entry.isFirstLevel()) {
85                    displayName = null;
86                    showImage = false;
87                }
88                break;
89            case RECIPIENT_ALTERNATES:
90                if (position != 0) {
91                    displayName = null;
92                    showImage = false;
93                }
94                break;
95            case SINGLE_RECIPIENT:
96                destination = Rfc822Tokenizer.tokenize(entry.getDestination())[0].getAddress();
97                destinationType = null;
98        }
99
100        // Bind the information to the view
101        bindTextToView(displayName, viewHolder.displayNameView);
102        bindTextToView(destination, viewHolder.destinationView);
103        bindTextToView(destinationType, viewHolder.destinationTypeView);
104        bindIconToView(showImage, entry, viewHolder.imageView, type);
105
106        return itemView;
107    }
108
109    /**
110     * Returns a new view with {@link #getItemLayoutResId(AdapterType)}.
111     */
112    public View newView(AdapterType type) {
113        return mInflater.inflate(getItemLayoutResId(type), null);
114    }
115
116    /**
117     * Returns the same view, or inflates a new one if the given view was null.
118     */
119    protected View reuseOrInflateView(View convertView, ViewGroup parent, AdapterType type) {
120        int itemLayout = getItemLayoutResId(type);
121        switch (type) {
122            case BASE_RECIPIENT:
123            case RECIPIENT_ALTERNATES:
124                break;
125            case SINGLE_RECIPIENT:
126                itemLayout = getAlternateItemLayoutResId(type);
127                break;
128        }
129        return convertView != null ? convertView : mInflater.inflate(itemLayout, parent, false);
130    }
131
132    /**
133     * Binds the text to the given text view. If the text was null, hides the text view.
134     */
135    protected void bindTextToView(CharSequence text, TextView view) {
136        if (view == null) {
137            return;
138        }
139
140        if (text != null) {
141            view.setText(text);
142            view.setVisibility(View.VISIBLE);
143        } else {
144            view.setVisibility(View.GONE);
145        }
146    }
147
148    /**
149     * Binds the avatar icon to the image view. If we don't want to show the image, hides the
150     * image view.
151     */
152    protected void bindIconToView(boolean showImage, RecipientEntry entry, ImageView view,
153        AdapterType type) {
154        if (view == null) {
155            return;
156        }
157
158        if (showImage) {
159            switch (type) {
160                case BASE_RECIPIENT:
161                    byte[] photoBytes = entry.getPhotoBytes();
162                    if (photoBytes != null && photoBytes.length > 0) {
163                        final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
164                            photoBytes.length);
165                        view.setImageBitmap(photo);
166                    } else {
167                        view.setImageResource(getDefaultPhotoResId());
168                    }
169                    break;
170                case RECIPIENT_ALTERNATES:
171                    Uri thumbnailUri = entry.getPhotoThumbnailUri();
172                    if (thumbnailUri != null) {
173                        // TODO: see if this needs to be done outside the main thread
174                        // as it may be too slow to get immediately.
175                        view.setImageURI(thumbnailUri);
176                    } else {
177                        view.setImageResource(getDefaultPhotoResId());
178                    }
179                    break;
180                case SINGLE_RECIPIENT:
181                default:
182                    break;
183            }
184            view.setVisibility(View.VISIBLE);
185        } else {
186            view.setVisibility(View.GONE);
187        }
188    }
189
190    protected CharSequence getDestinationType(RecipientEntry entry) {
191        return mQuery.getTypeLabel(mContext.getResources(), entry.getDestinationType(),
192            entry.getDestinationLabel()).toString().toUpperCase();
193    }
194
195    /**
196     * Returns a layout id for each item inside auto-complete list.
197     *
198     * Each View must contain two TextViews (for display name and destination) and one ImageView
199     * (for photo). Ids for those should be available via {@link #getDisplayNameResId()},
200     * {@link #getDestinationResId()}, and {@link #getPhotoResId()}.
201     */
202    protected @LayoutRes int getItemLayoutResId(AdapterType type) {
203        switch (type) {
204            case BASE_RECIPIENT:
205                return R.layout.chips_autocomplete_recipient_dropdown_item;
206            case RECIPIENT_ALTERNATES:
207                return R.layout.chips_recipient_dropdown_item;
208            default:
209                return R.layout.chips_recipient_dropdown_item;
210        }
211    }
212
213    /**
214     * Returns a layout id for each item inside alternate auto-complete list.
215     *
216     * Each View must contain two TextViews (for display name and destination) and one ImageView
217     * (for photo). Ids for those should be available via {@link #getDisplayNameResId()},
218     * {@link #getDestinationResId()}, and {@link #getPhotoResId()}.
219     */
220    protected @LayoutRes int getAlternateItemLayoutResId(AdapterType type) {
221        switch (type) {
222            case BASE_RECIPIENT:
223                return R.layout.chips_autocomplete_recipient_dropdown_item;
224            case RECIPIENT_ALTERNATES:
225                return R.layout.chips_recipient_dropdown_item;
226            default:
227                return R.layout.chips_recipient_dropdown_item;
228        }
229    }
230
231    /**
232     * Returns a resource ID representing an image which should be shown when ther's no relevant
233     * photo is available.
234     */
235    protected int getDefaultPhotoResId() {
236        return R.drawable.ic_contact_picture;
237    }
238
239    /**
240     * Returns an id for TextView in an item View for showing a display name. By default
241     * {@link android.R.id#title} is returned.
242     */
243    protected int getDisplayNameResId() {
244        return android.R.id.title;
245    }
246
247    /**
248     * Returns an id for TextView in an item View for showing a destination
249     * (an email address or a phone number).
250     * By default {@link android.R.id#text1} is returned.
251     */
252    protected int getDestinationResId() {
253        return android.R.id.text1;
254    }
255
256    /**
257     * Returns an id for TextView in an item View for showing the type of the destination.
258     * By default {@link android.R.id#text2} is returned.
259     */
260    protected int getDestinationTypeResId() {
261        return android.R.id.text2;
262    }
263
264    /**
265     * Returns an id for ImageView in an item View for showing photo image for a person. In default
266     * {@link android.R.id#icon} is returned.
267     */
268    protected int getPhotoResId() {
269        return android.R.id.icon;
270    }
271
272    /**
273     * A holder class the view. Uses the getters in DropdownChipLayouter to find the id of the
274     * corresponding views.
275     */
276    protected class ViewHolder {
277        public final TextView displayNameView;
278        public final TextView destinationView;
279        public final TextView destinationTypeView;
280        public final ImageView imageView;
281
282        public ViewHolder(View view) {
283            displayNameView = (TextView) view.findViewById(getDisplayNameResId());
284            destinationView = (TextView) view.findViewById(getDestinationResId());
285            destinationTypeView = (TextView) view.findViewById(getDestinationTypeResId());
286            imageView = (ImageView) view.findViewById(getPhotoResId());
287        }
288    }
289}
290