SendersView.java revision b9c036b36542924894f141e3ecd81381a12e94a9
18eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira/*
28eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * Copyright (C) 2012 Google Inc.
38eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * Licensed to The Android Open Source Project.
48eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira *
58eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
68eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * you may not use this file except in compliance with the License.
78eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * You may obtain a copy of the License at
88eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira *
98eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
108eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira *
118eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * Unless required by applicable law or agreed to in writing, software
128eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
138eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * See the License for the specific language governing permissions and
158eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira * limitations under the License.
168eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira */
178eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
188eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereirapackage com.android.mail.browse;
198eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
208eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.content.Context;
21b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereiraimport android.content.res.Resources;
228eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.graphics.Typeface;
23b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereiraimport android.text.SpannableStringBuilder;
248eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.text.TextUtils;
258eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.text.style.CharacterStyle;
26b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereiraimport android.text.style.ForegroundColorSpan;
278eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.text.style.StyleSpan;
288eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.text.util.Rfc822Token;
298eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.text.util.Rfc822Tokenizer;
308eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.util.AttributeSet;
318eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport android.widget.TextView;
328eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
33b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereiraimport com.android.mail.R;
348eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport com.android.mail.providers.Address;
358eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereiraimport com.android.mail.providers.Conversation;
36b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereiraimport com.android.mail.utils.Utils;
37b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
38b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereiraimport java.util.regex.Pattern;
398eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
408eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereirapublic class SendersView extends TextView {
41b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    public static final int DEFAULT_FORMATTING = 0;
42b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    public static final int MERGED_FORMATTING = 1;
43b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    public static String SENDERS_VERSION_SEPARATOR = "^**^";
448eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    CharacterStyle sNormalTextStyle = new StyleSpan(Typeface.NORMAL);
45b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private Pattern SENDERS_VERSION_SEPARATOR_PATTERN = Pattern.compile("\\^\\*\\*\\^");
46b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private int mFormatVersion = -1;
47b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private ForegroundColorSpan sLightTextStyle;
48b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private int DRAFT_TEXT_COLOR;
49b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private int LIGHT_TEXT_COLOR;
508eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
518eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    public SendersView(Context context) {
528eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        this(context, null);
538eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    }
548eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
558eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    public SendersView(Context context, AttributeSet attrs) {
568eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        this(context, attrs, -1);
578eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    }
588eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
598eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    public SendersView(Context context, AttributeSet attrs, int defStyle) {
608eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        super(context, attrs, defStyle);
61b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        Resources res = context.getResources();
62b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        LIGHT_TEXT_COLOR = res.getColor(R.color.light_text_color);
63b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        DRAFT_TEXT_COLOR = res.getColor(R.color.drafts);
64b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        sLightTextStyle = new ForegroundColorSpan(LIGHT_TEXT_COLOR);
658eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    }
668eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
678eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    public Typeface getTypeface(boolean isUnread) {
68b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        return mFormatVersion == DEFAULT_FORMATTING ? isUnread ? Typeface.DEFAULT_BOLD
69b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                : Typeface.DEFAULT : Typeface.DEFAULT;
708eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    }
718eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
72b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    public void formatSenders(ConversationItemViewModel header, boolean isUnread, int mode) {
738eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        if (TextUtils.isEmpty(header.conversation.senders)) {
748eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira            return;
758eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        }
76b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        Conversation conversation = header.conversation;
77b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        String sendersString = "";
78b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        String[] splits = TextUtils.split(conversation.senders, SENDERS_VERSION_SEPARATOR_PATTERN);
79b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        if (splits == null || splits.length < 2) {
80b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            mFormatVersion = DEFAULT_FORMATTING;
81b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            sendersString = header.conversation.senders;
82b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        } else {
83b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            mFormatVersion = Integer.parseInt(splits[0]);
84b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            // Format the rest of the senders string once the format version is
85b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            // stripped.
86b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            sendersString = splits[1];
87b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
88b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        switch (mFormatVersion) {
89b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            case MERGED_FORMATTING:
90b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                formatMerged(header, sendersString, isUnread, mode);
91b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                break;
92b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            case DEFAULT_FORMATTING:
93b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            default:
94b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                formatDefault(header, sendersString);
95b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                break;
96b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
978eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    }
988eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
99b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private void formatDefault(ConversationItemViewModel header, String sendersString) {
1008eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        String[] senders = TextUtils.split(sendersString, Address.ADDRESS_DELIMETER);
1018eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        String[] namesOnly = new String[senders.length];
1028eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        Rfc822Token[] senderTokens;
1038eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        String display;
1048eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        for (int i = 0; i < senders.length; i++) {
1058eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira            senderTokens = Rfc822Tokenizer.tokenize(senders[i]);
1068eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira            if (senderTokens != null && senderTokens.length > 0) {
1078eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                display = senderTokens[0].getName();
1088eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                if (TextUtils.isEmpty(display)) {
1098eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                    display = senderTokens[0].getAddress();
1108eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                }
1118eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                namesOnly[i] = display;
1128eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira            }
1138eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        }
114b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        header.sendersText = TextUtils.join(Address.ADDRESS_DELIMETER + " ", namesOnly);
115b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        header.addSenderFragment(0, header.sendersText.length(), sNormalTextStyle, true);
116b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    }
117b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
118b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private void formatMerged(ConversationItemViewModel header, String sendersString,
119b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            boolean isUnread, int mode) {
120b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        SpannableStringBuilder sendersBuilder = new SpannableStringBuilder();
121b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        SpannableStringBuilder statusBuilder = new SpannableStringBuilder();
122b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        Utils.getStyledSenderSnippet(getContext(), sendersString, sendersBuilder,
123b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                statusBuilder, ConversationItemViewCoordinates.getSubjectLength(getContext(), mode,
124b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                        header.folderDisplayer.hasVisibleFolders(),
125b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                        header.conversation.hasAttachments), false, false, header.hasDraftMessage);
126b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        header.sendersText = sendersBuilder.toString();
127b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
128b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        CharacterStyle[] spans = sendersBuilder.getSpans(0, sendersBuilder.length(),
129b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                CharacterStyle.class);
130b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        header.clearSenderFragments();
131b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        int lastPosition = 0;
132b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        CharacterStyle style = sNormalTextStyle;
133b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        if (spans != null) {
134b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            for (CharacterStyle span : spans) {
135b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                style = span;
136b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int start = sendersBuilder.getSpanStart(style);
137b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int end = sendersBuilder.getSpanEnd(style);
138b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                if (start > lastPosition) {
139b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                    header.addSenderFragment(lastPosition, start, sNormalTextStyle, false);
140b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                }
141b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // From instructions won't be updated until the next sync. So we
142b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // have to override the text style here to be consistent with
143b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // the background color.
144b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                if (isUnread) {
145b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                    header.addSenderFragment(start, end, style, false);
146b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                } else {
147b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                    header.addSenderFragment(start, end, sNormalTextStyle, false);
148b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                }
149b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                lastPosition = end;
150b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            }
151b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
152b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        if (lastPosition < sendersBuilder.length()) {
153b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            style = sLightTextStyle;
154b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            header.addSenderFragment(lastPosition, sendersBuilder.length(), style, true);
155b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
156b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        if (statusBuilder.length() > 0) {
157b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            if (header.sendersText.length() > 0) {
158b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                header.sendersText = header.sendersText.concat(", ");
159b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
160b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // Extend the last fragment to include the comma.
161b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int lastIndex = header.senderFragments.size() - 1;
162b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int start = header.senderFragments.get(lastIndex).start;
163b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int end = header.senderFragments.get(lastIndex).end + 2;
164b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                style = header.senderFragments.get(lastIndex).style;
165b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
166b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // The new fragment is only fixed if the previous fragment
167b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // is fixed.
168b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                boolean isFixed = header.senderFragments.get(lastIndex).isFixed;
169b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
170b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // Remove the old fragment.
171b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                header.senderFragments.remove(lastIndex);
172b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
173b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // Add new fragment.
174b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                header.addSenderFragment(start, end, style, isFixed);
175b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            }
176b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            int pos = header.sendersText.length();
177b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            header.sendersText = header.sendersText.concat(statusBuilder.toString());
178b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            header.addSenderFragment(pos, header.sendersText.length(), new ForegroundColorSpan(
179b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                    DRAFT_TEXT_COLOR), true);
180b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
1818eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    }
1828eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira}
183