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(); 67ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal /** Map of conversation ID to conversation objects. Every selected conversation is here. */ 68ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal private final HashMap<Long, Conversation> mInternalMap = new HashMap<Long, Conversation>(); 69ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal /** Map of Conversation URI to Conversation ID. */ 70fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook private final BiMap<String, Long> mConversationUriToIdMap = HashBiMap.create(); 71ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal /** All objects that are interested in changes to the selected set. */ 724a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal @VisibleForTesting 73f4ff7675cbc23c56b9b35f9f26574afef030f0afMark Wei final Set<ConversationSetObserver> mObservers = new HashSet<ConversationSetObserver>(); 744a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 75ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal /** 76ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal * Create a new object, 77ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal */ 782bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang public ConversationSelectionSet() { 79ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal // Do nothing. 802bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 812bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 822bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang private ConversationSelectionSet(Parcel source, ClassLoader loader) { 832bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang Parcelable[] conversations = source.readParcelableArray(loader); 842bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang for (Parcelable parceled : conversations) { 852bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang Conversation conversation = (Conversation) parceled; 862bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang put(conversation.id, conversation); 872bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 882bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang } 892bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang 904a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** 914a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * Registers an observer to listen for interesting changes on this set. 924a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * 934a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal * @param observer the observer to register. 944a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */ 954e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void addObserver(ConversationSetObserver observer) { 964e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 974e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mObservers.add(observer); 984e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 994a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1004a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 101d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 102d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Clear the selected set entirely. 103d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 1044e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void clear() { 1054e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1064e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook boolean initiallyNotEmpty = !mInternalMap.isEmpty(); 1074e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.clear(); 1084e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mConversationUriToIdMap.clear(); 1094e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook 1104e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (mInternalMap.isEmpty() && initiallyNotEmpty) { 1114e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 1124e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 1134e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnEmpty(observersCopy); 1144e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1151ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1161ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1171ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 118d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 119d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns true if the given key exists in the conversation selection set. This assumes 120d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * the internal representation holds conversation.id values. 121d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @param key the id of the conversation 122d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return true if the key exists in this selected set. 123d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 12420a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal private boolean containsKey(Long key) { 1254e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1264e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.containsKey(key); 1274e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1281ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1291ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 130d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 131d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns true if the given conversation is stored in the selection set. 132d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @param conversation 133d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return true if the conversation exists in the selected set. 134d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 1354e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public boolean contains(Conversation conversation) { 1364e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1374e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return containsKey(conversation.id); 1384e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 139d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal } 140d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal 1411ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal @Override 1421ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal public int describeContents() { 1431ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal return 0; 1441ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1451ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 1464e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnBecomeUnempty(ArrayList<ConversationSetObserver> observers) { 1474e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1484e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1494e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetPopulated(this); 1504e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1511ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1524a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1534a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1544e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnChange(ArrayList<ConversationSetObserver> observers) { 1554e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1564e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Copy observers so that they may unregister themselves as listeners on 1574e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // event handling. 1584e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1594e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetChanged(this); 1604e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1614a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1624a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1634a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1644e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void dispatchOnEmpty(ArrayList<ConversationSetObserver> observers) { 1654e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1664e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (ConversationSetObserver observer : observers) { 1674e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook observer.onSetEmpty(); 1684e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1694a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1704a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 1714a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 1721ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** 1731ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * Is this conversation set empty? 1741ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @return true if the conversation selection set is empty. False otherwise. 1751ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal */ 1764e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public boolean isEmpty() { 1774e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1784e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.isEmpty(); 1794e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 1801ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal } 1811ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal 1824e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void put(Long id, Conversation info) { 1834e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1844e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final boolean initiallyEmpty = mInternalMap.isEmpty(); 1854e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.put(id, info); 1864e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mConversationUriToIdMap.put(info.uri.toString(), id); 187866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 1884e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 1894e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 1904e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (initiallyEmpty) { 1914e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnBecomeUnempty(observersCopy); 1924e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 193866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira } 194866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira } 195866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 1964a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** @see java.util.HashMap#remove */ 1974e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void remove(Long id) { 1984e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 1994e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook removeAll(Collections.singleton(id)); 2004e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 201a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook } 202a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 2034e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook private void removeAll(Collection<Long> ids) { 2044e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2054e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final boolean initiallyNotEmpty = !mInternalMap.isEmpty(); 206866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 2074e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final BiMap<Long, String> inverseMap = mConversationUriToIdMap.inverse(); 208fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook 2094e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (Long id : ids) { 2104e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mInternalMap.remove(id); 2114e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook inverseMap.remove(id); 2124e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2134a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2144e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 2154e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnChange(observersCopy); 2164e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (mInternalMap.isEmpty() && initiallyNotEmpty) { 2174e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook dispatchOnEmpty(observersCopy); 2184e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2194a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2204a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2214a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2221ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** 2231ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * Unregisters an observer for change events. 2241ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * 2251ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @param observer the observer to unregister. 2261ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal */ 2274e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void removeObserver(ConversationSetObserver observer) { 2284e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2294e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook mObservers.remove(observer); 2304e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2314a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2324a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 233d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal /** 234d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Returns the number of conversations that are currently selected 235d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * @return the number of selected conversations. 236d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal */ 2374e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public int size() { 2384e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2394e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.size(); 2404e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2414a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2424a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2434a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal /** 244d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * Toggles the existence of the given conversation in the selection set. If the conversation is 245d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal * currently selected, it is deselected. If it doesn't exist in the selection set, then it is 24620a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal * selected. 2471ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal * @param conversation 2484a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */ 24920a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal public void toggle(Conversation conversation) { 25020a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal final long conversationId = conversation.id; 2511ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal if (containsKey(conversationId)) { 25272ef2e7ce576ad13ce6179a6a8c74f8ecad9ae85Vikram Aggarwal // We must not do anything with view here. 2531ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal remove(conversationId); 2544a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } else { 25520a97ded277fdcd3c63952a23718410c2882103fVikram Aggarwal put(conversationId, conversation); 2564a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2574a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2584a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 2591ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal /** @see java.util.HashMap#values */ 2604e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public Collection<Conversation> values() { 2614e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2624e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.values(); 2634e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 2644a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 2654a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal 266a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook /** @see java.util.HashMap#keySet() */ 2674e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public Set<Long> keySet() { 2684e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 2694e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return mInternalMap.keySet(); 2704e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 271a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook } 272a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 273cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal /** 274cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * Puts all conversations given in the input argument into the selection set. If there are 275cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * any listeners they are notified once after adding <em>all</em> conversations to the selection 276cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * set. 277cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal * @see java.util.HashMap#putAll(java.util.Map) 278cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal */ 2794556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang public void putAll(ConversationSelectionSet other) { 2804556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang if (other == null) { 2814556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang return; 2824556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 2834556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 2844556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang final boolean initiallyEmpty = mInternalMap.isEmpty(); 2854556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang mInternalMap.putAll(other.mInternalMap); 2864556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 287669947bf67a3d0eed377b18099bb873604ad5009Vikram Aggarwal final ArrayList<ConversationSetObserver> observersCopy = Lists.newArrayList(mObservers); 2884556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang dispatchOnChange(observersCopy); 2894556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang if (initiallyEmpty) { 2904556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang dispatchOnBecomeUnempty(observersCopy); 2914556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 2924556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang } 2934556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang 2944a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal @Override 2954a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal public void writeToParcel(Parcel dest, int flags) { 2961ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal Conversation[] values = values().toArray(new Conversation[size()]); 2974a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal dest.writeParcelableArray(values, flags); 2984a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal } 299866d319dd23ec8b7b7d5476c65f7f83469d55d2dMindy Pereira 30004dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal /** 30104dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal * @param deletedRows an arraylist of conversation IDs which have been deleted. 30204dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal */ 30304dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal public void delete(ArrayList<Integer> deletedRows) { 30404dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal for (long id : deletedRows) { 30504dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal remove(id); 30604dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal } 30704dc819886abc2e4f56e644b03449e29cdbae2d0Vikram Aggarwal } 308a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 309a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook /** 310a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * Iterates through a cursor of conversations and ensures that the current set is present 311a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * within the result set denoted by the cursor. Any conversations not foun in the result set 312a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * is removed from the collection. 313a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook */ 3144e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public void validateAgainstCursor(ConversationCursor cursor) { 3154e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 3164e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (isEmpty()) { 3174e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return; 3184e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 319a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3204e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (cursor == null) { 3214e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook clear(); 3224e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return; 3234e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 324a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3254e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // First ask the ConversationCursor for the list of conversations that have been deleted 3264e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<String> deletedConversations = cursor.getDeletedItems(); 3274e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // For each of the uris in the deleted set, add the conversation id to the 3284e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // itemsToRemoveFromBatch set. 3294e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<Long> itemsToRemoveFromBatch = Sets.newHashSet(); 3304e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook for (String conversationUri : deletedConversations) { 3314e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Long conversationId = mConversationUriToIdMap.get(conversationUri); 3324e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook if (conversationId != null) { 3334e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook itemsToRemoveFromBatch.add(conversationId); 3344e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 335fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook } 336fe7f5bb113014365746c9e1368773c5448645a9dPaul Westbrook 3374e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Get the set of the items that had been in the batch 3384e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook final Set<Long> batchConversationToCheck = new HashSet<Long>(keySet()); 3396b872ca329daa3ab9704731459d9f10c159a8298Paul Westbrook 3404e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // Remove all of the items that we know are missing. This will leave the items where 3414e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // we need to check for existence in the cursor 3424e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook batchConversationToCheck.removeAll(itemsToRemoveFromBatch); 343fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // At this point batchConversationToCheck contains the conversation ids for the 344fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // conversations that had been in the batch selection, with the items we know have been 345fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // deleted removed. 3466b872ca329daa3ab9704731459d9f10c159a8298Paul Westbrook 347fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // This set contains the conversation ids that are in the conversation cursor 3480a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook final Set<Long> cursorConversationIds = cursor.getConversationIds(); 349fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook 350fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // We want to remove all of the valid items that are in the conversation cursor, from 351fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // the batchConversations to check. The goal is after this block, anything remaining 352fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook // would be items that don't exist in the conversation cursor anymore. 353fa322a52f9d90e6cb8db9a4895eaace2de8de9a8Paul Westbrook if (!batchConversationToCheck.isEmpty() && cursorConversationIds != null) { 3540a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook batchConversationToCheck.removeAll(cursorConversationIds); 3554e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 3564e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook 3574e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // At this point any of the item that are remaining in the batchConversationToCheck set 3584e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook // are to be removed from the selected conversation set 3594e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook itemsToRemoveFromBatch.addAll(batchConversationToCheck); 360a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3614e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook removeAll(itemsToRemoveFromBatch); 3624e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 3634e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook } 364a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 3654e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook @Override 3664e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook public String toString() { 3674e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook synchronized (mLock) { 3684e22a2ad8e7b70d71710e5dc1d209320fe71929dPaul Westbrook return String.format("%s:%s", super.toString(), mInternalMap); 36944c1571a6a81eda7dd508f74b64adae1e124101bPaul Westbrook } 37044c1571a6a81eda7dd508f74b64adae1e124101bPaul Westbrook } 3714a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal} 372