Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77860a4f authored by Philip Whitehouse's avatar Philip Whitehouse
Browse files

Tidy up MessageListFragment

parent e590efaf
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
package com.fsck.k9.fragment;


import java.util.Arrays;

import com.fsck.k9.provider.EmailProvider.MessageColumns;
import com.fsck.k9.provider.EmailProvider.SpecialColumns;
import com.fsck.k9.provider.EmailProvider.ThreadColumns;


public final class MLFProjectionInfo {

    static final String[] THREADED_PROJECTION = {
            MessageColumns.ID,
            MessageColumns.UID,
            MessageColumns.INTERNAL_DATE,
            MessageColumns.SUBJECT,
            MessageColumns.DATE,
            MessageColumns.SENDER_LIST,
            MessageColumns.TO_LIST,
            MessageColumns.CC_LIST,
            MessageColumns.READ,
            MessageColumns.FLAGGED,
            MessageColumns.ANSWERED,
            MessageColumns.FORWARDED,
            MessageColumns.ATTACHMENT_COUNT,
            MessageColumns.FOLDER_ID,
            MessageColumns.PREVIEW_TYPE,
            MessageColumns.PREVIEW,
            ThreadColumns.ROOT,
            SpecialColumns.ACCOUNT_UUID,
            SpecialColumns.FOLDER_NAME,

            SpecialColumns.THREAD_COUNT,
    };

    static final int ID_COLUMN = 0;
    static final int UID_COLUMN = 1;
    static final int INTERNAL_DATE_COLUMN = 2;
    static final int SUBJECT_COLUMN = 3;
    static final int DATE_COLUMN = 4;
    static final int SENDER_LIST_COLUMN = 5;
    static final int TO_LIST_COLUMN = 6;
    static final int CC_LIST_COLUMN = 7;
    static final int READ_COLUMN = 8;
    static final int FLAGGED_COLUMN = 9;
    static final int ANSWERED_COLUMN = 10;
    static final int FORWARDED_COLUMN = 11;
    static final int ATTACHMENT_COUNT_COLUMN = 12;
    static final int FOLDER_ID_COLUMN = 13;
    static final int PREVIEW_TYPE_COLUMN = 14;
    static final int PREVIEW_COLUMN = 15;
    static final int THREAD_ROOT_COLUMN = 16;
    static final int ACCOUNT_UUID_COLUMN = 17;
    static final int FOLDER_NAME_COLUMN = 18;
    static final int THREAD_COUNT_COLUMN = 19;

    static final String[] PROJECTION = Arrays.copyOf(THREADED_PROJECTION,
            THREAD_COUNT_COLUMN);
}
+370 −0
Original line number Diff line number Diff line
package com.fsck.k9.fragment;


import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.format.DateUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CursorAdapter;
import android.widget.TextView;

import com.fsck.k9.Account;
import com.fsck.k9.FontSizes;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mailstore.DatabasePreviewType;
import com.fsck.k9.ui.ContactBadge;

import static com.fsck.k9.fragment.MLFProjectionInfo.ANSWERED_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.ATTACHMENT_COUNT_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.CC_LIST_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.DATE_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.FLAGGED_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.FOLDER_NAME_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.FORWARDED_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.PREVIEW_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.PREVIEW_TYPE_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.READ_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.SENDER_LIST_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.SUBJECT_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.THREAD_COUNT_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.TO_LIST_COLUMN;
import static com.fsck.k9.fragment.MLFProjectionInfo.UID_COLUMN;


public class MessageListAdapter extends CursorAdapter {

    private final MessageListFragment fragment;
    private Drawable mAttachmentIcon;
    private Drawable mForwardedIcon;
    private Drawable mAnsweredIcon;
    private Drawable mForwardedAnsweredIcon;
    private FontSizes fontSizes = K9.getFontSizes();

    MessageListAdapter(MessageListFragment fragment) {
        super(fragment.getActivity(), null, 0);
        this.fragment = fragment;
        mAttachmentIcon = fragment.getResources().getDrawable(R.drawable.ic_email_attachment_small);
        mAnsweredIcon = fragment.getResources().getDrawable(R.drawable.ic_email_answered_small);
        mForwardedIcon = fragment.getResources().getDrawable(R.drawable.ic_email_forwarded_small);
        mForwardedAnsweredIcon = fragment.getResources().getDrawable(R.drawable.ic_email_forwarded_answered_small);
    }

