ConversationSelectionSet.java revision 20a97ded277fdcd3c63952a23718410c2882103f
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; 241ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwalimport com.android.mail.providers.Conversation; 252bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.annotations.VisibleForTesting; 262bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.collect.BiMap; 272bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.collect.HashBiMap; 282bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.collect.Lists; 292bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huangimport com.google.common.collect.Sets; 30a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 314a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport java.util.ArrayList; 324a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport java.util.Collection; 33a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport java.util.Collections; 344a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport java.util.HashMap; 35a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport java.util.HashSet; 36a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport java.util.Set; 374a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 384a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal/** 394a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * A simple thread-safe wrapper over a set of conversations representing a 404a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * selection set (e.g. in a conversation list). This class dispatches changes 414a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * when the set goes empty, and when it becomes unempty. For simplicity, this 424a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * class <b>does not allow modifications</b> to the collection in observers when 434a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * responding to change events. 444a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */ 454a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalpublic class ConversationSelectionSet implements Parcelable { 462bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang public static final ClassLoaderCreator<ConversationSelectionSet> CREATOR = 472bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang new ClassLoaderCreator<ConversationSelectionSet>() { 481ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 491ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal @Override 501ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal public ConversationSelectionSet createFromParcel(Parcel source) { 512bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang return new ConversationSelectionSet(source, null); 522bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 532bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 542bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang @Override 552bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang public ConversationSelectionSet createFromParcel(Parcel source, ClassLoader loader) { 562bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang return new ConversationSelectionSet(source, loader); 571ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 581ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 591ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal @Override 601ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal public ConversationSelectionSet[] newArray(int size) { 611ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal return new ConversationSelectionSet[size]; 621ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 632bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 641ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal }; 651ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 664e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private final Object mLock = new Object(); 671ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal private final HashMap<Long, Conversation> mInternalMap = 681ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal new HashMap<Long, Conversation>(); 694a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 70fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook private final BiMap<String, Long> mConversationUriToIdMap = HashBiMap.create(); 71866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 724a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal @VisibleForTesting 734a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal final ArrayList<ConversationSetObserver> mObservers = new ArrayList<ConversationSetObserver>(); 744a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 752bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang public ConversationSelectionSet() { 762bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 772bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 782bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang private ConversationSelectionSet(Parcel source, ClassLoader loader) { 792bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang Parcelable[] conversations = source.readParcelableArray(loader); 802bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang for (Parcelable parceled : conversations) { 812bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang Conversation conversation = (Conversation) parceled; 822bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang put(conversation.id, conversation); 832bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 842bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 852bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 864a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** 874a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * Registers an observer to listen for interesting changes on this set. 884a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * 894a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * @param observer the observer to register. 904a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */ 914e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void addObserver(ConversationSetObserver observer) { 924e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 934e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mObservers.add(observer); 944e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 954a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 964a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 97d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 98d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Clear the selected set entirely. 99d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 1004e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void clear() { 1014e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1024e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook boolean initiallyNotEmpty = !mInternalMap.isEmpty(); 1034e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.clear(); 1044e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mConversationUriToIdMap.clear(); 1054e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook 1064e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (mInternalMap.isEmpty() && initiallyNotEmpty) { 1074e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 1084e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 1094e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnEmpty(observersCopy); 1104e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1111ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1121ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1131ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 114d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 115d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns true if the given key exists in the conversation selection set. This assumes 116d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * the internal representation holds conversation.id values. 117d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @param key the id of the conversation 118d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return true if the key exists in this selected set. 119d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 12020a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal private boolean containsKey(Long key) { 1214e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1224e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.containsKey(key); 1234e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1241ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1251ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 126d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 127d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns true if the given conversation is stored in the selection set. 128d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @param conversation 129d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return true if the conversation exists in the selected set. 130d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 1314e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public boolean contains(Conversation conversation) { 1324e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1334e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return containsKey(conversation.id); 1344e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 135d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal } 136d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal 1371ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal @Override 1381ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal public int describeContents() { 1391ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal return 0; 1401ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1411ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 1424e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnBecomeUnempty(ArrayList<ConversationSetObserver> observers) { 1434e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1444e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1454e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetPopulated(this); 1464e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1471ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1484a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1494a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1504e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnChange(ArrayList<ConversationSetObserver> observers) { 1514e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1524e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Copy observers so that they may unregister themselves as listeners on 1534e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // event handling. 1544e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1554e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetChanged(this); 1564e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1574a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1584a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1594a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1604e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnEmpty(ArrayList<ConversationSetObserver> observers) { 1614e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1624e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1634e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetEmpty(); 1644e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1654a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1664a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1674a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1681ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** 1691ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * Is this conversation set empty? 1701ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @return true if the conversation selection set is empty. False otherwise. 1711ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal */ 1724e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public boolean isEmpty() { 1734e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1744e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.isEmpty(); 1754e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1761ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1771ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 1784e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void put(Long id, Conversation info) { 1794e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1804e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final boolean initiallyEmpty = mInternalMap.isEmpty(); 1814e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.put(id, info); 1824e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mConversationUriToIdMap.put(info.uri.toString(), id); 183866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 1844e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 1854e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 1864e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (initiallyEmpty) { 1874e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnBecomeUnempty(observersCopy); 1884e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 189866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira } 190866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira } 191866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 1924a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** @see java.util.HashMap#remove */ 1934e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void remove(Long id) { 1944e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1954e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook removeAll(Collections.singleton(id)); 1964e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 197a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook } 198a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 1994e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void removeAll(Collection<Long> ids) { 2004e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2014e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final boolean initiallyNotEmpty = !mInternalMap.isEmpty(); 202866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 2034e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final BiMap<Long, String> inverseMap = mConversationUriToIdMap.inverse(); 204fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook 2054e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (Long id : ids) { 2064e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.remove(id); 2074e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook inverseMap.remove(id); 2084e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2094a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2104e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 2114e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 2124e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (mInternalMap.isEmpty() && initiallyNotEmpty) { 2134e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnEmpty(observersCopy); 2144e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2154a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2164a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2174a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2181ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** 2191ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * Unregisters an observer for change events. 2201ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * 2211ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @param observer the observer to unregister. 2221ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal */ 2234e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void removeObserver(ConversationSetObserver observer) { 2244e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2254e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mObservers.remove(observer); 2264e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2274a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2284a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 229d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 230d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns the number of conversations that are currently selected 231d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return the number of selected conversations. 232d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 2334e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public int size() { 2344e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2354e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.size(); 2364e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2374a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2384a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2394a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** 240d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Toggles the existence of the given conversation in the selection set. If the conversation is 241d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * currently selected, it is deselected. If it doesn't exist in the selection set, then it is 24220a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal * selected. 2431ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @param conversation 2444a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */ 24520a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal public void toggle(Conversation conversation) { 24620a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal final long conversationId = conversation.id; 2471ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal if (containsKey(conversationId)) { 24872ef2e7ce576ad13ce6179a6a8c74f8ecad9ae85Vikram Aggarwal // We must not do anything with view here. 2491ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal remove(conversationId); 2504a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } else { 25120a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal put(conversationId, conversation); 2524a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2534a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2544a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2551ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** @see java.util.HashMap#values */ 2564e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public Collection<Conversation> values() { 2574e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2584e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.values(); 2594e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2604a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2614a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 262a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook /** @see java.util.HashMap#keySet() */ 2634e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public Set<Long> keySet() { 2644e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2654e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.keySet(); 2664e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 267a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook } 268a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 269cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal /** 270cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * Puts all conversations given in the input argument into the selection set. If there are 271cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * any listeners they are notified once after adding <em>all</em> conversations to the selection 272cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * set. 273cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * @see java.util.HashMap#putAll(java.util.Map) 274cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal */ 2754556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang public void putAll(ConversationSelectionSet other) { 2764556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang if (other == null) { 2774556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang return; 2784556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 2794556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 2804556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang final boolean initiallyEmpty = mInternalMap.isEmpty(); 2814556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang mInternalMap.putAll(other.mInternalMap); 2824556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 283669947bf67a3d0eed377b18099bb873604ad5009Vikram Aggarwal final ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 2844556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang dispatchOnChange(observersCopy); 2854556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang if (initiallyEmpty) { 2864556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang dispatchOnBecomeUnempty(observersCopy); 2874556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 2884556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 2894556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 2904a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal @Override 2914a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal public void writeToParcel(Parcel dest, int flags) { 2921ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal Conversation[] values = values().toArray(new Conversation[size()]); 2934a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal dest.writeParcelableArray(values, flags); 2944a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 295866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 29604dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal /** 29704dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal * @param deletedRows an arraylist of conversation IDs which have been deleted. 29804dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal */ 29904dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal public void delete(ArrayList<Integer> deletedRows) { 30004dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal for (long id : deletedRows) { 30104dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal remove(id); 30204dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal } 30304dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal } 304a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 305a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook /** 306a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * Iterates through a cursor of conversations and ensures that the current set is present 307a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * within the result set denoted by the cursor. Any conversations not foun in the result set 308a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * is removed from the collection. 309a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook */ 3104e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void validateAgainstCursor(ConversationCursor cursor) { 3114e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 3124e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (isEmpty()) { 3134e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return; 3144e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 315a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3164e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (cursor == null) { 3174e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook clear(); 3184e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return; 3194e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 320a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3214e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // First ask the ConversationCursor for the list of conversations that have been deleted 3224e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<String> deletedConversations = cursor.getDeletedItems(); 3234e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // For each of the uris in the deleted set, add the conversation id to the 3244e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // itemsToRemoveFromBatch set. 3254e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<Long> itemsToRemoveFromBatch = Sets.newHashSet(); 3264e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (String conversationUri : deletedConversations) { 3274e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Long conversationId = mConversationUriToIdMap.get(conversationUri); 3284e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (conversationId != null) { 3294e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook itemsToRemoveFromBatch.add(conversationId); 3304e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 331fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook } 332fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook 3334e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Get the set of the items that had been in the batch 3344e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<Long> batchConversationToCheck = new HashSet<Long>(keySet()); 3356b872ca329daa3ab9704731459d9f10c159a8298Paul Westbrook 3364e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Remove all of the items that we know are missing. This will leave the items where 3374e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // we need to check for existence in the cursor 3384e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook batchConversationToCheck.removeAll(itemsToRemoveFromBatch); 339fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // At this point batchConversationToCheck contains the conversation ids for the 340fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // conversations that had been in the batch selection, with the items we know have been 341fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // deleted removed. 3426b872ca329daa3ab9704731459d9f10c159a8298Paul Westbrook 343fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // This set contains the conversation ids that are in the conversation cursor 3440a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook final Set<Long> cursorConversationIds = cursor.getConversationIds(); 345fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook 346fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // We want to remove all of the valid items that are in the conversation cursor, from 347fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // the batchConversations to check. The goal is after this block, anything remaining 348fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // would be items that don't exist in the conversation cursor anymore. 349fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook if (!batchConversationToCheck.isEmpty() && cursorConversationIds != null) { 3500a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook batchConversationToCheck.removeAll(cursorConversationIds); 3514e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 3524e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook 3534e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // At this point any of the item that are remaining in the batchConversationToCheck set 3544e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // are to be removed from the selected conversation set 3554e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook itemsToRemoveFromBatch.addAll(batchConversationToCheck); 356a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3574e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook removeAll(itemsToRemoveFromBatch); 3584e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 3594e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 360a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3614e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook @Override 3624e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public String toString() { 3634e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 3644e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return String.format("%s:%s", super.toString(), mInternalMap); 36544c1571a6a81eda7dd508f74b64adae1e124101bPaul Westbrook } 36644c1571a6a81eda7dd508f74b64adae1e124101bPaul Westbrook } 3674a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal} 368