1432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler/*
2432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * Copyright (C) 2009 The Android Open Source Project
3432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler *
4432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * Licensed under the Apache License, Version 2.0 (the "License");
5432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * you may not use this file except in compliance with the License.
6432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * You may obtain a copy of the License at
7432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler *
8432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler *      http://www.apache.org/licenses/LICENSE-2.0
9432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler *
10432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * Unless required by applicable law or agreed to in writing, software
11432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * distributed under the License is distributed on an "AS IS" BASIS,
12432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * See the License for the specific language governing permissions and
14432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * limitations under the License.
15432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler */
16432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
17432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadlerpackage com.android.email.activity;
18432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
19432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadlerimport android.content.Context;
2018811dfb457eff8ba8b5436bd2641a4215480cdfBen Komaloimport android.content.res.Configuration;
21937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.content.res.Resources;
22937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.graphics.Bitmap;
23937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.graphics.BitmapFactory;
24937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.graphics.Canvas;
255b81690de1ea15035ab0539df683acd8e28b0ebdMakoto Onukiimport android.graphics.Paint;
26937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.graphics.Typeface;
27bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blankimport android.graphics.drawable.Drawable;
28937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.text.Layout.Alignment;
2906a2f4a3b1322fb6fecb908bd04e777199529ce7Marc Blankimport android.text.Spannable;
3006a2f4a3b1322fb6fecb908bd04e777199529ce7Marc Blankimport android.text.SpannableString;
3106a2f4a3b1322fb6fecb908bd04e777199529ce7Marc Blankimport android.text.SpannableStringBuilder;
32937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.text.StaticLayout;
33937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.text.TextPaint;
34937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.text.TextUtils;
35937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.text.TextUtils.TruncateAt;
36937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.text.format.DateUtils;
374beab88c92226628bf743070f88affb701968ce0Mindy Pereiraimport android.text.style.ForegroundColorSpan;
3806a2f4a3b1322fb6fecb908bd04e777199529ce7Marc Blankimport android.text.style.StyleSpan;
39432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadlerimport android.util.AttributeSet;
40432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadlerimport android.view.MotionEvent;
41937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankimport android.view.View;
428e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugoimport android.view.accessibility.AccessibilityEvent;
43432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
447f876c0375fd65d8796a96f137481a4c28593617Ben Komaloimport com.android.email.R;
457f876c0375fd65d8796a96f137481a4c28593617Ben Komaloimport com.android.emailcommon.utility.TextUtilities;
467f876c0375fd65d8796a96f137481a4c28593617Ben Komaloimport com.google.common.base.Objects;
477f876c0375fd65d8796a96f137481a4c28593617Ben Komalo
48432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler/**
49432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * This custom View is the list item for the MessageList activity, and serves two purposes:
50432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * 1.  It's a container to store message metadata (e.g. the ids of the message, mailbox, & account)
51432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler * 2.  It handles internal clicks such as the checkbox or the favorite star
52432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler */
53937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blankpublic class MessageListItem extends View {
54bdf84d57e623499d9c250fcc6fa1ba1f947cd2b5Makoto Onuki    // Note: messagesAdapter directly fiddles with these fields.
55bdf84d57e623499d9c250fcc6fa1ba1f947cd2b5Makoto Onuki    /* package */ long mMessageId;
56bdf84d57e623499d9c250fcc6fa1ba1f947cd2b5Makoto Onuki    /* package */ long mMailboxId;
57bdf84d57e623499d9c250fcc6fa1ba1f947cd2b5Makoto Onuki    /* package */ long mAccountId;
58432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
59e4202e782c91075c09cd5c348517a90620622a7fMindy Pereira    private ThreePaneLayout mLayout;
60851f3219e2bbcfb9c02c439d84d0885d890ded8dMakoto Onuki    private MessagesAdapter mAdapter;
618b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo    private MessageListItemCoordinates mCoordinates;
628b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo    private Context mContext;
63432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
64432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    private boolean mDownEvent;
65432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
66c81bef672089654e6da3babbeb0172bd636564b2Marc Blank    public static final String MESSAGE_LIST_ITEMS_CLIP_LABEL =
67c81bef672089654e6da3babbeb0172bd636564b2Marc Blank        "com.android.email.MESSAGE_LIST_ITEMS";
68c81bef672089654e6da3babbeb0172bd636564b2Marc Blank
69432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    public MessageListItem(Context context) {
70432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        super(context);
71937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        init(context);
72432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    }
73432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
74432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    public MessageListItem(Context context, AttributeSet attrs) {
75432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        super(context, attrs);
76937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        init(context);
77432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    }
78432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
79432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    public MessageListItem(Context context, AttributeSet attrs, int defStyle) {
80432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        super(context, attrs, defStyle);
81937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        init(context);
82937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    }
83937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
84937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    // Wide mode shows sender, snippet, time, and favorite spread out across the screen
858b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo    private static final int MODE_WIDE = MessageListItemCoordinates.WIDE_MODE;
86937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    // Sentinel indicating that the view needs layout
87937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    public static final int NEEDS_LAYOUT = -1;
88937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
89937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static boolean sInit = false;
90937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static final TextPaint sDefaultPaint = new TextPaint();
91937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static final TextPaint sBoldPaint = new TextPaint();
92937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static final TextPaint sDatePaint = new TextPaint();
93937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static Bitmap sAttachmentIcon;
94937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static Bitmap sInviteIcon;
95891da84a254d55c68df21998fbce9c1772eb6172Ben Komalo    private static int sBadgeMargin;
96937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static Bitmap sFavoriteIconOff;
97937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static Bitmap sFavoriteIconOn;
98937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static Bitmap sSelectedIconOn;
99937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static Bitmap sSelectedIconOff;
100e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo    private static Bitmap sStateReplied;
101e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo    private static Bitmap sStateForwarded;
102e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo    private static Bitmap sStateRepliedAndForwarded;
10306a2f4a3b1322fb6fecb908bd04e777199529ce7Marc Blank    private static String sSubjectSnippetDivider;
1048e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    private static String sSubjectDescription;
1058e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    private static String sSubjectEmptyDescription;
1064beab88c92226628bf743070f88affb701968ce0Mindy Pereira
1074beab88c92226628bf743070f88affb701968ce0Mindy Pereira    // Static colors.
1084beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int DEFAULT_TEXT_COLOR;
1094beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int ACTIVATED_TEXT_COLOR;
1104beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int LIGHT_TEXT_COLOR;
1114beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int DRAFT_TEXT_COLOR;
1124beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int SUBJECT_TEXT_COLOR_READ;
1134beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int SUBJECT_TEXT_COLOR_UNREAD;
1144beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int SNIPPET_TEXT_COLOR_READ;
1154beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int SNIPPET_TEXT_COLOR_UNREAD;
1164beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int SENDERS_TEXT_COLOR_READ;
1174beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int SENDERS_TEXT_COLOR_UNREAD;
1184beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int DATE_TEXT_COLOR_READ;
1194beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private static int DATE_TEXT_COLOR_UNREAD;
120937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
121937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    public String mSender;
1224beab88c92226628bf743070f88affb701968ce0Mindy Pereira    public SpannableStringBuilder mText;
12378684ccc795c0d5211dfc04a834cb452dccb1058Marc Blank    public CharSequence mSnippet;
1248e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    private String mSubject;
1258b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo    private StaticLayout mSubjectLayout;
126937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    public boolean mRead;
127937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    public boolean mHasAttachment = false;
128937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    public boolean mHasInvite = true;
129937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    public boolean mIsFavorite = false;
130e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo    public boolean mHasBeenRepliedTo = false;
131e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo    public boolean mHasBeenForwarded = false;
1325b81690de1ea15035ab0539df683acd8e28b0ebdMakoto Onuki    /** {@link Paint} for account color chips.  null if no chips should be drawn.  */
1335b81690de1ea15035ab0539df683acd8e28b0ebdMakoto Onuki    public Paint mColorChipPaint;
134937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
135937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private int mMode = -1;
136937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
137937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private int mViewWidth = 0;
138937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private int mViewHeight = 0;
1398b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo
140937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private static int sItemHeightWide;
141383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo    private static int sItemHeightNormal;
142eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki
143eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    // Note: these cannot be shared Drawables because they are selectors which have state.
144eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    private Drawable mReadSelector;
145eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    private Drawable mUnreadSelector;
146eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    private Drawable mWideReadSelector;
147eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    private Drawable mWideUnreadSelector;
148937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
149937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private CharSequence mFormattedSender;
150bcf5d5825fe69bf8bd7edfd077027a52abd2c8ebMarc Blank    // We must initialize this to something, in case the timestamp of the message is zero (which
151bcf5d5825fe69bf8bd7edfd077027a52abd2c8ebMarc Blank    // should be very rare); this is otherwise set in setTimestamp
152bcf5d5825fe69bf8bd7edfd077027a52abd2c8ebMarc Blank    private CharSequence mFormattedDate = "";
153937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
154937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private void init(Context context) {
1558b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        mContext = context;
156937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        if (!sInit) {
157937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            Resources r = context.getResources();
1588e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo            sSubjectDescription = r.getString(R.string.message_subject_description).concat(", ");
1598e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo            sSubjectEmptyDescription = r.getString(R.string.message_is_empty_description);
16006a2f4a3b1322fb6fecb908bd04e777199529ce7Marc Blank            sSubjectSnippetDivider = r.getString(R.string.message_list_subject_snippet_divider);
161937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sItemHeightWide =
162937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                r.getDimensionPixelSize(R.dimen.message_list_item_height_wide);
163383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo            sItemHeightNormal =
164383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                r.getDimensionPixelSize(R.dimen.message_list_item_height_normal);
165937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
166937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sDefaultPaint.setTypeface(Typeface.DEFAULT);
167937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sDefaultPaint.setAntiAlias(true);
168937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sDatePaint.setTypeface(Typeface.DEFAULT);
169937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sDatePaint.setAntiAlias(true);
1708b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo            sBoldPaint.setTypeface(Typeface.DEFAULT_BOLD);
171937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sBoldPaint.setAntiAlias(true);
1724beab88c92226628bf743070f88affb701968ce0Mindy Pereira
17364ac7a6cc81ef6dc84354153b978bd5db944e8b0Todd Kennedy            sAttachmentIcon = BitmapFactory.decodeResource(r, R.drawable.ic_badge_attachment);
1748466f79a06433bf3d05a770578c72b2b60e1bd7cBen Komalo            sInviteIcon = BitmapFactory.decodeResource(r, R.drawable.ic_badge_invite_holo_light);
175891da84a254d55c68df21998fbce9c1772eb6172Ben Komalo            sBadgeMargin = r.getDimensionPixelSize(R.dimen.message_list_badge_margin);
176937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sFavoriteIconOff =
177891da84a254d55c68df21998fbce9c1772eb6172Ben Komalo                BitmapFactory.decodeResource(r, R.drawable.btn_star_off_normal_email_holo_light);
178937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sFavoriteIconOn =
179891da84a254d55c68df21998fbce9c1772eb6172Ben Komalo                BitmapFactory.decodeResource(r, R.drawable.btn_star_on_normal_email_holo_light);
180937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sSelectedIconOff =
181937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                BitmapFactory.decodeResource(r, R.drawable.btn_check_off_normal_holo_light);
182937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sSelectedIconOn =
183937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                BitmapFactory.decodeResource(r, R.drawable.btn_check_on_normal_holo_light);
184937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
185e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo            sStateReplied =
1868466f79a06433bf3d05a770578c72b2b60e1bd7cBen Komalo                BitmapFactory.decodeResource(r, R.drawable.ic_badge_reply_holo_light);
187e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo            sStateForwarded =
1888466f79a06433bf3d05a770578c72b2b60e1bd7cBen Komalo                BitmapFactory.decodeResource(r, R.drawable.ic_badge_forward_holo_light);
189e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo            sStateRepliedAndForwarded =
1908466f79a06433bf3d05a770578c72b2b60e1bd7cBen Komalo                BitmapFactory.decodeResource(r, R.drawable.ic_badge_reply_forward_holo_light);
191e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo
1924beab88c92226628bf743070f88affb701968ce0Mindy Pereira            DEFAULT_TEXT_COLOR = r.getColor(R.color.default_text_color);
1934beab88c92226628bf743070f88affb701968ce0Mindy Pereira            ACTIVATED_TEXT_COLOR = r.getColor(android.R.color.white);
1944beab88c92226628bf743070f88affb701968ce0Mindy Pereira            SUBJECT_TEXT_COLOR_READ = r.getColor(R.color.subject_text_color_read);
1954beab88c92226628bf743070f88affb701968ce0Mindy Pereira            SUBJECT_TEXT_COLOR_UNREAD = r.getColor(R.color.subject_text_color_unread);
1964beab88c92226628bf743070f88affb701968ce0Mindy Pereira            SNIPPET_TEXT_COLOR_READ = r.getColor(R.color.snippet_text_color_read);
1974beab88c92226628bf743070f88affb701968ce0Mindy Pereira            SNIPPET_TEXT_COLOR_UNREAD = r.getColor(R.color.snippet_text_color_unread);
1984beab88c92226628bf743070f88affb701968ce0Mindy Pereira            SENDERS_TEXT_COLOR_READ = r.getColor(R.color.senders_text_color_read);
1994beab88c92226628bf743070f88affb701968ce0Mindy Pereira            SENDERS_TEXT_COLOR_UNREAD = r.getColor(R.color.senders_text_color_unread);
2004beab88c92226628bf743070f88affb701968ce0Mindy Pereira            DATE_TEXT_COLOR_READ = r.getColor(R.color.date_text_color_read);
2014beab88c92226628bf743070f88affb701968ce0Mindy Pereira            DATE_TEXT_COLOR_UNREAD = r.getColor(R.color.date_text_color_unread);
202ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo
203937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            sInit = true;
204937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        }
205937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    }
206937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
207937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    /**
20828e496daef8be15def1de3787845a22bed0c8b79Ben Komalo     * Invalidate all drawing caches associated with drawing message list items.
20928e496daef8be15def1de3787845a22bed0c8b79Ben Komalo     * This is an expensive operation, and should be done rarely, such as when system font size
21028e496daef8be15def1de3787845a22bed0c8b79Ben Komalo     * changes occurs.
21128e496daef8be15def1de3787845a22bed0c8b79Ben Komalo     */
21228e496daef8be15def1de3787845a22bed0c8b79Ben Komalo    public static void resetDrawingCaches() {
21328e496daef8be15def1de3787845a22bed0c8b79Ben Komalo        MessageListItemCoordinates.resetCaches();
21428e496daef8be15def1de3787845a22bed0c8b79Ben Komalo        sInit = false;
21528e496daef8be15def1de3787845a22bed0c8b79Ben Komalo    }
21628e496daef8be15def1de3787845a22bed0c8b79Ben Komalo
21728e496daef8be15def1de3787845a22bed0c8b79Ben Komalo    /**
2182313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo     * Sets message subject and snippet safely, ensuring the cache is invalidated.
2198e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo     */
220217dc144bd967f21850cefc6e1a84e9d35a67f85Ben Komalo    public void setText(String subject, String snippet, boolean forceUpdate) {
2212313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo        boolean changed = false;
2227f876c0375fd65d8796a96f137481a4c28593617Ben Komalo        if (!Objects.equal(mSubject, subject)) {
2238e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo            mSubject = subject;
2242313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            changed = true;
22556593af65ad25a69f81fb471d346c7519b58fbc4Jorge Lugo            populateContentDescription();
2268e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo        }
2272313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo
2282313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo        if (!Objects.equal(mSnippet, snippet)) {
2292313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            mSnippet = snippet;
2302313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            changed = true;
2312313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo        }
2322313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo
233217dc144bd967f21850cefc6e1a84e9d35a67f85Ben Komalo        if (forceUpdate || changed || (mSubject == null && mSnippet == null) /* first time */) {
2342313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            SpannableStringBuilder ssb = new SpannableStringBuilder();
2352313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            boolean hasSubject = false;
2362313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            if (!TextUtils.isEmpty(mSubject)) {
2372313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                SpannableString ss = new SpannableString(mSubject);
2382313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                ss.setSpan(new StyleSpan(mRead ? Typeface.NORMAL : Typeface.BOLD), 0, ss.length(),
2392313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2402313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                ssb.append(ss);
2412313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                hasSubject = true;
2422313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            }
2432313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            if (!TextUtils.isEmpty(mSnippet)) {
2442313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                if (hasSubject) {
2452313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                    ssb.append(sSubjectSnippetDivider);
2462313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                }
2472313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                ssb.append(mSnippet);
2482313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            }
2492313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo            mText = ssb;
250ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo            requestLayout();
2512313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo        }
2522313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo    }
2532313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo
254ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo    long mTimeFormatted = 0;
2552313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo    public void setTimestamp(long timestamp) {
256ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo        if (mTimeFormatted != timestamp) {
257ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo            mFormattedDate = DateUtils.getRelativeTimeSpanString(mContext, timestamp).toString();
258ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo            mTimeFormatted = timestamp;
259ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo        }
2608e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    }
2618e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo
2628e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    /**
263937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     * Determine the mode of this view (WIDE or NORMAL)
264937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     *
265937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     * @param width The width of the view
266937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     * @return The mode of the view
267937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     */
268937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private int getViewMode(int width) {
2698b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        return MessageListItemCoordinates.getMode(mContext, width);
270937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    }
271937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
272eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    private Drawable mCurentBackground = null; // Only used by updateBackground()
273eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki
2748b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo    private void updateBackground() {
275eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki        final Drawable newBackground;
276e4202e782c91075c09cd5c348517a90620622a7fMindy Pereira        boolean isMultiPane = MessageListItemCoordinates.isMultiPane(mContext);
277bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blank        if (mRead) {
278e4202e782c91075c09cd5c348517a90620622a7fMindy Pereira            if (isMultiPane && mLayout.isLeftPaneVisible()) {
279eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                if (mWideReadSelector == null) {
280eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                    mWideReadSelector = getContext().getResources()
28121e4fe49b68738523ebdc8c340469a46d79ed691Mindy Pereira                            .getDrawable(R.drawable.conversation_wide_read_selector);
282eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                }
283eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                newBackground = mWideReadSelector;
284bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blank            } else {
285eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                if (mReadSelector == null) {
286eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                    mReadSelector = getContext().getResources()
28721e4fe49b68738523ebdc8c340469a46d79ed691Mindy Pereira                            .getDrawable(R.drawable.conversation_read_selector);
288eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                }
289eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                newBackground = mReadSelector;
290bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blank            }
291bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blank        } else {
292e4202e782c91075c09cd5c348517a90620622a7fMindy Pereira            if (isMultiPane && mLayout.isLeftPaneVisible()) {
293eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                if (mWideUnreadSelector == null) {
2944beab88c92226628bf743070f88affb701968ce0Mindy Pereira                    mWideUnreadSelector = getContext().getResources().getDrawable(
2954beab88c92226628bf743070f88affb701968ce0Mindy Pereira                            R.drawable.conversation_wide_unread_selector);
296eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                }
297eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                newBackground = mWideUnreadSelector;
298bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blank            } else {
299eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                if (mUnreadSelector == null) {
300eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                    mUnreadSelector = getContext().getResources()
30121e4fe49b68738523ebdc8c340469a46d79ed691Mindy Pereira                            .getDrawable(R.drawable.conversation_unread_selector);
302eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                }
303eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki                newBackground = mUnreadSelector;
304bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blank            }
305bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blank        }
306eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki        if (newBackground != mCurentBackground) {
307eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki            // setBackgroundDrawable is a heavy operation.  Only call it when really needed.
308eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki            setBackgroundDrawable(newBackground);
309eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki            mCurentBackground = newBackground;
310eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki        }
311eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    }
312bd9b2ff2b31b8db24471f74d35b4f6c220a1d2e3Marc Blank
3134beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private void calculateSubjectText() {
3144beab88c92226628bf743070f88affb701968ce0Mindy Pereira        if (mText == null || mText.length() == 0) {
3154beab88c92226628bf743070f88affb701968ce0Mindy Pereira            return;
3164beab88c92226628bf743070f88affb701968ce0Mindy Pereira        }
3174beab88c92226628bf743070f88affb701968ce0Mindy Pereira        boolean hasSubject = false;
318c1d9be5dc36f64b104fcaf041dd38c28ed4d05a0Makoto Onuki        int snippetStart = 0;
3194beab88c92226628bf743070f88affb701968ce0Mindy Pereira        if (!TextUtils.isEmpty(mSubject)) {
3204beab88c92226628bf743070f88affb701968ce0Mindy Pereira            int subjectColor = getFontColor(mRead ? SUBJECT_TEXT_COLOR_READ
3214beab88c92226628bf743070f88affb701968ce0Mindy Pereira                    : SUBJECT_TEXT_COLOR_UNREAD);
3224beab88c92226628bf743070f88affb701968ce0Mindy Pereira            mText.setSpan(new ForegroundColorSpan(subjectColor), 0, mSubject.length(),
3234beab88c92226628bf743070f88affb701968ce0Mindy Pereira                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
324c1d9be5dc36f64b104fcaf041dd38c28ed4d05a0Makoto Onuki            snippetStart = mSubject.length() + 1;
3254beab88c92226628bf743070f88affb701968ce0Mindy Pereira        }
3264beab88c92226628bf743070f88affb701968ce0Mindy Pereira        if (!TextUtils.isEmpty(mSnippet)) {
3274beab88c92226628bf743070f88affb701968ce0Mindy Pereira            int snippetColor = getFontColor(mRead ? SNIPPET_TEXT_COLOR_READ
3284beab88c92226628bf743070f88affb701968ce0Mindy Pereira                    : SNIPPET_TEXT_COLOR_UNREAD);
329c1d9be5dc36f64b104fcaf041dd38c28ed4d05a0Makoto Onuki            mText.setSpan(new ForegroundColorSpan(snippetColor), snippetStart, mText.length(),
330c1d9be5dc36f64b104fcaf041dd38c28ed4d05a0Makoto Onuki                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
3314beab88c92226628bf743070f88affb701968ce0Mindy Pereira        }
3324beab88c92226628bf743070f88affb701968ce0Mindy Pereira    }
3334beab88c92226628bf743070f88affb701968ce0Mindy Pereira
334eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    private void calculateDrawingData() {
3358b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        sDefaultPaint.setTextSize(mCoordinates.subjectFontSize);
3364beab88c92226628bf743070f88affb701968ce0Mindy Pereira        calculateSubjectText();
3378b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        mSubjectLayout = new StaticLayout(mText, sDefaultPaint,
338383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                mCoordinates.subjectWidth, Alignment.ALIGN_NORMAL, 1, 0, false /* includePad */);
3398b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        if (mCoordinates.subjectLineCount < mSubjectLayout.getLineCount()) {
3408b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo            // TODO: ellipsize.
3418b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo            int end = mSubjectLayout.getLineEnd(mCoordinates.subjectLineCount - 1);
3428b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo            mSubjectLayout = new StaticLayout(mText.subSequence(0, end),
3438b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                    sDefaultPaint, mCoordinates.subjectWidth, Alignment.ALIGN_NORMAL, 1, 0, true);
344937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        }
345937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
346937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        // Now, format the sender for its width
347937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        TextPaint senderPaint = mRead ? sDefaultPaint : sBoldPaint;
348937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        // And get the ellipsized string for the calculated width
3490a73e8cd459f4839f7c54fd74c9085271a1fb806Makoto Onuki        if (TextUtils.isEmpty(mSender)) {
3500a73e8cd459f4839f7c54fd74c9085271a1fb806Makoto Onuki            mFormattedSender = "";
3510a73e8cd459f4839f7c54fd74c9085271a1fb806Makoto Onuki        } else {
352383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo            int senderWidth = mCoordinates.sendersWidth;
353383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo            senderPaint.setTextSize(mCoordinates.sendersFontSize);
3544beab88c92226628bf743070f88affb701968ce0Mindy Pereira            senderPaint.setColor(getFontColor(mRead ? SENDERS_TEXT_COLOR_READ
3554beab88c92226628bf743070f88affb701968ce0Mindy Pereira                    : SENDERS_TEXT_COLOR_UNREAD));
3560a73e8cd459f4839f7c54fd74c9085271a1fb806Makoto Onuki            mFormattedSender = TextUtils.ellipsize(mSender, senderPaint, senderWidth,
3570a73e8cd459f4839f7c54fd74c9085271a1fb806Makoto Onuki                    TruncateAt.END);
3580a73e8cd459f4839f7c54fd74c9085271a1fb806Makoto Onuki        }
359937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    }
360937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    @Override
361937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
362452b34772942f767e2ba1a14fa03bdd58b29d180Marc Blank        if (widthMeasureSpec != 0 || mViewWidth == 0) {
363452b34772942f767e2ba1a14fa03bdd58b29d180Marc Blank            mViewWidth = MeasureSpec.getSize(widthMeasureSpec);
364452b34772942f767e2ba1a14fa03bdd58b29d180Marc Blank            int mode = getViewMode(mViewWidth);
365452b34772942f767e2ba1a14fa03bdd58b29d180Marc Blank            if (mode != mMode) {
366452b34772942f767e2ba1a14fa03bdd58b29d180Marc Blank                mMode = mode;
367452b34772942f767e2ba1a14fa03bdd58b29d180Marc Blank            }
368452b34772942f767e2ba1a14fa03bdd58b29d180Marc Blank            mViewHeight = measureHeight(heightMeasureSpec, mMode);
369937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        }
370937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        setMeasuredDimension(mViewWidth, mViewHeight);
371937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    }
372937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
373937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    /**
374937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     * Determine the height of this view
375937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     *
376937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     * @param measureSpec A measureSpec packed into an int
377937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     * @param mode The current mode of this view
378937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     * @return The height of the view, honoring constraints from measureSpec
379937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank     */
380937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    private int measureHeight(int measureSpec, int mode) {
381937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        int result = 0;
382937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        int specMode = MeasureSpec.getMode(measureSpec);
383937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        int specSize = MeasureSpec.getSize(measureSpec);
384937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
385937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        if (specMode == MeasureSpec.EXACTLY) {
386937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            // We were told how big to be
387937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            result = specSize;
388937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        } else {
389937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            // Measure the text
390937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            if (mMode == MODE_WIDE) {
391937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                result = sItemHeightWide;
392937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            } else {
393383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                result = sItemHeightNormal;
394937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            }
395937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            if (specMode == MeasureSpec.AT_MOST) {
396937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                // Respect AT_MOST value if that was what is called for by
397937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                // measureSpec
398937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                result = Math.min(result, specSize);
399937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            }
400937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        }
401937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        return result;
402937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    }
403937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
404937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    @Override
405eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    public void draw(Canvas canvas) {
406eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki        // Update the background, before View.draw() draws it.
407f42d5c6234bfb26f63a711fd59076964283fb7f0Todd Kennedy        setSelected(mAdapter.isSelected(this));
408eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki        updateBackground();
409eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki        super.draw(canvas);
410eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    }
411eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki
412eaf748e6eecb4cb3644dcd950d56d1edb2d50a63Makoto Onuki    @Override
4138b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
4148b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        super.onLayout(changed, left, top, right, bottom);
4158b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo
4168b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        mCoordinates = MessageListItemCoordinates.forWidth(mContext, mViewWidth);
417ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo        calculateDrawingData();
4188b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo    }
4198b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo
4204beab88c92226628bf743070f88affb701968ce0Mindy Pereira    private int getFontColor(int defaultColor) {
4214beab88c92226628bf743070f88affb701968ce0Mindy Pereira        return isActivated() ? ACTIVATED_TEXT_COLOR : defaultColor;
4224beab88c92226628bf743070f88affb701968ce0Mindy Pereira    }
4234beab88c92226628bf743070f88affb701968ce0Mindy Pereira
4248b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo    @Override
425937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank    protected void onDraw(Canvas canvas) {
4268b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        // Draw the color chip indicating the mailbox this belongs to
4275b81690de1ea15035ab0539df683acd8e28b0ebdMakoto Onuki        if (mColorChipPaint != null) {
4288b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo            canvas.drawRect(
4298b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                    mCoordinates.chipX, mCoordinates.chipY,
4308b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                    mCoordinates.chipX + mCoordinates.chipWidth,
4318b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                    mCoordinates.chipY + mCoordinates.chipHeight,
4328b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                    mColorChipPaint);
4335b81690de1ea15035ab0539df683acd8e28b0ebdMakoto Onuki        }
4345b81690de1ea15035ab0539df683acd8e28b0ebdMakoto Onuki
435937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        // Draw the checkbox
436937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        canvas.drawBitmap(mAdapter.isSelected(this) ? sSelectedIconOn : sSelectedIconOff,
437383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                mCoordinates.checkmarkX, mCoordinates.checkmarkY, null);
438937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
439937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        // Draw the sender name
4402313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo        Paint senderPaint = mRead ? sDefaultPaint : sBoldPaint;
4414beab88c92226628bf743070f88affb701968ce0Mindy Pereira        senderPaint.setColor(getFontColor(mRead ? SENDERS_TEXT_COLOR_READ
4424beab88c92226628bf743070f88affb701968ce0Mindy Pereira                : SENDERS_TEXT_COLOR_UNREAD));
443ba0008b669146f808f5e250bf0c140fc3a638049Ben Komalo        senderPaint.setTextSize(mCoordinates.sendersFontSize);
4448b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        canvas.drawText(mFormattedSender, 0, mFormattedSender.length(),
4458b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                mCoordinates.sendersX, mCoordinates.sendersY - mCoordinates.sendersAscent,
4462313f48bdc34409a1d17bd841d440d8215eb9528Ben Komalo                senderPaint);
447937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
448e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo        // Draw the reply state. Draw nothing if neither replied nor forwarded.
449e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo        if (mHasBeenRepliedTo && mHasBeenForwarded) {
4507891106a7d6b4c406792a8a9144e5148e6bf5ddbBen Komalo            canvas.drawBitmap(sStateRepliedAndForwarded,
451383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                    mCoordinates.stateX, mCoordinates.stateY, null);
452e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo        } else if (mHasBeenRepliedTo) {
4537891106a7d6b4c406792a8a9144e5148e6bf5ddbBen Komalo            canvas.drawBitmap(sStateReplied,
454383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                    mCoordinates.stateX, mCoordinates.stateY, null);
455e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo        } else if (mHasBeenForwarded) {
4567891106a7d6b4c406792a8a9144e5148e6bf5ddbBen Komalo            canvas.drawBitmap(sStateForwarded,
457383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                    mCoordinates.stateX, mCoordinates.stateY, null);
458e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo        }
459e2f6f18396f27d2fee1149d6a3896721297bf7f3Jorge Lugo
4608b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        // Subject and snippet.
4618b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        sDefaultPaint.setTextSize(mCoordinates.subjectFontSize);
4628b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        canvas.save();
4638b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        canvas.translate(
4648b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                mCoordinates.subjectX,
4658b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                mCoordinates.subjectY);
4668b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        mSubjectLayout.draw(canvas);
4678b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        canvas.restore();
468937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
4698b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        // Draw the date
4708b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        sDatePaint.setTextSize(mCoordinates.dateFontSize);
4714beab88c92226628bf743070f88affb701968ce0Mindy Pereira        sDatePaint.setColor(mRead ? DATE_TEXT_COLOR_READ : DATE_TEXT_COLOR_UNREAD);
4728b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        int dateX = mCoordinates.dateXEnd
4738b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                - (int) sDatePaint.measureText(mFormattedDate, 0, mFormattedDate.length());
4748b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo
4758b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        canvas.drawText(mFormattedDate, 0, mFormattedDate.length(),
4768b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo                dateX, mCoordinates.dateY - mCoordinates.dateAscent, sDatePaint);
47706a2f4a3b1322fb6fecb908bd04e777199529ce7Marc Blank
4788b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        // Draw the favorite icon
4798b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        canvas.drawBitmap(mIsFavorite ? sFavoriteIconOn : sFavoriteIconOff,
480383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                mCoordinates.starX, mCoordinates.starY, null);
4818b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo
4828b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        // TODO: deal with the icon layouts better from the coordinate class so that this logic
4838b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        // doesn't have to exist.
4848b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo        // Draw the attachment and invite icons, if necessary.
485891da84a254d55c68df21998fbce9c1772eb6172Ben Komalo        int iconsLeft = dateX - sBadgeMargin;
486937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        if (mHasAttachment) {
4878b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo            iconsLeft = iconsLeft - sAttachmentIcon.getWidth();
488383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo            canvas.drawBitmap(sAttachmentIcon, iconsLeft, mCoordinates.paperclipY, null);
489937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        }
490937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        if (mHasInvite) {
4918b2109f047d8b50edbb677a822f6ee34df8d17b8Ben Komalo            iconsLeft -= sInviteIcon.getWidth();
492383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo            canvas.drawBitmap(sInviteIcon, iconsLeft, mCoordinates.paperclipY, null);
493937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank        }
494937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank
495432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    }
496432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
497432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    /**
498432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler     * Called by the adapter at bindView() time
4990a892ae04f0557367a751e1b130e260feccc3c52Makoto Onuki     *
500432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler     * @param adapter the adapter that creates this view
501e4202e782c91075c09cd5c348517a90620622a7fMindy Pereira     * @param layout If this is a three pane implementation, the
502e4202e782c91075c09cd5c348517a90620622a7fMindy Pereira     *            ThreePaneLayout. Otherwise, null.
503432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler     */
504e4202e782c91075c09cd5c348517a90620622a7fMindy Pereira    public void bindViewInit(MessagesAdapter adapter, ThreePaneLayout layout) {
505e4202e782c91075c09cd5c348517a90620622a7fMindy Pereira        mLayout = layout;
506432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        mAdapter = adapter;
5074beab88c92226628bf743070f88affb701968ce0Mindy Pereira        requestLayout();
508432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    }
509432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
5102076fa7a3b088ed220d9fa0607a457d2829afb8cBen Komalo    private static final int TOUCH_SLOP = 24;
51118811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo    private static int sScaledTouchSlop = -1;
51218811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo
51318811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo    private void initializeSlop(Context context) {
51418811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo        if (sScaledTouchSlop == -1) {
51518811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo            final Resources res = context.getResources();
51618811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo            final Configuration config = res.getConfiguration();
51718811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo            final float density = res.getDisplayMetrics().density;
51818811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo            final float sizeAndDensity;
51918811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo            if (config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_XLARGE)) {
52018811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo                sizeAndDensity = density * 1.5f;
52118811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo            } else {
52218811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo                sizeAndDensity = density;
52318811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo            }
52418811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo            sScaledTouchSlop = (int) (sizeAndDensity * TOUCH_SLOP + 0.5f);
52518811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo        }
52618811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo    }
52718811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo
528432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    /**
529432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler     * Overriding this method allows us to "catch" clicks in the checkbox or star
530432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler     * and process them accordingly.
531432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler     */
532432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    @Override
533432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    public boolean onTouchEvent(MotionEvent event) {
53418811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo        initializeSlop(getContext());
53518811dfb457eff8ba8b5436bd2641a4215480cdfBen Komalo
536432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        boolean handled = false;
537432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        int touchX = (int) event.getX();
538383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo        int checkRight = mCoordinates.checkmarkX
539383279a1c5e0cba982645a5ed06e06534d12159eBen Komalo                + mCoordinates.checkmarkWidthIncludingMargins + sScaledTouchSlop;
5402076fa7a3b088ed220d9fa0607a457d2829afb8cBen Komalo        int starLeft = mCoordinates.starX - sScaledTouchSlop;
541432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
542432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        switch (event.getAction()) {
543432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler            case MotionEvent.ACTION_DOWN:
544937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                if (touchX < checkRight || touchX > starLeft) {
545c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                    mDownEvent = true;
546937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                    if ((touchX < checkRight) || (touchX > starLeft)) {
547c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                        handled = true;
548c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                    }
549432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                }
550432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                break;
551432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
552432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler            case MotionEvent.ACTION_CANCEL:
553432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                mDownEvent = false;
554432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                break;
555432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
556432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler            case MotionEvent.ACTION_UP:
557432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                if (mDownEvent) {
558937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                    if (touchX < checkRight) {
559bdf84d57e623499d9c250fcc6fa1ba1f947cd2b5Makoto Onuki                        mAdapter.toggleSelected(this);
560bdf84d57e623499d9c250fcc6fa1ba1f947cd2b5Makoto Onuki                        handled = true;
561937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                    } else if (touchX > starLeft) {
562937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                        mIsFavorite = !mIsFavorite;
563937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank                        mAdapter.updateFavorite(this, mIsFavorite);
564432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                        handled = true;
565432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                    }
566432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                }
567432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler                break;
568432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        }
569432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
570432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        if (handled) {
571937ea4fc87eabd8fe785abeb2af1a38450e3fca9Marc Blank            invalidate();
572432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        } else {
573432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler            handled = super.onTouchEvent(event);
574432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        }
575432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler
576432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler        return handled;
577432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler    }
5788e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo
5798e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    @Override
5808e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
58170a3f210418d060c4e1616f2692c728564cae9e4Jorge Lugo        event.setClassName(getClass().getName());
58270a3f210418d060c4e1616f2692c728564cae9e4Jorge Lugo        event.setPackageName(getContext().getPackageName());
58370a3f210418d060c4e1616f2692c728564cae9e4Jorge Lugo        event.setEnabled(true);
58470a3f210418d060c4e1616f2692c728564cae9e4Jorge Lugo        event.setContentDescription(getContentDescription());
58570a3f210418d060c4e1616f2692c728564cae9e4Jorge Lugo        return true;
5868e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    }
5878e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo
5888e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    /**
58970a3f210418d060c4e1616f2692c728564cae9e4Jorge Lugo     * Sets the content description for this item, used for accessibility.
5908e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo     */
59170a3f210418d060c4e1616f2692c728564cae9e4Jorge Lugo    private void populateContentDescription() {
59256593af65ad25a69f81fb471d346c7519b58fbc4Jorge Lugo        if (!TextUtils.isEmpty(mSubject)) {
59356593af65ad25a69f81fb471d346c7519b58fbc4Jorge Lugo            setContentDescription(sSubjectDescription + mSubject);
59456593af65ad25a69f81fb471d346c7519b58fbc4Jorge Lugo        } else {
59556593af65ad25a69f81fb471d346c7519b58fbc4Jorge Lugo            setContentDescription(sSubjectEmptyDescription);
5968e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo        }
5978e779e627a2185320ab168fdce09477a73bf5f22Jorge Lugo    }
598432d1ec3ed54cd2f627fd79b5fe7b64341fa273aAndrew Stadler}
599