ConversationSelectionSet.java revision 2bc8bc1f69faacaddab41ab61e1d2e8658f4181f
14a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal/******************************************************************************* 24a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * Copyright (C) 2012 Google Inc. 34a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * Licensed to The Android Open Source Project. 44a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * 54a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * Licensed under the Apache License, Version 2.0 (the "License"); 64a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * you may not use this file except in compliance with the License. 74a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * You may obtain a copy of the License at 84a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * 94a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * http://www.apache.org/licenses/LICENSE-2.0 104a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * 114a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * Unless required by applicable law or agreed to in writing, software 124a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * distributed under the License is distributed on an "AS IS" BASIS, 134a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 144a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * See the License for the specific language governing permissions and 154a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * limitations under the License. 164a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *******************************************************************************/ 174a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 184a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalpackage com.android.mail.ui; 194a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 204a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport android.os.Parcel; 214a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport android.os.Parcelable; 224a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 23a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport com.android.mail.browse.ConversationCursor; 242bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.android.mail.browse.ConversationItemView; 251ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwalimport com.android.mail.providers.Conversation; 262bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.annotations.VisibleForTesting; 272bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.collect.BiMap; 282bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.collect.HashBiMap; 292bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.collect.Lists; 302bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.collect.Sets; 31a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 324a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport java.util.ArrayList; 334a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport java.util.Collection; 34a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport java.util.Collections; 354a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport java.util.HashMap; 36a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport java.util.HashSet; 37a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport java.util.Set; 384a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 394a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal/** 404a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * A simple thread-safe wrapper over a set of conversations representing a 414a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * selection set (e.g. in a conversation list). This class dispatches changes 424a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * when the set goes empty, and when it becomes unempty. For simplicity, this 434a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * class <b>does not allow modifications</b> to the collection in observers when 444a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * responding to change events. 454a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */ 464a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalpublic class ConversationSelectionSet implements Parcelable { 472bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang public static final ClassLoaderCreator<ConversationSelectionSet> CREATOR = 482bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang new ClassLoaderCreator<ConversationSelectionSet>() { 491ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 501ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal @Override 511ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal public ConversationSelectionSet createFromParcel(Parcel source) { 522bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang return new ConversationSelectionSet(source, null); 532bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 542bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 552bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang @Override 562bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang public ConversationSelectionSet createFromParcel(Parcel source, ClassLoader loader) { 572bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang return new ConversationSelectionSet(source, loader); 581ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 591ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 601ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal @Override 611ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal public ConversationSelectionSet[] newArray(int size) { 621ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal return new ConversationSelectionSet[size]; 631ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 642bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 651ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal }; 661ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 674e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private final Object mLock = new Object(); 681ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal private final HashMap<Long, Conversation> mInternalMap = 691ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal new HashMap<Long, Conversation>(); 704a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 71c7e3a7db9ff9cb43241995001462764b70b75e96Vikram Aggarwal /** 72c7e3a7db9ff9cb43241995001462764b70b75e96Vikram Aggarwal * Map of conversation IDs to {@link ConversationItemView} objects. The views are <b>not</b> 73c7e3a7db9ff9cb43241995001462764b70b75e96Vikram Aggarwal * updated when a new list view object is created on orientation change. 74c7e3a7db9ff9cb43241995001462764b70b75e96Vikram Aggarwal */ 75866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira private final HashMap<Long, ConversationItemView> mInternalViewMap = 76866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira new HashMap<Long, ConversationItemView>(); 77fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook private final BiMap<String, Long> mConversationUriToIdMap = HashBiMap.create(); 78866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 794a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal @VisibleForTesting 804a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal final ArrayList<ConversationSetObserver> mObservers = new ArrayList<ConversationSetObserver>(); 814a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 822bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang public ConversationSelectionSet() { 832bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 842bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 852bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang private ConversationSelectionSet(Parcel source, ClassLoader loader) { 862bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang Parcelable[] conversations = source.readParcelableArray(loader); 872bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang for (Parcelable parceled : conversations) { 882bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang Conversation conversation = (Conversation) parceled; 892bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang put(conversation.id, conversation); 902bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 912bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 922bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 934a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** 944a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * Registers an observer to listen for interesting changes on this set. 954a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * 964a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * @param observer the observer to register. 974a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */ 984e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void addObserver(ConversationSetObserver observer) { 994e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1004e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mObservers.add(observer); 1014e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1024a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1034a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 104d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 105d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Clear the selected set entirely. 106d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 1074e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void clear() { 1084e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1094e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook boolean initiallyNotEmpty = !mInternalMap.isEmpty(); 1104e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalViewMap.clear(); 1114e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.clear(); 1124e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mConversationUriToIdMap.clear(); 1134e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook 1144e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (mInternalMap.isEmpty() && initiallyNotEmpty) { 1154e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 1164e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 1174e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnEmpty(observersCopy); 1184e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1191ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1201ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1211ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 122d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 123d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns true if the given key exists in the conversation selection set. This assumes 124d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * the internal representation holds conversation.id values. 125d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @param key the id of the conversation 126d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return true if the key exists in this selected set. 127d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 1284e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public boolean containsKey(Long key) { 1294e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1304e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.containsKey(key); 1314e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1321ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1331ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 134d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 135d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns true if the given conversation is stored in the selection set. 136d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @param conversation 137d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return true if the conversation exists in the selected set. 138d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 1394e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public boolean contains(Conversation conversation) { 1404e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1414e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return containsKey(conversation.id); 1424e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 143d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal } 144d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal 1451ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal @Override 1461ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal public int describeContents() { 1471ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal return 0; 1481ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1491ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 1504e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnBecomeUnempty(ArrayList<ConversationSetObserver> observers) { 1514e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1524e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1534e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetPopulated(this); 1544e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1551ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1564a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1574a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1584e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnChange(ArrayList<ConversationSetObserver> observers) { 1594e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1604e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Copy observers so that they may unregister themselves as listeners on 1614e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // event handling. 1624e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1634e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetChanged(this); 1644e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1654a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1664a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1674a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1684e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnEmpty(ArrayList<ConversationSetObserver> observers) { 1694e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1704e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1714e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetEmpty(); 1724e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1734a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1744a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1754a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1761ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** 1771ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * Is this conversation set empty? 1781ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @return true if the conversation selection set is empty. False otherwise. 1791ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal */ 1804e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public boolean isEmpty() { 1814e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1824e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.isEmpty(); 1834e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1841ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1851ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 1864e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void put(Long id, Conversation info) { 1874e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1884e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final boolean initiallyEmpty = mInternalMap.isEmpty(); 1894e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.put(id, info); 1904e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Fill out the view map with null. The sizes will match, but 1914e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // we won't have any views available yet to store. 1924e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalViewMap.put(id, null); 1934e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mConversationUriToIdMap.put(info.uri.toString(), id); 194866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 1954e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 1964e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 1974e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (initiallyEmpty) { 1984e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnBecomeUnempty(observersCopy); 1994e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 200866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira } 201866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira } 202866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 203866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira /** @see java.util.HashMap#put */ 2044e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void put(Long id, ConversationItemView info) { 2054e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2064e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook boolean initiallyEmpty = mInternalMap.isEmpty(); 2074e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalViewMap.put(id, info); 2084e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.put(id, info.mHeader.conversation); 2094e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mConversationUriToIdMap.put(info.mHeader.conversation.uri.toString(), id); 2104e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook 2114e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 2124e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 2134e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (initiallyEmpty) { 2144e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnBecomeUnempty(observersCopy); 2154e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2164a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2174a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2184a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2194a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** @see java.util.HashMap#remove */ 2204e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void remove(Long id) { 2214e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2224e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook removeAll(Collections.singleton(id)); 2234e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 224a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook } 225a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 2264e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void removeAll(Collection<Long> ids) { 2274e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2284e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final boolean initiallyNotEmpty = !mInternalMap.isEmpty(); 229866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 2304e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final BiMap<Long, String> inverseMap = mConversationUriToIdMap.inverse(); 231fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook 2324e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (Long id : ids) { 2334e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalViewMap.remove(id); 2344e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.remove(id); 2354e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook inverseMap.remove(id); 2364e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2374a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2384e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 2394e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 2404e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (mInternalMap.isEmpty() && initiallyNotEmpty) { 2414e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnEmpty(observersCopy); 2424e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2434a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2444a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2454a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2461ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** 2471ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * Unregisters an observer for change events. 2481ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * 2491ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @param observer the observer to unregister. 2501ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal */ 2514e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void removeObserver(ConversationSetObserver observer) { 2524e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2534e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mObservers.remove(observer); 2544e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2554a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2564a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 257d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 258d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns the number of conversations that are currently selected 259d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return the number of selected conversations. 260d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 2614e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public int size() { 2624e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2634e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.size(); 2644e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2654a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2664a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2674a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** 268d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Toggles the existence of the given conversation in the selection set. If the conversation is 269d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * currently selected, it is deselected. If it doesn't exist in the selection set, then it is 27072ef2e7ce576ad13ce6179a6a8c74f8ecad9ae85Vikram Aggarwal * selected. If you are certain that you are deselecting a conversation (you have verified 27172ef2e7ce576ad13ce6179a6a8c74f8ecad9ae85Vikram Aggarwal * that {@link #contains(Conversation)} or {@link #containsKey(Long)} are true), then you 27272ef2e7ce576ad13ce6179a6a8c74f8ecad9ae85Vikram Aggarwal * may pass a null {@link ConversationItemView}. 2731ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @param conversation 2744a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */ 275866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira public void toggle(ConversationItemView view, Conversation conversation) { 2761ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal long conversationId = conversation.id; 2771ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal if (containsKey(conversationId)) { 27872ef2e7ce576ad13ce6179a6a8c74f8ecad9ae85Vikram Aggarwal // We must not do anything with view here. 2791ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal remove(conversationId); 2804a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } else { 281866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira put(conversationId, view); 2824a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2834a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2844a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2851ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** @see java.util.HashMap#values */ 2864e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public Collection<Conversation> values() { 2874e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2884e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.values(); 2894e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2904a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2914a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 292a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook /** @see java.util.HashMap#keySet() */ 2934e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public Set<Long> keySet() { 2944e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2954e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.keySet(); 2964e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 297a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook } 298a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 299cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal /** 300cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * Puts all conversations given in the input argument into the selection set. If there are 301cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * any listeners they are notified once after adding <em>all</em> conversations to the selection 302cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * set. 303cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * @see java.util.HashMap#putAll(java.util.Map) 304cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal */ 3054556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang public void putAll(ConversationSelectionSet other) { 3064556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang if (other == null) { 3074556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang return; 3084556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 3094556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 3104556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang final boolean initiallyEmpty = mInternalMap.isEmpty(); 3114556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang mInternalMap.putAll(other.mInternalMap); 3124556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 3134e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<Long> keys = other.mInternalMap.keySet(); 3144e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (Long key : keys) { 3154e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Fill out the view map with null. The sizes will match, but 3164e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // we won't have any views available yet to store. 3174e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalViewMap.put(key, null); 3184e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 3194e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook 3204556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 3214556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang dispatchOnChange(observersCopy); 3224556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang if (initiallyEmpty) { 3234556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang dispatchOnBecomeUnempty(observersCopy); 3244556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 3254556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 3264556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 3274a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal @Override 3284a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal public void writeToParcel(Parcel dest, int flags) { 3291ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal Conversation[] values = values().toArray(new Conversation[size()]); 3304a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal dest.writeParcelableArray(values, flags); 3314a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 332866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 333866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira public Collection<ConversationItemView> views() { 334866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira return mInternalViewMap.values(); 335866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira } 33604dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal 33704dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal /** 33804dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal * @param deletedRows an arraylist of conversation IDs which have been deleted. 33904dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal */ 34004dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal public void delete(ArrayList<Integer> deletedRows) { 34104dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal for (long id : deletedRows) { 34204dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal remove(id); 34304dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal } 34404dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal } 345a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 346a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook /** 347a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * Iterates through a cursor of conversations and ensures that the current set is present 348a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * within the result set denoted by the cursor. Any conversations not foun in the result set 349a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * is removed from the collection. 350a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook */ 3514e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void validateAgainstCursor(ConversationCursor cursor) { 3524e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 3534e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (isEmpty()) { 3544e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return; 3554e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 356a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3574e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (cursor == null) { 3584e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook clear(); 3594e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return; 3604e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 361a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3624e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // First ask the ConversationCursor for the list of conversations that have been deleted 3634e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<String> deletedConversations = cursor.getDeletedItems(); 3644e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // For each of the uris in the deleted set, add the conversation id to the 3654e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // itemsToRemoveFromBatch set. 3664e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<Long> itemsToRemoveFromBatch = Sets.newHashSet(); 3674e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (String conversationUri : deletedConversations) { 3684e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Long conversationId = mConversationUriToIdMap.get(conversationUri); 3694e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (conversationId != null) { 3704e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook itemsToRemoveFromBatch.add(conversationId); 3714e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 372fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook } 373fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook 3744e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Get the set of the items that had been in the batch 3754e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<Long> batchConversationToCheck = new HashSet<Long>(keySet()); 3766b872ca329daa3ab9704731459d9f10c159a8298Paul Westbrook 3774e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Remove all of the items that we know are missing. This will leave the items where 3784e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // we need to check for existence in the cursor 3794e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook batchConversationToCheck.removeAll(itemsToRemoveFromBatch); 3806b872ca329daa3ab9704731459d9f10c159a8298Paul Westbrook 3810a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook // While there are items to check, remove all items that are still in the cursor. 3820a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook final Set<Long> cursorConversationIds = cursor.getConversationIds(); 3830a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook while (!batchConversationToCheck.isEmpty() && cursorConversationIds != null) { 3840a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook batchConversationToCheck.removeAll(cursorConversationIds); 3854e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 3864e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook 3874e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // At this point any of the item that are remaining in the batchConversationToCheck set 3884e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // are to be removed from the selected conversation set 3894e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook itemsToRemoveFromBatch.addAll(batchConversationToCheck); 390a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3914e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook removeAll(itemsToRemoveFromBatch); 3924e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 3934e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 394a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3954e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook @Override 3964e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public String toString() { 3974e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 3984e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return String.format("%s:%s", super.toString(), mInternalMap); 39944c1571a6a81eda7dd508f74b64adae1e124101bPaul Westbrook } 40044c1571a6a81eda7dd508f74b64adae1e124101bPaul Westbrook } 4014a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal} 402