| /* |
| * Copyright (C) 2012 Google Inc. |
| * Licensed to The Android Open Source Project. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.mail.browse; |
| |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| |
| import com.android.mail.content.ObjectCursor; |
| import com.android.mail.providers.Account; |
| import com.android.mail.providers.Attachment; |
| import com.android.mail.providers.Conversation; |
| import com.android.mail.providers.UIProvider.CursorExtraKeys; |
| import com.android.mail.providers.UIProvider.CursorStatus; |
| import com.android.mail.ui.ConversationUpdater; |
| |
| import com.google.common.collect.Lists; |
| |
| import java.util.List; |
| |
| /** |
| * MessageCursor contains the messages within a conversation; the public methods within should |
| * only be called by the UI thread, as cursor position isn't guaranteed to be maintained |
| */ |
| public class MessageCursor extends ObjectCursor<ConversationMessage> { |
| /** |
| * The current controller that this cursor can use to reference the owning {@link Conversation}, |
| * and a current {@link ConversationUpdater}. Since this cursor will survive a rotation, but |
| * the controller does not, whatever the new controller is MUST update this reference before |
| * using this cursor. |
| */ |
| private ConversationController mController; |
| |
| private Integer mStatus; |
| |
| public interface ConversationController { |
| Conversation getConversation(); |
| ConversationUpdater getListController(); |
| MessageCursor getMessageCursor(); |
| Account getAccount(); |
| } |
| |
| public MessageCursor(Cursor inner) { |
| super(inner, ConversationMessage.FACTORY); |
| } |
| |
| public void setController(ConversationController controller) { |
| mController = controller; |
| } |
| |
| public ConversationMessage getMessage() { |
| final ConversationMessage m = getModel(); |
| // ALWAYS set up each ConversationMessage with the latest controller. |
| // Rotation invalidates everything except this Cursor, its Loader and the cached Messages, |
| // so if we want to continue using them after rotate, we have to ensure their controller |
| // references always point to the current controller. |
| m.setController(mController); |
| return m; |
| } |
| |
| public Conversation getConversation() { |
| return mController != null ? mController.getConversation() : null; |
| } |
| |
| // Is the conversation starred? |
| public boolean isConversationStarred() { |
| int pos = -1; |
| while (moveToPosition(++pos)) { |
| if (getMessage().starred) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| public boolean isConversationRead() { |
| int pos = -1; |
| while (moveToPosition(++pos)) { |
| if (!getMessage().read) { |
| return false; |
| } |
| } |
| return true; |
| } |
| public void markMessagesRead() { |
| int pos = -1; |
| while (moveToPosition(++pos)) { |
| getMessage().read = true; |
| } |
| } |
| |
| public int getStateHashCode() { |
| return getStateHashCode(0); |
| } |
| |
| /** |
| * Calculate a hash code that compactly summarizes the state of the messages in this cursor, |
| * with respect to the way the messages are displayed in conversation view. This is not a |
| * general-purpose hash code. When the state hash codes of a new cursor differs from the |
| * existing cursor's hash code, the conversation view will re-render from scratch. |
| * |
| * @param exceptLast optional number of messages to exclude iterating through at the end of the |
| * cursor. pass zero to iterate through all messages (or use {@link #getStateHashCode()}). |
| * @return state hash code of the selected messages in this cursor |
| */ |
| public int getStateHashCode(int exceptLast) { |
| int hashCode = 17; |
| int pos = -1; |
| final int stopAt = getCount() - exceptLast; |
| while (moveToPosition(++pos) && pos < stopAt) { |
| hashCode = 31 * hashCode + getMessage().getStateHashCode(); |
| } |
| return hashCode; |
| } |
| |
| public int getStatus() { |
| if (mStatus != null) { |
| return mStatus; |
| } |
| |
| mStatus = CursorStatus.LOADED; |
| final Bundle extras = getExtras(); |
| if (extras != null && extras.containsKey(CursorExtraKeys.EXTRA_STATUS)) { |
| mStatus = extras.getInt(CursorExtraKeys.EXTRA_STATUS); |
| } |
| return mStatus; |
| } |
| |
| /** |
| * Returns true if the cursor is fully loaded. Returns false if the cursor is expected to get |
| * new messages. |
| * @return |
| */ |
| public boolean isLoaded() { |
| return !CursorStatus.isWaitingForResults(getStatus()); |
| } |
| |
| public String getDebugDump() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(String.format("conv='%s' status=%d messages:\n", |
| mController.getConversation(), getStatus())); |
| int pos = -1; |
| while (moveToPosition(++pos)) { |
| final ConversationMessage m = getMessage(); |
| final List<Uri> attUris = Lists.newArrayList(); |
| for (Attachment a : m.getAttachments()) { |
| attUris.add(a.uri); |
| } |
| sb.append(String.format( |
| "[Message #%d hash=%s uri=%s id=%s serverId=%s from='%s' draftType=%d" + |
| " isSending=%s read=%s starred=%s attUris=%s]\n", |
| pos, m.getStateHashCode(), m.uri, m.id, m.serverId, m.getFrom(), m.draftType, |
| m.isSending, m.read, m.starred, attUris)); |
| } |
| return sb.toString(); |
| } |
| |
| } |