/* * Licensed Materials - Property of IBM * © Copyright IBM Corporation 2015. All Rights Reserved. */ package com.ibm.mil.readyapps.telco.offers; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.os.SystemClock; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.Snackbar; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.RelativeLayout; import com.ibm.mil.readyapps.telco.R; import com.ibm.mil.readyapps.telco.activities.MainActivity; import com.ibm.mil.readyapps.telco.baseplan.BasePlanModelImpl; import com.ibm.mil.readyapps.telco.termsconditions.TermsConditionsActivity; import com.ibm.mil.readyapps.telco.views.RobotoTextView; import com.yqritc.recyclerviewmultipleviewtypesadapter.DataBindAdapter; import com.yqritc.recyclerviewmultipleviewtypesadapter.DataBinder; import java.util.Iterator; import java.util.List; import butterknife.ButterKnife; import butterknife.Bind; import butterknife.OnClick; import rx.Observable; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; import rx.subjects.PublishSubject; /** * Binder to append the user myPlan offer information * to the corresponding recyclerView ViewHolder */ public class OfferBinder extends DataBinder<OfferBinder.OfferViewHolder> { private final CoordinatorLayout snackbar_layout; private final Context context; private final Resources resources; private long mLastClickTime = 0; private List<Offer> offers; private Offer offerToAccept; private int cardPosition; private final BasePlanModelImpl basePlanModel; private Snackbar snackbar; private final PublishSubject<Offer> offerAcceptStream; private final PublishSubject<Offer> offerDismissStream; private final PublishSubject<Offer> undoAcceptStream; /** * Constructor to initialize the cycle binder * * @param dataBindAdapter the adapter that uses this binder to populate the recyclerView * @param layout coordinator layout from the main activity * used for inflating the snackBar * @param context context from the main activity * used to access application resources */ public OfferBinder(DataBindAdapter dataBindAdapter, CoordinatorLayout layout, Context context) { super(dataBindAdapter); this.snackbar_layout = layout; this.context = context; resources = context.getResources(); offerAcceptStream = PublishSubject.create(); offerDismissStream = PublishSubject.create(); undoAcceptStream = PublishSubject.create(); basePlanModel = new BasePlanModelImpl(); initTabListener(); } /** * listen for tab changes */ private void initTabListener() { MainActivity.tabChangeStream .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Integer>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Integer tabPosition) { if (tabPosition != MainActivity.MY_PLAN) { removeAllShareCards(); } } }); } /** * remove share cards from my plan tab if any exist on tab change */ private void removeAllShareCards() { for (Iterator<Offer> iterator = offers.iterator(); iterator.hasNext(); ) { Offer offer = iterator.next(); if (offer.isShareCard()) { iterator.remove(); } } notifyDataSetChanged(); } /** * creates a new ViewHolder using the provided xml layout * * @param viewGroup the parent ViewGroup that this ViewHolder will inflate * specifies the xml layout that the binder should use to create the view * @return the inflated view */ @Override public OfferViewHolder newViewHolder(ViewGroup viewGroup) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.telco_card_layout_view, viewGroup, false); return new OfferViewHolder(view); } /** * @param offerViewHolder the ViewHolder to use onBind * has all the associated views in this layout * @param position the position of this ViewHolder in the binder */ @Override public void bindViewHolder(OfferViewHolder offerViewHolder, int position) { Offer offer = offers.get(position); offerViewHolder.titleTextView.setText(offer.getTitle()); offerViewHolder.bodyTextView.setText(offer.getBody()); offerViewHolder.dismissButton.setText(context.getString(R.string.card_dismiss)); offerViewHolder.iconImageView.setImageResource(offer.getCardIcon()); String actionText = context.getString(R.string.card_accept); int acceptColor = context.getResources().getColor(R.color.light_indigo); int dismissColor = context.getResources().getColor(R.color.gray_ae); int bottomPortionColor = context.getResources().getColor(R.color.white); String costText = offer.getLocalizedCost(); RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) offerViewHolder.actionButton.getLayoutParams(); params.addRule(RelativeLayout.CENTER_HORIZONTAL, 0); params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); if (offer.isShareCard()) { actionText = context.getString(R.string.card_share); acceptColor = context.getResources().getColor(R.color.white); dismissColor = context.getResources().getColor(R.color.transparent_white); bottomPortionColor = context.getResources().getColor(R.color.light_indigo); costText = ""; } offerViewHolder.actionButton.setText(actionText); offerViewHolder.actionButton.setTextColor(acceptColor); offerViewHolder.dismissButton.setTextColor(dismissColor); offerViewHolder.cardBottomPortion.setBackgroundColor(bottomPortionColor); offerViewHolder.costTextView.setText(costText); } @Override public int getItemCount() { return offers.size(); } /** * Add all offers for MyPlan to the binder dataset * @param offers to add */ public void addAll(List<Offer> offers) { this.offers = offers; notifyBinderDataSetChanged(); } /** * @param offer to add to the offers list */ public void addNewOffer(Offer offer) { offers.add(offer); notifyBinderDataSetChanged(); } /** * @param offer to remove from offers list */ public void removeOffer(Offer offer) { offers.remove(offer); notifyDataSetChanged(); } /** * Starts a the TermsConditionsActivity and passes along data from the passed offer * @param selectedOffer The selected offer * @param offerContext The context of the selected offer */ private void goToInfo(Offer selectedOffer, Context offerContext) { String offerTitle = selectedOffer.getTitle(); String termsCond = selectedOffer.getTermsConditions(); Intent intent = new Intent(offerContext, TermsConditionsActivity.class); intent.putExtra(TermsConditionsActivity.OFFER_TITLE, offerTitle); intent.putExtra(TermsConditionsActivity.OFFER_TERMS_CONDITIONS, termsCond); offerContext.startActivity(intent); } /** * Define the OfferViewHolder * inject views that need to be updated in the ViewHolder onBind */ public class OfferViewHolder extends RecyclerView.ViewHolder { @Bind(R.id.offer_title)RobotoTextView titleTextView; @Bind(R.id.offer_body)RobotoTextView bodyTextView; @Bind(R.id.card_icon)ImageView iconImageView; @Bind(R.id.costTextView)RobotoTextView costTextView; @Bind(R.id.accept)RobotoTextView actionButton; @Bind(R.id.dismiss_button)RobotoTextView dismissButton; @Bind(R.id.card_bottom_portion)RelativeLayout cardBottomPortion; public OfferViewHolder(View view) { super(view); ButterKnife.bind(this, view); } /** * Accept tapped offer and add it to corresponding list * * @param view clicked */ @OnClick(R.id.accept) public void onAcceptClick(View view) { int position = getAdapterPosition(); cardPosition = getCardPosition(position); offerToAccept = offers.get(cardPosition); if (offerToAccept.isShareCard()) { ((MainActivity) context).tryToTweet(offerToAccept.getBody()); removeFromMyPlanUI(cardPosition); } else { acceptOffer(); transformToShareCard(); } } /** * Dismiss tapped offer and remove from offers list * * @param view clicked */ @OnClick(R.id.dismiss_button) public void onDismissClick(View view) { //prevent multiple fast clicks if (SystemClock.elapsedRealtime() - mLastClickTime < 500){ return; } mLastClickTime = SystemClock.elapsedRealtime(); dismissOffer(getAdapterPosition()); if (snackbar != null) { snackbar.dismiss(); } } /** * When the info icon button is clicked on a card, need to get the offer * that was selected and call the function goToInfo() * @param view The view that was clicked */ @OnClick(R.id.info_icon_button)public void onInfoClick(View view){ Offer selectedOffer; int position = getAdapterPosition(); int cardPosition = getCardPosition(position); selectedOffer = offers.get(cardPosition); goToInfo(selectedOffer, view.getContext()); } } /** * Get position of the offer * @param position position of recyclerView that was accessed * @return position of the offer with respect to the * offers list not the recyclerView */ private int getCardPosition(int position) { int BINDER_OFFSET = 3; return position - BINDER_OFFSET; } /** * Change UI of an accepted card to a Twitter shared card * update views accordinly */ private void transformToShareCard() { Offer copyPlaceholderOffer = new Offer(offerToAccept); copyPlaceholderOffer.setShareCard(); copyPlaceholderOffer.setCardIcon(R.drawable.twitter); copyPlaceholderOffer.setTitle(context.getString(R.string.card_tell_friends)); copyPlaceholderOffer.setBody(context.getString(R.string.card_share_body)); copyPlaceholderOffer.setCostZero(); offers.set(cardPosition, copyPlaceholderOffer); notifyDataSetChanged(); } /** * Accept selected offer * update basePlan * provide undo option */ private void acceptOffer() { if (offerToAccept.doesAffectBaseCost()) { basePlanModel.updateBaseCost(offerToAccept.getCost()); } else { basePlanModel.updateAddonCost(offerToAccept.getCost()); } createSnackBar(offerToAccept); offerAcceptStream.onNext(offerToAccept); } /** * Dismiss selected offer * remove from offer list and view * * @param position of the offer to be dismissed */ private void dismissOffer(int position) { int cardPosition = getCardPosition(position); Offer offerToDismiss = offers.get(cardPosition); removeFromMyPlanUI(cardPosition); offerDismissStream.onNext(offerToDismiss); } /** * Remove offer from UI * @param position to remove */ private void removeFromMyPlanUI(int position) { offers.remove(position); notifyBinderItemRemoved(position); notifyBinderItemRangeChanged(position, 1); } /** * create a SnackBar after changing base plan * the user gets ability to undo the plan change * * @param acceptedOffer the offer added/removed after plan change * used to undo the plan change */ private void createSnackBar(final Offer acceptedOffer) { View.OnClickListener undoAction; //on click listener for undo of offer acceptance undoAction = new View.OnClickListener() { @Override public void onClick(View view) { offers.set(cardPosition, offerToAccept); undoAcceptStream.onNext(acceptedOffer); notifyDataSetChanged(); basePlanModel.updateCost(-acceptedOffer.getCost(), acceptedOffer.doesAffectBaseCost()); } }; //build the SnackBar and display it snackbar = Snackbar.make(snackbar_layout, resources.getString(R.string.baseplan_snackbar), Snackbar.LENGTH_LONG) .setAction(resources.getString(R.string.undo), undoAction) .setActionTextColor(resources.getColor(R.color.light_indigo)); snackbar.show(); } /** * Get the Publish Subject stream that observes when an accepted offer is accepted * * @return observable for the offer accepted update */ public Observable<Offer> getOfferAcceptStream() { return offerAcceptStream; } /** * Get the Publish Subject stream that observes when an accepted offer is dismissed * * @return observable for the offer dismissed update */ public Observable<Offer> getOfferDismissStream() { return offerDismissStream; } /** * Get the Publish Subject stream that observes when the * user clicks "undo" on an accepted offer * * @return observable for the offer undo accept update */ public Observable<Offer> getUndoAcceptStream() { return undoAcceptStream; } }