    private String recipientSigil(boolean toMe, boolean ccMe) {
        if (toMe) {
            return fragment.getString(R.string.messagelist_sent_to_me_sigil);
        } else if (ccMe) {
            return fragment.getString(R.string.messagelist_sent_cc_me_sigil);
        } else {
            return "";
        }
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View view = fragment.getLayoutInflater().inflate(R.layout.message_list_item, parent, false);

        MessageViewHolder holder = new MessageViewHolder(fragment);
        holder.date = (TextView) view.findViewById(R.id.date);
        holder.chip = view.findViewById(R.id.chip);


        if (fragment.previewLines == 0 && fragment.contactsPictureLoader == null) {
            view.findViewById(R.id.preview).setVisibility(View.GONE);
            holder.preview = (TextView) view.findViewById(R.id.sender_compact);
            holder.flagged = (CheckBox) view.findViewById(R.id.flagged_center_right);
            view.findViewById(R.id.flagged_bottom_right).setVisibility(View.GONE);



        } else {
            view.findViewById(R.id.sender_compact).setVisibility(View.GONE);
            holder.preview = (TextView) view.findViewById(R.id.preview);
            holder.flagged = (CheckBox) view.findViewById(R.id.flagged_bottom_right);
            view.findViewById(R.id.flagged_center_right).setVisibility(View.GONE);

        }

        ContactBadge contactBadge =
                (ContactBadge) view.findViewById(R.id.contact_badge);
        if (fragment.contactsPictureLoader != null) {
            holder.contactBadge = contactBadge;
        } else {
            contactBadge.setVisibility(View.GONE);
        }

        if (fragment.senderAboveSubject) {
            holder.from = (TextView) view.findViewById(R.id.subject);
            fontSizes.setViewTextSize(holder.from, fontSizes.getMessageListSender());

        } else {
            holder.subject = (TextView) view.findViewById(R.id.subject);
            fontSizes.setViewTextSize(holder.subject, fontSizes.getMessageListSubject());

        }

        fontSizes.setViewTextSize(holder.date, fontSizes.getMessageListDate());


        // 1 preview line is needed even if it is set to 0, because subject is part of the same text view
        holder.preview.setLines(Math.max(fragment.previewLines,1));
        fontSizes.setViewTextSize(holder.preview, fontSizes.getMessageListPreview());
        holder.threadCount = (TextView) view.findViewById(R.id.thread_count);
        fontSizes.setViewTextSize(holder.threadCount, fontSizes.getMessageListSubject()); // thread count is next to subject
        view.findViewById(R.id.selected_checkbox_wrapper).setVisibility((fragment.checkboxes) ? View.VISIBLE : View.GONE);

        holder.flagged.setVisibility(fragment.stars ? View.VISIBLE : View.GONE);
        holder.flagged.setOnClickListener(holder);


        holder.selected = (CheckBox) view.findViewById(R.id.selected_checkbox);
        holder.selected.setOnClickListener(holder);


        view.setTag(holder);

        return view;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        Account account = fragment.getAccountFromCursor(cursor);

        String fromList = cursor.getString(SENDER_LIST_COLUMN);
        String toList = cursor.getString(TO_LIST_COLUMN);
        String ccList = cursor.getString(CC_LIST_COLUMN);
        Address[] fromAddrs = Address.unpack(fromList);
        Address[] toAddrs = Address.unpack(toList);
        Address[] ccAddrs = Address.unpack(ccList);

        boolean fromMe = fragment.messageHelper.toMe(account, fromAddrs);
        boolean toMe = fragment.messageHelper.toMe(account, toAddrs);
        boolean ccMe = fragment.messageHelper.toMe(account, ccAddrs);

        CharSequence displayName = fragment.messageHelper.getDisplayName(account, fromAddrs, toAddrs);
        CharSequence displayDate = DateUtils.getRelativeTimeSpanString(context, cursor.getLong(DATE_COLUMN));

        Address counterpartyAddress = fetchCounterPartyAddress(fromMe, toAddrs, ccAddrs, fromAddrs);

        int threadCount = (fragment.showingThreadedList) ? cursor.getInt(THREAD_COUNT_COLUMN) : 0;

        String subject = MlfUtils.buildSubject(cursor.getString(SUBJECT_COLUMN),
                fragment.getString(R.string.general_no_subject), threadCount);

        boolean read = (cursor.getInt(READ_COLUMN) == 1);
        boolean flagged = (cursor.getInt(FLAGGED_COLUMN) == 1);
        boolean answered = (cursor.getInt(ANSWERED_COLUMN) == 1);
        boolean forwarded = (cursor.getInt(FORWARDED_COLUMN) == 1);

        boolean hasAttachments = (cursor.getInt(ATTACHMENT_COUNT_COLUMN) > 0);

        MessageViewHolder holder = (MessageViewHolder) view.getTag();

        int maybeBoldTypeface = (read) ? Typeface.NORMAL : Typeface.BOLD;

        long uniqueId = cursor.getLong(fragment.uniqueIdColumn);
        boolean selected = fragment.selected.contains(uniqueId);

        holder.chip.setBackgroundColor(account.getChipColor());
        if (fragment.checkboxes) {
            holder.selected.setChecked(selected);
        }
        if (fragment.stars) {
            holder.flagged.setChecked(flagged);
        }
        holder.position = cursor.getPosition();
        if (holder.contactBadge != null) {
            updateContactBadge(holder, counterpartyAddress);
        }
        setBackgroundColor(view, selected, read);
        if (fragment.activeMessage != null) {
            changeBackgroundColorIfActiveMessage(cursor, account, view);
        }
        updateWithThreadCount(holder, threadCount);
        CharSequence beforePreviewText = (fragment.senderAboveSubject) ? subject : displayName;
        String sigil = recipientSigil(toMe, ccMe);
        SpannableStringBuilder messageStringBuilder = new SpannableStringBuilder(sigil)
                .append(beforePreviewText);
        if (fragment.previewLines > 0) {
            String preview = getPreview(cursor);
            messageStringBuilder.append(" ").append(preview);
        }
        holder.preview.setText(messageStringBuilder, TextView.BufferType.SPANNABLE);

        formatPreviewText(holder.preview, beforePreviewText, sigil);

        Drawable statusHolder = buildStatusHolder(forwarded, answered);

        if (holder.from != null ) {
            holder.from.setTypeface(Typeface.create(holder.from.getTypeface(), maybeBoldTypeface));
            if (fragment.senderAboveSubject) {
                holder.from.setCompoundDrawablesWithIntrinsicBounds(
                        statusHolder, // left
                        null, // top
                        hasAttachments ? mAttachmentIcon : null, // right
                        null); // bottom

                holder.from.setText(displayName);
            } else {
                holder.from.setText(new SpannableStringBuilder(sigil).append(displayName));
            }
        }
        if (holder.subject != null ) {
            if (!fragment.senderAboveSubject) {
                holder.subject.setCompoundDrawablesWithIntrinsicBounds(
                        statusHolder, // left
                        null, // top
                        hasAttachments ? mAttachmentIcon : null, // right
                        null); // bottom
            }

            holder.subject.setTypeface(Typeface.create(holder.subject.getTypeface(), maybeBoldTypeface));
            holder.subject.setText(subject);
        }
        holder.date.setText(displayDate);
    }

