MessageCursor.java revision c42ad5ea3a49e6045a79d1fde3d858033176e67b
17bdc3750454efe59617b7df945eadd7e59bee954Andy Huang/*
27bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * Copyright (C) 2012 Google Inc.
37bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * Licensed to The Android Open Source Project.
47bdc3750454efe59617b7df945eadd7e59bee954Andy Huang *
57bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * Licensed under the Apache License, Version 2.0 (the "License");
67bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * you may not use this file except in compliance with the License.
77bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * You may obtain a copy of the License at
87bdc3750454efe59617b7df945eadd7e59bee954Andy Huang *
97bdc3750454efe59617b7df945eadd7e59bee954Andy Huang *      http://www.apache.org/licenses/LICENSE-2.0
107bdc3750454efe59617b7df945eadd7e59bee954Andy Huang *
117bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * Unless required by applicable law or agreed to in writing, software
127bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * distributed under the License is distributed on an "AS IS" BASIS,
137bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
147bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * See the License for the specific language governing permissions and
157bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * limitations under the License.
167bdc3750454efe59617b7df945eadd7e59bee954Andy Huang */
177bdc3750454efe59617b7df945eadd7e59bee954Andy Huang
187bdc3750454efe59617b7df945eadd7e59bee954Andy Huangpackage com.android.mail.browse;
197bdc3750454efe59617b7df945eadd7e59bee954Andy Huang
207bdc3750454efe59617b7df945eadd7e59bee954Andy Huangimport android.database.Cursor;
21fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huangimport android.net.Uri;
2247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huangimport android.os.Bundle;
23839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport android.os.Parcelable;
247bdc3750454efe59617b7df945eadd7e59bee954Andy Huang
25c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrookimport com.android.mail.content.CursorCreator;
26c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrookimport com.android.mail.content.ObjectCursor;
27bbe74aee04f669990e888095b0d6858dc1e17ce1Mark Weiimport com.android.mail.providers.Account;
286766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huangimport com.android.mail.providers.Attachment;
29839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport com.android.mail.providers.Conversation;
307bdc3750454efe59617b7df945eadd7e59bee954Andy Huangimport com.android.mail.providers.Message;
3147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huangimport com.android.mail.providers.UIProvider.CursorExtraKeys;
3247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huangimport com.android.mail.providers.UIProvider.CursorStatus;
33839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport com.android.mail.ui.ConversationUpdater;
34bbe74aee04f669990e888095b0d6858dc1e17ce1Mark Wei
35cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huangimport com.google.common.base.Objects;
36fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huangimport com.google.common.collect.Lists;
377bdc3750454efe59617b7df945eadd7e59bee954Andy Huang
38fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huangimport java.util.List;
397bdc3750454efe59617b7df945eadd7e59bee954Andy Huang
407bdc3750454efe59617b7df945eadd7e59bee954Andy Huang/**
417bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * MessageCursor contains the messages within a conversation; the public methods within should
427bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * only be called by the UI thread, as cursor position isn't guaranteed to be maintained
437bdc3750454efe59617b7df945eadd7e59bee954Andy Huang */
44c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrookpublic class MessageCursor extends ObjectCursor<MessageCursor.ConversationMessage> {
4502133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang    /**
4602133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang     * The current controller that this cursor can use to reference the owning {@link Conversation},
4702133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang     * and a current {@link ConversationUpdater}. Since this cursor will survive a rotation, but
4802133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang     * the controller does not, whatever the new controller is MUST update this reference before
4902133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang     * using this cursor.
5002133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang     */
5102133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang    private ConversationController mController;
527bdc3750454efe59617b7df945eadd7e59bee954Andy Huang
5347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang    private Integer mStatus;
5447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang
55cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang    public interface ConversationController {
5602133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        Conversation getConversation();
57cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        ConversationUpdater getListController();
58cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        MessageCursor getMessageCursor();
59bbe74aee04f669990e888095b0d6858dc1e17ce1Mark Wei        Account getAccount();
60cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang    }
61cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang
62839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    /**
63839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     * A message created as part of a conversation view. Sometimes, like during star/unstar, it's
64cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang     * handy to have the owning {@link Conversation} for context.
65cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang     *
66cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang     * <p>This class must remain separate from the {@link MessageCursor} from whence it came,
67cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang     * because cursors can be closed by their Loaders at any time. The
68cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang     * {@link ConversationController} intermediate is used to obtain the currently opened cursor.
69839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     *
70839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     * <p>(N.B. This is a {@link Parcelable}, so try not to add non-transient fields here.
71839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     * Parcelable state belongs either in {@link Message} or {@link MessageViewState}. The
72839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     * assumption is that this class never needs the state of its extra context saved.)
73839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     */
74cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang    public static final class ConversationMessage extends Message {
75839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
7602133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        private transient ConversationController mController;
77839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
78c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook        private ConversationMessage(Cursor cursor) {
79839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            super(cursor);
8002133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        }
8102133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang
8202133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        public void setController(ConversationController controller) {
83cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            mController = controller;
84cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        }
85cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang
8602133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        public Conversation getConversation() {
8702133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang            return mController.getConversation();
8802133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        }
8902133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang
90cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        /**
91cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang         * Returns a hash code based on this message's identity, contents and current state.
92cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang         * This is a separate method from hashCode() to allow for an instance of this class to be
93cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang         * a functional key in a hash-based data structure.
94cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang         *
95cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang         */
96cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        public int getStateHashCode() {
976766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang            return Objects.hashCode(uri, read, starred, getAttachmentsStateHashCode());
986766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang        }
996766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang
1006766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang        private int getAttachmentsStateHashCode() {
1016766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang            int hash = 0;
1026766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang            for (Attachment a : getAttachments()) {
103fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huang                final Uri uri = a.getIdentifierUri();
104fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huang                hash += (uri != null ? uri.hashCode() : 0);
1056766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang            }
1066766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang            return hash;
107839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
108839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
109839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        public boolean isConversationStarred() {
110cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            final MessageCursor c = mController.getMessageCursor();
111cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            return c != null && c.isConversationStarred();
112839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
113839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
114839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        public void star(boolean newStarred) {
115cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            final ConversationUpdater listController = mController.getListController();
116cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            if (listController != null) {
117cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang                listController.starMessage(this, newStarred);
118cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            }
119839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
120839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
121c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook        /**
122c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook         * Public object that knows how to construct Messages given Cursors.
123c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook         */
124c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook        public static final CursorCreator<ConversationMessage> FACTORY =
125c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook                new CursorCreator<ConversationMessage>() {
126c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook            @Override
127c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook            public ConversationMessage createFromCursor(Cursor c) {
128c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook                return new ConversationMessage(c);
129c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook            }
130c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook
131c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook            @Override
132c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook            public String toString() {
133c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook                return "ConversationMessage CursorCreator";
134c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook            }
135c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook        };
136c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook
137839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    }
138839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
13902133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang    public MessageCursor(Cursor inner) {
140c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook        super(inner, ConversationMessage.FACTORY);
14102133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang    }
14202133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang
14302133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang    public void setController(ConversationController controller) {
144cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        mController = controller;
1457bdc3750454efe59617b7df945eadd7e59bee954Andy Huang    }
1467bdc3750454efe59617b7df945eadd7e59bee954Andy Huang
147839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    public ConversationMessage getMessage() {
148c42ad5ea3a49e6045a79d1fde3d858033176e67bPaul Westbrook        final ConversationMessage m = getModel();
14902133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        // ALWAYS set up each ConversationMessage with the latest controller.
15002133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        // Rotation invalidates everything except this Cursor, its Loader and the cached Messages,
15102133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        // so if we want to continue using them after rotate, we have to ensure their controller
15202133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        // references always point to the current controller.
15302133aa80d4ff68739a8b8c6f4cc00150a3cfc80Andy Huang        m.setController(mController);
1547bdc3750454efe59617b7df945eadd7e59bee954Andy Huang        return m;
1557bdc3750454efe59617b7df945eadd7e59bee954Andy Huang    }
156cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank
157cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank    // Is the conversation starred?
158cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank    public boolean isConversationStarred() {
159cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank        int pos = -1;
160cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank        while (moveToPosition(++pos)) {
161423bea25992492efea7d414819729f9eae7ce72eAndy Huang            if (getMessage().starred) {
162cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank                return true;
163cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank            }
164cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank        }
165cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank        return false;
166cf164d64bcb1da92b427bda99b97f7ec310ef704Marc Blank    }
167bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook
168423bea25992492efea7d414819729f9eae7ce72eAndy Huang
169423bea25992492efea7d414819729f9eae7ce72eAndy Huang    public boolean isConversationRead() {
170423bea25992492efea7d414819729f9eae7ce72eAndy Huang        int pos = -1;
171423bea25992492efea7d414819729f9eae7ce72eAndy Huang        while (moveToPosition(++pos)) {
172423bea25992492efea7d414819729f9eae7ce72eAndy Huang            if (!getMessage().read) {
173423bea25992492efea7d414819729f9eae7ce72eAndy Huang                return false;
174423bea25992492efea7d414819729f9eae7ce72eAndy Huang            }
175423bea25992492efea7d414819729f9eae7ce72eAndy Huang        }
176423bea25992492efea7d414819729f9eae7ce72eAndy Huang        return true;
177423bea25992492efea7d414819729f9eae7ce72eAndy Huang    }
178cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang    public void markMessagesRead() {
179cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        int pos = -1;
180cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        while (moveToPosition(++pos)) {
181cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            getMessage().read = true;
182cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        }
183cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang    }
184cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang
1856766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang    public int getStateHashCode() {
18606c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang        return getStateHashCode(0);
18706c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang    }
18806c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang
18906c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang    /**
19006c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     * Calculate a hash code that compactly summarizes the state of the messages in this cursor,
19106c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     * with respect to the way the messages are displayed in conversation view. This is not a
19206c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     * general-purpose hash code. When the state hash codes of a new cursor differs from the
19306c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     * existing cursor's hash code, the conversation view will re-render from scratch.
19406c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     *
19506c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     * @param exceptLast optional number of messages to exclude iterating through at the end of the
19606c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     * cursor. pass zero to iterate through all messages (or use {@link #getStateHashCode()}).
19706c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     * @return state hash code of the selected messages in this cursor
19806c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang     */
19906c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang    public int getStateHashCode(int exceptLast) {
200cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        int hashCode = 17;
201cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        int pos = -1;
20206c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang        final int stopAt = getCount() - exceptLast;
20306c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang        while (moveToPosition(++pos) && pos < stopAt) {
204cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            hashCode = 31 * hashCode + getMessage().getStateHashCode();
205cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        }
206cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang        return hashCode;
207cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang    }
208cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang
20947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang    public int getStatus() {
21047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        if (mStatus != null) {
21147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang            return mStatus;
21247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        }
21347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang
21447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        mStatus = CursorStatus.LOADED;
21547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        final Bundle extras = getExtras();
21647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        if (extras != null && extras.containsKey(CursorExtraKeys.EXTRA_STATUS)) {
21747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang            mStatus = extras.getInt(CursorExtraKeys.EXTRA_STATUS);
21847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        }
21947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        return mStatus;
22047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang    }
22147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang
222838c8c9b385d5907d3dc3e52aaeec87bdde2ae22Vikram Aggarwal    /**
223838c8c9b385d5907d3dc3e52aaeec87bdde2ae22Vikram Aggarwal     * Returns true if the cursor is fully loaded. Returns false if the cursor is expected to get
224838c8c9b385d5907d3dc3e52aaeec87bdde2ae22Vikram Aggarwal     * new messages.
225838c8c9b385d5907d3dc3e52aaeec87bdde2ae22Vikram Aggarwal     * @return
226838c8c9b385d5907d3dc3e52aaeec87bdde2ae22Vikram Aggarwal     */
22747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang    public boolean isLoaded() {
228838c8c9b385d5907d3dc3e52aaeec87bdde2ae22Vikram Aggarwal        return !CursorStatus.isWaitingForResults(getStatus());
22947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang    }
23047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang
23147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang    public String getDebugDump() {
23247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        StringBuilder sb = new StringBuilder();
2339e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang        sb.append(String.format("conv='%s' status=%d messages:\n",
2349e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang                mController.getConversation(), getStatus()));
23547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        int pos = -1;
23647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        while (moveToPosition(++pos)) {
2376766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang            final ConversationMessage m = getMessage();
238fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huang            final List<Uri> attUris = Lists.newArrayList();
239fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huang            for (Attachment a : m.getAttachments()) {
240fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huang                attUris.add(a.uri);
241fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huang            }
24247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang            sb.append(String.format(
2436766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang                    "[Message #%d hash=%s uri=%s id=%s serverId=%s from='%s' draftType=%d" +
244fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huang                    " isSending=%s read=%s starred=%s attUris=%s]\n",
2458960f0af431bc164003e09b3c8981aab808d9ec1Scott Kennedy                    pos, m.getStateHashCode(), m.uri, m.id, m.serverId, m.getFrom(), m.draftType,
246fc0eab16cbb294dd388011dfb2b6b3a846c4731fAndy Huang                    m.isSending, m.read, m.starred, attUris));
24747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        }
24847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang        return sb.toString();
24947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang    }
25047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang
2517bdc3750454efe59617b7df945eadd7e59bee954Andy Huang}