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