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