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