    private void formatPreviewText(TextView preview, CharSequence beforePreviewText, String sigil) {
        Spannable previewText = (Spannable)preview.getText();
        previewText.setSpan(buildSenderSpan(), 0, beforePreviewText.length() + sigil.length(),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        int previewSpanColor = buildPreviewSpanColor();

        // Set span (color) for preview message
        previewText.setSpan(new ForegroundColorSpan(previewSpanColor), beforePreviewText.length() + sigil.length(),
                previewText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    /**
     * Create a span section for the sender, and assign the correct font size and weight
     */
    private AbsoluteSizeSpan buildSenderSpan() {
        int fontSize = (fragment.senderAboveSubject) ?
                fontSizes.getMessageListSubject():
                fontSizes.getMessageListSender();
        return new AbsoluteSizeSpan(fontSize, true);
    }

    private Address fetchCounterPartyAddress(boolean fromMe, Address[] toAddrs, Address[] ccAddrs, Address[] fromAddrs) {
        if (fromMe) {
            if (toAddrs.length > 0) {
                return toAddrs[0];
            } else if (ccAddrs.length > 0) {
                return ccAddrs[0];
            }
        } else if (fromAddrs.length > 0) {
            return fromAddrs[0];
        }
        return null;
    }

    private void updateContactBadge(MessageViewHolder holder, Address counterpartyAddress) {
        if (counterpartyAddress != null) {
            Utility.setContactForBadge(holder.contactBadge, counterpartyAddress);
                    /*
                     * At least in Android 2.2 a different background + padding is used when no
                     * email address is available. ListView reuses the views but ContactBadge
                     * doesn't reset the padding, so we do it ourselves.
                     */
            holder.contactBadge.setPadding(0, 0, 0, 0);
            fragment.contactsPictureLoader.loadContactPicture(counterpartyAddress, holder.contactBadge);
        } else {
            holder.contactBadge.assignContactUri(null);
            holder.contactBadge.setImageResource(R.drawable.ic_contact_picture);
        }
    }

    private void changeBackgroundColorIfActiveMessage(Cursor cursor, Account account, View view) {
        String uid = cursor.getString(UID_COLUMN);
        String folderName = cursor.getString(FOLDER_NAME_COLUMN);

        if (account.getUuid().equals(fragment.activeMessage.getAccountUuid()) &&
                folderName.equals(fragment.activeMessage.getFolderName()) &&
                uid.equals(fragment.activeMessage.getUid())) {
            int res = R.attr.messageListActiveItemBackgroundColor;

            TypedValue outValue = new TypedValue();
            fragment.getActivity().getTheme().resolveAttribute(res, outValue, true);
            view.setBackgroundColor(outValue.data);
        }
    }

    private int buildPreviewSpanColor() {
        //TODO: make this part of the theme
        return (K9.getK9Theme() == K9.Theme.LIGHT) ?
                Color.rgb(105, 105, 105) :
                Color.rgb(160, 160, 160);
    }

    private Drawable buildStatusHolder(boolean forwarded, boolean answered) {
        if (forwarded && answered) {
            return mForwardedAnsweredIcon;
        } else if (answered) {
            return mAnsweredIcon;
        } else if (forwarded) {
            return mForwardedIcon;
        }
        return null;
    }

    private void setBackgroundColor(View view, boolean selected, boolean read) {
        if (selected || K9.useBackgroundAsUnreadIndicator()) {
            int res;
            if (selected) {
                res = R.attr.messageListSelectedBackgroundColor;
            } else if (read) {
                res = R.attr.messageListReadItemBackgroundColor;
            } else {
                res = R.attr.messageListUnreadItemBackgroundColor;
            }

            TypedValue outValue = new TypedValue();
            fragment.getActivity().getTheme().resolveAttribute(res, outValue, true);
            view.setBackgroundColor(outValue.data);
        } else {
            view.setBackgroundColor(Color.TRANSPARENT);
        }
    }

    private void updateWithThreadCount(MessageViewHolder holder, int threadCount) {
        if (threadCount > 1) {
            holder.threadCount.setText(String.format("%d", threadCount));
            holder.threadCount.setVisibility(View.VISIBLE);
        } else {
            holder.threadCount.setVisibility(View.GONE);
        }
    }

    private String getPreview(Cursor cursor) {
        String previewTypeString = cursor.getString(PREVIEW_TYPE_COLUMN);
        DatabasePreviewType previewType = DatabasePreviewType.fromDatabaseValue(previewTypeString);

        switch (previewType) {
            case NONE:
            case ERROR: {
                return "";
            }
            case ENCRYPTED: {
                return fragment.getString(R.string.preview_encrypted);
            }
            case TEXT: {
                return cursor.getString(PREVIEW_COLUMN);
            }
        }

        throw new AssertionError("Unknown preview type: " + previewType);
    }
}
Loading