1 /* 2 * Copyright 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 19 20 package com.example.android.batchstepsensor.cardstream; 21 22 import android.os.Bundle; 23 import android.support.v4.app.Fragment; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.view.ViewGroup; 27 28 import java.util.Collection; 29 import java.util.HashMap; 30 import java.util.HashSet; 31 import java.util.LinkedHashMap; 32 33 import com.example.android.batchstepsensor.R; 34 35 /** 36 * A Fragment that handles a stream of cards. 37 * Cards can be shown or hidden. When a card is shown it can also be marked as not-dismissible, see 38 * {@link CardStreamLinearLayout#addCard(android.view.View, boolean)}. 39 */ 40 public class CardStreamFragment extends Fragment { 41 42 private static final int INITIAL_SIZE = 15; 43 private CardStreamLinearLayout mLayout = null; 44 private LinkedHashMap<String, Card> mVisibleCards = new LinkedHashMap<String, Card>(INITIAL_SIZE); 45 private HashMap<String, Card> mHiddenCards = new HashMap<String, Card>(INITIAL_SIZE); 46 private HashSet<String> mDismissibleCards = new HashSet<String>(INITIAL_SIZE); 47 48 // Set the listener to handle dismissed cards by moving them to the hidden cards map. 49 private CardStreamLinearLayout.OnDissmissListener mCardDismissListener = 50 new CardStreamLinearLayout.OnDissmissListener() { 51 @Override 52 public void onDismiss(String tag) { 53 dismissCard(tag); 54 } 55 }; 56 57 58 @Override 59 public View onCreateView(LayoutInflater inflater, ViewGroup container, 60 Bundle savedInstanceState) { 61 62 View view = inflater.inflate(R.layout.cardstream, container, false); 63 mLayout = (CardStreamLinearLayout) view.findViewById(R.id.card_stream); 64 mLayout.setOnDismissListener(mCardDismissListener); 65 66 return view; 67 } 68 69 /** 70 * Add a visible, dismissible card to the card stream. 71 * 72 * @param card 73 */ 74 public void addCard(Card card) { 75 final String tag = card.getTag(); 76 77 if (!mVisibleCards.containsKey(tag) && !mHiddenCards.containsKey(tag)) { 78 final View view = card.getView(); 79 view.setTag(tag); 80 mHiddenCards.put(tag, card); 81 } 82 } 83 84 /** 85 * Add and show a card. 86 * 87 * @param card 88 * @param show 89 */ 90 public void addCard(Card card, boolean show) { 91 addCard(card); 92 if (show) { 93 showCard(card.getTag()); 94 } 95 } 96 97 /** 98 * Remove a card and return true if it has been successfully removed. 99 * 100 * @param tag 101 * @return 102 */ 103 public boolean removeCard(String tag) { 104 // Attempt to remove a visible card first 105 Card card = mVisibleCards.get(tag); 106 if (card != null) { 107 // Card is visible, also remove from layout 108 mVisibleCards.remove(tag); 109 mLayout.removeView(card.getView()); 110 return true; 111 } else { 112 // Card is hidden, no need to remove from layout 113 card = mHiddenCards.remove(tag); 114 return card != null; 115 } 116 } 117 118 /** 119 * Show a dismissible card, returns false if the card could not be shown. 120 * 121 * @param tag 122 * @return 123 */ 124 public boolean showCard(String tag) { 125 return showCard(tag, true); 126 } 127 128 /** 129 * Show a card, returns false if the card could not be shown. 130 * 131 * @param tag 132 * @param dismissible 133 * @return 134 */ 135 public boolean showCard(String tag, boolean dismissible) { 136 final Card card = mHiddenCards.get(tag); 137 // ensure the card is hidden and not already visible 138 if (card != null && !mVisibleCards.containsValue(tag)) { 139 mHiddenCards.remove(tag); 140 mVisibleCards.put(tag, card); 141 mLayout.addCard(card.getView(), dismissible); 142 if (dismissible) { 143 mDismissibleCards.add(tag); 144 } 145 return true; 146 } 147 return false; 148 } 149 150 /** 151 * Hides the card, returns false if the card could not be hidden. 152 * 153 * @param tag 154 * @return 155 */ 156 public boolean hideCard(String tag) { 157 final Card card = mVisibleCards.get(tag); 158 if (card != null) { 159 mVisibleCards.remove(tag); 160 mDismissibleCards.remove(tag); 161 mHiddenCards.put(tag, card); 162 163 mLayout.removeView(card.getView()); 164 return true; 165 } 166 return mHiddenCards.containsValue(tag); 167 } 168 169 170 private void dismissCard(String tag) { 171 final Card card = mVisibleCards.get(tag); 172 if (card != null) { 173 mDismissibleCards.remove(tag); 174 mVisibleCards.remove(tag); 175 mHiddenCards.put(tag, card); 176 } 177 } 178 179 180 public boolean isCardVisible(String tag) { 181 return mVisibleCards.containsValue(tag); 182 } 183 184 /** 185 * Returns true if the card is shown and is dismissible. 186 * 187 * @param tag 188 * @return 189 */ 190 public boolean isCardDismissible(String tag) { 191 return mDismissibleCards.contains(tag); 192 } 193 194 /** 195 * Returns the Card for this tag. 196 * 197 * @param tag 198 * @return 199 */ 200 public Card getCard(String tag) { 201 final Card card = mVisibleCards.get(tag); 202 if (card != null) { 203 return card; 204 } else { 205 return mHiddenCards.get(tag); 206 } 207 } 208 209 /** 210 * Moves the view port to show the card with this tag. 211 * 212 * @param tag 213 * @see CardStreamLinearLayout#setFirstVisibleCard(String) 214 */ 215 public void setFirstVisibleCard(String tag) { 216 final Card card = mVisibleCards.get(tag); 217 if (card != null) { 218 mLayout.setFirstVisibleCard(tag); 219 } 220 } 221 222 public int getVisibleCardCount() { 223 return mVisibleCards.size(); 224 } 225 226 public Collection<Card> getVisibleCards() { 227 return mVisibleCards.values(); 228 } 229 230 public void restoreState(CardStreamState state, OnCardClickListener callback) { 231 // restore hidden cards 232 for (Card c : state.hiddenCards) { 233 Card card = new Card.Builder(callback,c).build(getActivity()); 234 mHiddenCards.put(card.getTag(), card); 235 } 236 237 // temporarily set up list of dismissible 238 final HashSet<String> dismissibleCards = state.dismissibleCards; 239 240 //restore shown cards 241 for (Card c : state.visibleCards) { 242 Card card = new Card.Builder(callback,c).build(getActivity()); 243 addCard(card); 244 final String tag = card.getTag(); 245 showCard(tag, dismissibleCards.contains(tag)); 246 } 247 248 // move to first visible card 249 final String firstShown = state.shownTag; 250 if (firstShown != null) { 251 mLayout.setFirstVisibleCard(firstShown); 252 } 253 254 mLayout.triggerShowInitialAnimation(); 255 } 256 257 public CardStreamState dumpState() { 258 final Card[] visible = cloneCards(mVisibleCards.values()); 259 final Card[] hidden = cloneCards(mHiddenCards.values()); 260 final HashSet<String> dismissible = new HashSet<String>(mDismissibleCards); 261 final String firstVisible = mLayout.getFirstVisibleCardTag(); 262 263 return new CardStreamState(visible, hidden, dismissible, firstVisible); 264 } 265 266 private Card[] cloneCards(Collection<Card> cards) { 267 Card[] cardArray = new Card[cards.size()]; 268 int i = 0; 269 for (Card c : cards) { 270 cardArray[i++] = c.createShallowClone(); 271 } 272 273 return cardArray; 274 } 275 276 }