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
18package com.android.mail.ui;
19
20import android.net.Uri;
21import android.os.Bundle;
22import android.os.Parcel;
23import android.os.Parcelable;
24
25import com.android.mail.providers.Conversation;
26import com.android.mail.providers.Message;
27import com.google.common.collect.Maps;
28import com.google.common.collect.Sets;
29
30import java.util.Map;
31import java.util.Set;
32
33/**
34 * A small class to keep state for conversation view when restoring.
35 *
36 */
37public 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    public ConversationViewState() {}
62
63    /**
64     * Copy constructor that will copy overall conversation state, but NOT individual message state.
65     */
66    public ConversationViewState(ConversationViewState other) {
67        mConversationInfo = other.mConversationInfo;
68    }
69
70    public boolean isUnread(Message m) {
71        final MessageViewState mvs = mMessageViewStates.get(m.uri);
72        return (mvs != null && !mvs.read);
73    }
74
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
84    public boolean getShouldShowImages(Message m) {
85        final MessageViewState mvs = mMessageViewStates.get(m.uri);
86        return (mvs != null && mvs.showImages);
87    }
88
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     */
105    public Integer getExpansionState(Message m) {
106        final MessageViewState mvs = mMessageViewStates.get(m.uri);
107        return (mvs == null ? null : mvs.expansionState);
108    }
109
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
119    public byte[] getConversationInfo() {
120        return mConversationInfo;
121    }
122
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     */
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
143    public boolean contains(Message m) {
144        return mMessageViewStates.containsKey(m.uri);
145    }
146
147    @Override
148    public int describeContents() {
149        return 0;
150    }
151
152    @Override
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
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
204        public MessageViewState() {}
205
206        @Override
207        public int describeContents() {
208            return 0;
209        }
210
211        @Override
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
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