1 /* 2 * Copyright (C) 2012 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mail.ui; 19 20 import android.net.Uri; 21 import android.os.Bundle; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 25 import com.android.mail.providers.Conversation; 26 import com.android.mail.providers.Message; 27 import com.google.common.collect.Maps; 28 import com.google.common.collect.Sets; 29 30 import java.util.Map; 31 import java.util.Set; 32 33 /** 34 * A small class to keep state for conversation view when restoring. 35 * 36 */ 37 public class ConversationViewState implements Parcelable { 38 39 // N.B. don't serialize entire Messages because they contain body HTML/text 40 41 private final Map<Uri, MessageViewState> mMessageViewStates = Maps.newHashMap(); 42 43 private byte[] mConversationInfo; 44 45 public static final class ExpansionState { 46 public static int NONE = 0; 47 public static int EXPANDED = 1; 48 public static int COLLAPSED = 2; 49 public static int SUPER_COLLAPSED = 3; 50 51 private ExpansionState() {} 52 53 public static boolean isExpanded(int state) { 54 return state == EXPANDED; 55 } 56 public static boolean isSuperCollapsed(int state) { 57 return state == SUPER_COLLAPSED; 58 } 59 60 /** 61 * Returns true if the {@link ExpansionState} is 62 * {@link #COLLAPSED} or {@link #SUPER_COLLAPSED}. 63 */ 64 public static boolean isCollapsed(int state) { 65 return state > EXPANDED; 66 } 67 } 68 69 public ConversationViewState() {} 70 71 /** 72 * Copy constructor that will copy overall conversation state, but NOT individual message state. 73 */ 74 public ConversationViewState(ConversationViewState other) { 75 mConversationInfo = other.mConversationInfo; 76 } 77 78 public boolean isUnread(Message m) { 79 final MessageViewState mvs = mMessageViewStates.get(m.uri); 80 return (mvs != null && !mvs.read); 81 } 82 83 public void setReadState(Message m, boolean read) { 84 MessageViewState mvs = mMessageViewStates.get(m.uri); 85 if (mvs == null) { 86 mvs = new MessageViewState(); 87 } 88 mvs.read = read; 89 mMessageViewStates.put(m.uri, mvs); 90 } 91 92 public boolean getShouldShowImages(Message m) { 93 final MessageViewState mvs = mMessageViewStates.get(m.uri); 94 return (mvs != null && mvs.showImages); 95 } 96 97 public void setShouldShowImages(Message m, boolean showImages) { 98 MessageViewState mvs = mMessageViewStates.get(m.uri); 99 if (mvs == null) { 100 mvs = new MessageViewState(); 101 } 102 mvs.showImages = showImages; 103 mMessageViewStates.put(m.uri, mvs); 104 } 105 106 /** 107 * Returns the expansion state of a message in a conversation view. 108 * 109 * @param m a Message in the conversation 110 * @return 1 = expanded, 2 = collapsed, 3 = super collapsed, or null otherwise 111 * (see {@link ExpansionState}). 112 */ 113 public Integer getExpansionState(Message m) { 114 final MessageViewState mvs = mMessageViewStates.get(m.uri); 115 return (mvs == null ? null : mvs.expansionState); 116 } 117 118 public void setExpansionState(Message m, int expansionState) { 119 MessageViewState mvs = mMessageViewStates.get(m.uri); 120 if (mvs == null) { 121 mvs = new MessageViewState(); 122 } 123 mvs.expansionState = expansionState; 124 mMessageViewStates.put(m.uri, mvs); 125 } 126 127 public byte[] getConversationInfo() { 128 return mConversationInfo; 129 } 130 131 public void setInfoForConversation(Conversation conv) { 132 mConversationInfo = conv.conversationInfo != null ? conv.conversationInfo.toBlob() : null; 133 } 134 135 /** 136 * Returns URIs of all unread messages in the conversation per 137 * {@link #setReadState(Message, boolean)}. Returns an empty set for read conversations. 138 * 139 */ 140 public Set<Uri> getUnreadMessageUris() { 141 final Set<Uri> result = Sets.newHashSet(); 142 for (Uri uri : mMessageViewStates.keySet()) { 143 final MessageViewState mvs = mMessageViewStates.get(uri); 144 if (mvs != null && !mvs.read) { 145 result.add(uri); 146 } 147 } 148 return result; 149 } 150 151 public boolean contains(Message m) { 152 return mMessageViewStates.containsKey(m.uri); 153 } 154 155 @Override 156 public int describeContents() { 157 return 0; 158 } 159 160 @Override 161 public void writeToParcel(Parcel dest, int flags) { 162 final Bundle states = new Bundle(); 163 for (Uri uri : mMessageViewStates.keySet()) { 164 final MessageViewState mvs = mMessageViewStates.get(uri); 165 states.putParcelable(uri.toString(), mvs); 166 } 167 dest.writeBundle(states); 168 dest.writeByteArray(mConversationInfo); 169 } 170 171 private ConversationViewState(Parcel source, ClassLoader loader) { 172 final Bundle states = source.readBundle(loader); 173 for (String key : states.keySet()) { 174 final MessageViewState state = states.getParcelable(key); 175 mMessageViewStates.put(Uri.parse(key), state); 176 } 177 mConversationInfo = source.createByteArray(); 178 } 179 180 public static final ClassLoaderCreator<ConversationViewState> CREATOR = 181 new ClassLoaderCreator<ConversationViewState>() { 182 183 @Override 184 public ConversationViewState createFromParcel(Parcel source) { 185 return new ConversationViewState(source, null); 186 } 187 188 @Override 189 public ConversationViewState createFromParcel(Parcel source, ClassLoader loader) { 190 return new ConversationViewState(source, loader); 191 } 192 193 @Override 194 public ConversationViewState[] newArray(int size) { 195 return new ConversationViewState[size]; 196 } 197 198 }; 199 200 // Keep per-message state in an inner Parcelable. 201 // This is a semi-private implementation detail. 202 static class MessageViewState implements Parcelable { 203 204 public boolean read; 205 /** 206 * See {@link ExpansionState} for values. 207 * 208 */ 209 public Integer expansionState; 210 public boolean showImages; 211 212 public MessageViewState() {} 213 214 @Override 215 public int describeContents() { 216 return 0; 217 } 218 219 @Override 220 public void writeToParcel(Parcel dest, int flags) { 221 dest.writeInt(read ? 1 : 0); 222 dest.writeInt(expansionState == null ? -1 : expansionState.intValue()); 223 dest.writeInt(showImages ? 1 : 0); 224 } 225 226 private MessageViewState(Parcel source) { 227 read = (source.readInt() != 0); 228 final int expandedVal = source.readInt(); 229 expansionState = (expandedVal == -1) ? null : expandedVal; 230 showImages = (source.readInt() != 0); 231 } 232 233 @SuppressWarnings("hiding") 234 public static final Parcelable.Creator<MessageViewState> CREATOR = 235 new Parcelable.Creator<MessageViewState>() { 236 237 @Override 238 public MessageViewState createFromParcel(Parcel source) { 239 return new MessageViewState(source); 240 } 241 242 @Override 243 public MessageViewState[] newArray(int size) { 244 return new MessageViewState[size]; 245 } 246 247 }; 248 249 } 250 251 } 252