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 
ExpansionState()51         private ExpansionState() {}
52 
isExpanded(int state)53         public static boolean isExpanded(int state) {
54             return state == EXPANDED;
55         }
isSuperCollapsed(int state)56         public static boolean isSuperCollapsed(int state) {
57             return state == SUPER_COLLAPSED;
58         }
59     }
60 
ConversationViewState()61     public ConversationViewState() {}
62 
63     /**
64      * Copy constructor that will copy overall conversation state, but NOT individual message state.
65      */
ConversationViewState(ConversationViewState other)66     public ConversationViewState(ConversationViewState other) {
67         mConversationInfo = other.mConversationInfo;
68     }
69 
isUnread(Message m)70     public boolean isUnread(Message m) {
71         final MessageViewState mvs = mMessageViewStates.get(m.uri);
72         return (mvs != null && !mvs.read);
73     }
74 
setReadState(Message m, boolean read)75     public void setReadState(Message m, boolean read) {
76         MessageViewState mvs = mMessageViewStates.get(m.uri);
77         if (mvs == null) {
78             mvs = new MessageViewState();
79         }
80         mvs.read = read;
81         mMessageViewStates.put(m.uri, mvs);
82     }
83 
getShouldShowImages(Message m)84     public boolean getShouldShowImages(Message m) {
85         final MessageViewState mvs = mMessageViewStates.get(m.uri);
86         return (mvs != null && mvs.showImages);
87     }
88 
setShouldShowImages(Message m, boolean showImages)89     public void setShouldShowImages(Message m, boolean showImages) {
90         MessageViewState mvs = mMessageViewStates.get(m.uri);
91         if (mvs == null) {
92             mvs = new MessageViewState();
93         }
94         mvs.showImages = showImages;
95         mMessageViewStates.put(m.uri, mvs);
96     }
97 
98     /**
99      * Returns the expansion state of a message in a conversation view.
100      *
101      * @param m a Message in the conversation
102      * @return 1 = expanded, 2 = collapsed, 3 = super collapsed, or null otherwise
103      * (see {@link ExpansionState}).
104      */
getExpansionState(Message m)105     public Integer getExpansionState(Message m) {
106         final MessageViewState mvs = mMessageViewStates.get(m.uri);
107         return (mvs == null ? null : mvs.expansionState);
108     }
109 
setExpansionState(Message m, int expansionState)110     public void setExpansionState(Message m, int expansionState) {
111         MessageViewState mvs = mMessageViewStates.get(m.uri);
112         if (mvs == null) {
113             mvs = new MessageViewState();
114         }
115         mvs.expansionState = expansionState;
116         mMessageViewStates.put(m.uri, mvs);
117     }
118 
getConversationInfo()119     public byte[] getConversationInfo() {
120         return mConversationInfo;
121     }
122 
setInfoForConversation(Conversation conv)123     public void setInfoForConversation(Conversation conv) {
124         mConversationInfo = conv.conversationInfo.toBlob();
125     }
126 
127     /**
128      * Returns URIs of all unread messages in the conversation per
129      * {@link #setReadState(Message, boolean)}. Returns an empty set for read conversations.
130      *
131      */
getUnreadMessageUris()132     public Set<Uri> getUnreadMessageUris() {
133         final Set<Uri> result = Sets.newHashSet();
134         for (Uri uri : mMessageViewStates.keySet()) {
135             final MessageViewState mvs = mMessageViewStates.get(uri);
136             if (mvs != null && !mvs.read) {
137                 result.add(uri);
138             }
139         }
140         return result;
141     }
142 
contains(Message m)143     public boolean contains(Message m) {
144         return mMessageViewStates.containsKey(m.uri);
145     }
146 
147     @Override
describeContents()148     public int describeContents() {
149         return 0;
150     }
151 
152     @Override
writeToParcel(Parcel dest, int flags)153     public void writeToParcel(Parcel dest, int flags) {
154         final Bundle states = new Bundle();
155         for (Uri uri : mMessageViewStates.keySet()) {
156             final MessageViewState mvs = mMessageViewStates.get(uri);
157             states.putParcelable(uri.toString(), mvs);
158         }
159         dest.writeBundle(states);
160         dest.writeByteArray(mConversationInfo);
161     }
162 
ConversationViewState(Parcel source, ClassLoader loader)163     private ConversationViewState(Parcel source, ClassLoader loader) {
164         final Bundle states = source.readBundle(loader);
165         for (String key : states.keySet()) {
166             final MessageViewState state = states.getParcelable(key);
167             mMessageViewStates.put(Uri.parse(key), state);
168         }
169         mConversationInfo = source.createByteArray();
170     }
171 
172     public static final ClassLoaderCreator<ConversationViewState> CREATOR =
173             new ClassLoaderCreator<ConversationViewState>() {
174 
175         @Override
176         public ConversationViewState createFromParcel(Parcel source) {
177             return new ConversationViewState(source, null);
178         }
179 
180         @Override
181         public ConversationViewState createFromParcel(Parcel source, ClassLoader loader) {
182             return new ConversationViewState(source, loader);
183         }
184 
185         @Override
186         public ConversationViewState[] newArray(int size) {
187             return new ConversationViewState[size];
188         }
189 
190     };
191 
192     // Keep per-message state in an inner Parcelable.
193     // This is a semi-private implementation detail.
194     static class MessageViewState implements Parcelable {
195 
196         public boolean read;
197         /**
198          * See {@link ExpansionState} for values.
199          *
200          */
201         public Integer expansionState;
202         public boolean showImages;
203 
MessageViewState()204         public MessageViewState() {}
205 
206         @Override
describeContents()207         public int describeContents() {
208             return 0;
209         }
210 
211         @Override
writeToParcel(Parcel dest, int flags)212         public void writeToParcel(Parcel dest, int flags) {
213             dest.writeInt(read ? 1 : 0);
214             dest.writeInt(expansionState == null ? -1 : expansionState.intValue());
215             dest.writeInt(showImages ? 1 : 0);
216         }
217 
MessageViewState(Parcel source)218         private MessageViewState(Parcel source) {
219             read = (source.readInt() != 0);
220             final int expandedVal = source.readInt();
221             expansionState = (expandedVal == -1) ? null : expandedVal;
222             showImages = (source.readInt() != 0);
223         }
224 
225         @SuppressWarnings("hiding")
226         public static final Parcelable.Creator<MessageViewState> CREATOR =
227                 new Parcelable.Creator<MessageViewState>() {
228 
229             @Override
230             public MessageViewState createFromParcel(Parcel source) {
231                 return new MessageViewState(source);
232             }
233 
234             @Override
235             public MessageViewState[] newArray(int size) {
236                 return new MessageViewState[size];
237             }
238 
239         };
240 
241     }
242 
243 }
244