SendersView.java revision 648df3f0b0ebcd3c4adf907d70ff0938e5dfc78f
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);
45a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira    public static 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) {
73648df3f0b0ebcd3c4adf907d70ff0938e5dfc78fMindy Pereira        if (TextUtils.isEmpty(header.conversation.senders)
74648df3f0b0ebcd3c4adf907d70ff0938e5dfc78fMindy Pereira                && header.conversation.conversationInfo == null) {
758eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira            return;
768eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        }
77b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        Conversation conversation = header.conversation;
78648df3f0b0ebcd3c4adf907d70ff0938e5dfc78fMindy Pereira        String sendersInfo = conversation.conversationInfo != null ?
79648df3f0b0ebcd3c4adf907d70ff0938e5dfc78fMindy Pereira                conversation.conversationInfo.sendersInfo : header.conversation.senders;
80648df3f0b0ebcd3c4adf907d70ff0938e5dfc78fMindy Pereira        SendersInfo info = new SendersInfo(sendersInfo);
81a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira        mFormatVersion = info.version;
82b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        switch (mFormatVersion) {
83b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            case MERGED_FORMATTING:
84a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                formatMerged(header, info.text, isUnread, mode);
85b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                break;
86b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            case DEFAULT_FORMATTING:
87b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            default:
88a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                formatDefault(header, info.text);
89b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                break;
90b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
918eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    }
928eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira
93b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private void formatDefault(ConversationItemViewModel header, String sendersString) {
948eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        String[] senders = TextUtils.split(sendersString, Address.ADDRESS_DELIMETER);
958eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        String[] namesOnly = new String[senders.length];
968eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        Rfc822Token[] senderTokens;
978eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        String display;
988eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        for (int i = 0; i < senders.length; i++) {
998eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira            senderTokens = Rfc822Tokenizer.tokenize(senders[i]);
1008eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira            if (senderTokens != null && senderTokens.length > 0) {
1018eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                display = senderTokens[0].getName();
1028eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                if (TextUtils.isEmpty(display)) {
1038eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                    display = senderTokens[0].getAddress();
1048eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                }
1058eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira                namesOnly[i] = display;
1068eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira            }
1078eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira        }
108b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        header.sendersText = TextUtils.join(Address.ADDRESS_DELIMETER + " ", namesOnly);
109b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        header.addSenderFragment(0, header.sendersText.length(), sNormalTextStyle, true);
110b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    }
111b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
112b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira    private void formatMerged(ConversationItemViewModel header, String sendersString,
113b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            boolean isUnread, int mode) {
114b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        SpannableStringBuilder sendersBuilder = new SpannableStringBuilder();
115b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        SpannableStringBuilder statusBuilder = new SpannableStringBuilder();
116b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        Utils.getStyledSenderSnippet(getContext(), sendersString, sendersBuilder,
117b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                statusBuilder, ConversationItemViewCoordinates.getSubjectLength(getContext(), mode,
118b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                        header.folderDisplayer.hasVisibleFolders(),
119b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                        header.conversation.hasAttachments), false, false, header.hasDraftMessage);
120b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        header.sendersText = sendersBuilder.toString();
121b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
122b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        CharacterStyle[] spans = sendersBuilder.getSpans(0, sendersBuilder.length(),
123b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                CharacterStyle.class);
124b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        header.clearSenderFragments();
125b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        int lastPosition = 0;
126b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        CharacterStyle style = sNormalTextStyle;
127b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        if (spans != null) {
128b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            for (CharacterStyle span : spans) {
129b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                style = span;
130b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int start = sendersBuilder.getSpanStart(style);
131b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int end = sendersBuilder.getSpanEnd(style);
132b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                if (start > lastPosition) {
133b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                    header.addSenderFragment(lastPosition, start, sNormalTextStyle, false);
134b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                }
135b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // From instructions won't be updated until the next sync. So we
136b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // have to override the text style here to be consistent with
137b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // the background color.
138b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                if (isUnread) {
139b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                    header.addSenderFragment(start, end, style, false);
140b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                } else {
141b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                    header.addSenderFragment(start, end, sNormalTextStyle, false);
142b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                }
143b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                lastPosition = end;
144b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            }
145b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
146b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        if (lastPosition < sendersBuilder.length()) {
147b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            style = sLightTextStyle;
148b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            header.addSenderFragment(lastPosition, sendersBuilder.length(), style, true);
149b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
150b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        if (statusBuilder.length() > 0) {
151b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            if (header.sendersText.length() > 0) {
152b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                header.sendersText = header.sendersText.concat(", ");
153b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
154b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // Extend the last fragment to include the comma.
155b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int lastIndex = header.senderFragments.size() - 1;
156b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int start = header.senderFragments.get(lastIndex).start;
157b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                int end = header.senderFragments.get(lastIndex).end + 2;
158b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                style = header.senderFragments.get(lastIndex).style;
159b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
160b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // The new fragment is only fixed if the previous fragment
161b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // is fixed.
162b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                boolean isFixed = header.senderFragments.get(lastIndex).isFixed;
163b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
164b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // Remove the old fragment.
165b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                header.senderFragments.remove(lastIndex);
166b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira
167b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                // Add new fragment.
168b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                header.addSenderFragment(start, end, style, isFixed);
169b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            }
170b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            int pos = header.sendersText.length();
171b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            header.sendersText = header.sendersText.concat(statusBuilder.toString());
172b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira            header.addSenderFragment(pos, header.sendersText.length(), new ForegroundColorSpan(
173b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira                    DRAFT_TEXT_COLOR), true);
174b9c036b36542924894f141e3ecd81381a12e94a9Mindy Pereira        }
1758eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira    }
176a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira
177a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira    public static class SendersInfo {
178a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira        public int version;
179a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira        public String text;
180a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira
181a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira        public SendersInfo(String toParse) {
182a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira            if (TextUtils.isEmpty(toParse)) {
183a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                version = 0;
184a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                text = "";
185a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira            } else {
186a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                String[] splits = TextUtils.split(toParse, SENDERS_VERSION_SEPARATOR_PATTERN);
187a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                if (splits == null || splits.length < 2) {
188a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                    version = SendersView.DEFAULT_FORMATTING;
189a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                    text = toParse;
190a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                } else {
191a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                    version = Integer.parseInt(splits[0]);
192a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                    text = splits[1];
193a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira                }
194a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira            }
195a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira        }
196a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira    }
1978eb43be8074acb4e09da1385225fbb41c97b1bd5Mindy Pereira}
198