Android Open Source - appboy-android-sdk Appboy List Adapter






From Project

Back to project page appboy-android-sdk.

License

The source code is released under:

Copyright (c) 2014 Appboy, Inc. All rights reserved. * Use of source code or binaries contained within Appboy's Android SDK is permitted only to enable use of the Appboy platform by customers of Appb...

If you think the Android project appboy-android-sdk listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.appboy.ui.adapters;
//  w  w w . j a  v  a2 s . com
import android.annotation.TargetApi;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;

import com.appboy.Constants;
import com.appboy.models.cards.BannerImageCard;
import com.appboy.models.cards.CaptionedImageCard;
import com.appboy.models.cards.Card;
import com.appboy.models.cards.CrossPromotionSmallCard;
import com.appboy.models.cards.ShortNewsCard;
import com.appboy.models.cards.TextAnnouncementCard;
import com.appboy.ui.configuration.XmlUIConfigurationProvider;
import com.appboy.ui.widget.BannerImageCardView;
import com.appboy.ui.widget.BaseCardView;
import com.appboy.ui.widget.CaptionedImageCardView;
import com.appboy.ui.widget.CrossPromotionSmallCardView;
import com.appboy.ui.widget.DefaultCardView;
import com.appboy.ui.widget.ShortNewsCardView;
import com.appboy.ui.widget.TextAnnouncementCardView;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Default adapter used to display cards and log card impressions for the Appboy feed.
 *
 * This allows the stream to reuse cards when they go out of view.
 *
 * IMPORTANT - When you add a new card, be sure to add the new view type and update the view count here
 *
 * A card generates an impression once per viewing per open ListView.?If a card is viewed more than once
 * in a particular ListView, it generates only one impression.?If closed an reopened, a card will again
 * generate an impression.?This also takes into account the case of a card being off-screen in the ListView.
 * The card only generates an impression when it actually scrolls onto the screen.
 *
 * IMPORTANT - You must call resetCardImpressionTracker() whenever the ListView is displayed. This will ensure
 *             that cards that come into view will be tracked according to the description above.
 *
 * Adding and removing cards to and from the adapter should be done using the following synchronized
 * methods: {@link com.appboy.ui.adapters.AppboyListAdapter#add(Card)},
 * {@link com.appboy.ui.adapters.AppboyListAdapter#clear()}clear(),
 * {@link com.appboy.ui.adapters.AppboyListAdapter#replaceFeed(java.util.List)}
 */
public class AppboyListAdapter extends ArrayAdapter<Card> {
  private static final String TAG = String.format("%s.%s", Constants.APPBOY_LOG_TAG_PREFIX, AppboyListAdapter.class.getName());

  private final Context mContext;
  private final Set<String> mCardIdImpressions;
  private final XmlUIConfigurationProvider mUiConfigurationProvider;

  public AppboyListAdapter(Context context, int layoutResourceId, List<Card> cards) {
    super(context, layoutResourceId, cards);
    mContext = context;
    mCardIdImpressions = new HashSet<String>();
    mUiConfigurationProvider = new XmlUIConfigurationProvider(context);
  }

  /**
   * Be sure to keep view count in sync with the number of card types in the stream.
   * It is used internally to return the correct card type and to cache views for reuse
   */
  @Override
  public int getViewTypeCount() {
    return 8;
  }

  @Override
  public int getItemViewType(int position) {
    Card card = getItem(position);
    if (card instanceof BannerImageCard) {
      return 1;
    } else if (card instanceof CaptionedImageCard) {
      return 2;
    } else if (card instanceof CrossPromotionSmallCard) {
      return 3;
    } else if (card instanceof ShortNewsCard) {
      return 4;
    } else if (card instanceof TextAnnouncementCard) {
      return 5;
    } else {
      return 0;
    }
  }

  /**
   * Always try to use a convert view if possible, otherwise create one from scratch. The convertView should always
   * be of the appropriate type, but it will be recycled, so you need to fully re-populate it with data from the card.
   */
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    BaseCardView view;
    Card card = getItem(position);

    if (convertView == null) {
      if (card instanceof BannerImageCard) {
        view = new BannerImageCardView(mContext);
      } else if (card instanceof CaptionedImageCard) {
        view = new CaptionedImageCardView(mContext);
      } else if (card instanceof CrossPromotionSmallCard) {
        view = new CrossPromotionSmallCardView(mContext);
      } else if (card instanceof ShortNewsCard) {
        view = new ShortNewsCardView(mContext);
      } else if (card instanceof TextAnnouncementCard) {
        view = new TextAnnouncementCardView(mContext);
      } else {
        view = new DefaultCardView(mContext);
      }
    } else {
      Log.d(TAG, "Reusing convertView for rendering of item " + position);
      view = (BaseCardView) convertView;
    }

    Log.d(TAG, String.format("Using view of type: %s for card at position %d: %s", view.getClass().getName(),
        position, card.toString()));
    view.setCard(card);
    logCardImpression(card);
    return view;
  }

  public synchronized void replaceFeed(List<Card> cards) {
    setNotifyOnChange(false);

    if (cards == null) {
      clear();
      notifyDataSetChanged();
      return;
    }

    Log.d(TAG, String.format("Replacing existing feed of %d cards with new feed containing %d cards.",
      getCount(), cards.size()));
    int i = 0, j = 0, newFeedSize = cards.size();
    Card existingCard, newCard;

    // Iterate over the entire existing feed, skipping items at the head of the list whenever they're the same as the
    // head of the new list and otherwise removing them.
    while (i < getCount()) {
      existingCard = getItem(i);
      newCard = null;

      // Only consider a new card if there are any left.
      if (j < newFeedSize) {
        newCard = cards.get(j);
      }

      // If there is still a card to add and it is the same as the next existing card in the feed, continue.
      if (newCard != null && newCard.isEqualToCard(existingCard)) {
        i++;
        j++;
      } else { // Otherwise, we need to get rid of the next card in the adapter, and continue checking.
        remove(existingCard);
      }
    }

    // Now we add the remainder of the feed.
    if (android.os.Build.VERSION.SDK_INT < 11) {
      while (j < newFeedSize) {
        add(cards.get(j));
        j++;
      }
    } else {
      addAllBatch(cards.subList(j, newFeedSize));
    }
    notifyDataSetChanged();
  }

  @Override
  public synchronized void add(Card card) {
    super.add(card);
  }

  @TargetApi(11)
  private synchronized void addAllBatch(Collection<Card> cards) {
    super.addAll(cards);
  }

  /**
   * Resets the list of viewed cards. This must be called every time the ListView is displayed and it will reset
   * the impressions.
   */
  public void resetCardImpressionTracker() {
    mCardIdImpressions.clear();
  }

  private void logCardImpression(Card card) {
    String cardId = card.getId();
    if (!mCardIdImpressions.contains(cardId)) {
      mCardIdImpressions.add(cardId);
      card.logImpression();
      Log.d(TAG, String.format("Logged impression for card %s", cardId));
    } else {
      Log.d(TAG, String.format("Already counted impression for card %s", cardId));
    }
    if (!card.getViewed()){
      card.setViewed(true);
    }
  }

  /**
   * Helper method to batch set cards to visually read after either an up or down scroll of the feed.
   * Since scrolls can have multiple cards scrolled off screen at a time, this method can batch set those
   * cards to read.
   * @param startIndex Where to start setting cards to viewed. The card at this index will
   *                   be set to viewed. Must be less than endIndex
   * @param endIndex Where to end setting cards to viewed. The card at this index will be set to viewed.
   */
  public void batchSetCardsToRead(int startIndex, int endIndex){
    if (getCount() == 0){
      Log.d(TAG, "mAdapter is empty in setting some cards to viewed.");
      return;
    }

    // Make sure the start and end are in bounds
    startIndex = Math.max(0, startIndex);
    endIndex = Math.min(getCount(), endIndex);

    for (int traversalIndex = startIndex; traversalIndex < endIndex; traversalIndex++){
      // Get the card
      Card card = getItem(traversalIndex);
      if (card == null){
        Log.d(TAG, "Card was null in setting some cards to viewed.");
        break;
      }

      if (!card.isRead()){
        card.setIsRead(true);
      }
    }
  }
}




Java Source Code List

com.android.vending.billing.utils.Base64DecoderException.java
com.android.vending.billing.utils.Base64.java
com.android.vending.billing.utils.IabException.java
com.android.vending.billing.utils.IabHelper.java
com.android.vending.billing.utils.IabResult.java
com.android.vending.billing.utils.Inventory.java
com.android.vending.billing.utils.Purchase.java
com.android.vending.billing.utils.Security.java
com.android.vending.billing.utils.SkuDetails.java
com.appboy.AppboyAdmReceiver.java
com.appboy.AppboyGcmReceiver.java
com.appboy.AppboyNotificationUtils.java
com.appboy.helloworld.HelloAppboyActivity.java
com.appboy.sample.AppboyBroadcastReceiver.java
com.appboy.sample.AppboyFragmentActivity.java
com.appboy.sample.CustomAppboyNavigator.java
com.appboy.sample.CustomSlideupManagerListener.java
com.appboy.sample.CustomSlideupViewFactory.java
com.appboy.sample.DecisionFragment.java
com.appboy.sample.DroidBoyActivity.java
com.appboy.sample.DroidGirlActivity.java
com.appboy.sample.DroidboyApplication.java
com.appboy.sample.FeedCategoriesFragment.java
com.appboy.sample.FeedFragmentActivity.java
com.appboy.sample.FeedbackFragmentActivity.java
com.appboy.sample.PreferencesActivity.java
com.appboy.sample.SlideupTesterActivity.java
com.appboy.sample.Test.java
com.appboy.sample.UserProfileDialog.java
com.appboy.sample.util.SharedPrefsUtil.java
com.appboy.ui.AppboyFeedFragment.java
com.appboy.ui.AppboyFeedbackFragment.java
com.appboy.ui.AppboyNavigator.java
com.appboy.ui.AppboyWebViewActivity.java
com.appboy.ui.actions.ActionFactory.java
com.appboy.ui.actions.ActivityAction.java
com.appboy.ui.actions.GooglePlayAppDetailsAction.java
com.appboy.ui.actions.IAction.java
com.appboy.ui.actions.ViewAction.java
com.appboy.ui.actions.WebAction.java
com.appboy.ui.activities.AppboyBaseActivity.java
com.appboy.ui.activities.AppboyBaseFragmentActivity.java
com.appboy.ui.activities.AppboyFeedActivity.java
com.appboy.ui.adapters.AppboyListAdapter.java
com.appboy.ui.configuration.XmlUIConfigurationProvider.java
com.appboy.ui.slideups.AppboySlideupManager.java
com.appboy.ui.slideups.ISlideupManagerListener.java
com.appboy.ui.slideups.ISlideupViewFactory.java
com.appboy.ui.slideups.ISlideupViewLifecycleListener.java
com.appboy.ui.slideups.SlideupCloser.java
com.appboy.ui.slideups.SlideupOperation.java
com.appboy.ui.slideups.SlideupViewWrapper.java
com.appboy.ui.slideups.SwipeDismissTouchListener.java
com.appboy.ui.slideups.TouchAwareSwipeDismissTouchListener.java
com.appboy.ui.support.DrawingUtils.java
com.appboy.ui.support.StringUtils.java
com.appboy.ui.support.UriUtils.java
com.appboy.ui.support.ViewUtils.java
com.appboy.ui.widget.BannerImageCardView.java
com.appboy.ui.widget.BaseCardView.java
com.appboy.ui.widget.CaptionedImageCardView.java
com.appboy.ui.widget.CrossPromotionSmallCardView.java
com.appboy.ui.widget.DefaultCardView.java
com.appboy.ui.widget.ShortNewsCardView.java
com.appboy.ui.widget.StarRatingView.java
com.appboy.ui.widget.TextAnnouncementCardView.java