public class ExcitedRetrofitFactory {

    private static final Object object = new Object();
    private static volatile ExcitedRetrofit retrofit;

    public static ExcitedRetrofit getRetrofit(Context context) {
        synchronized (object) {
            if (retrofit == null) {
                retrofit = new ExcitedRetrofit(context);
            }
            return retrofit;
        }
    }
}
 public final class Register {

    public static void registerItem(MultiTypeAdapter adapter, DataService dataService) {
        registerCommon(adapter, dataService);
    }

    public static void registerChannelItem(MultiTypeAdapter adapter, DataService dataService) {
        adapter.register(Header.class, new HeaderViewProvider());
        registerCommon(adapter, dataService);
    }

    private static void registerCommon(MultiTypeAdapter adapter, DataService dataService) {
        adapter.register(Music.class, new MusicViewProvider(dataService));
        adapter.register(Web.class, new WebViewProvider(dataService));
        adapter.register(Video.class, new VideoViewProvider(dataService));
        adapter.register(Picture.class, new PictureViewProvider(dataService));
        adapter.register(Footer.class, new FooterViewProvider());
        adapter.register(EmptyChannel.class, new ChannelEmptyViewProvider());
        adapter.register(SendingCard.class, new SendingCardHolder());
    }
}
 public interface QueryAPI {

    @POST("graphql")
    Observable<NewData> getData(@Query("query") String query);

    @POST("graphql")
    Observable<Response<NewData>> loadData(@Query("query") String query);

    @POST("graphql")
    Observable<NewData> mutation(@Query("query") String query);

} public class DataObserver implements IData.IItems {

    private IItems iItems;

    public DataObserver(IItems iItems) {
        this.iItems = iItems;
    }

    @Override
    public void addData(MultiTypeAdapter adapter, List<Card> cards) {
        Items items = items();
        for (int index = 0; index < cards.size(); index++) {
            final Card card = cards.get(index);
            if (card.type.equals(MUSIC)) {
                items.add(new Music(card, index, adapter, cards, showPlus()));
            } else if (card.type.equals(VIDEO)) {
                items.add(new Video(card, index, adapter, cards, showPlus()));
            } else if (card.type.equals(WEB)) {
                items.add(new Web(card, index, adapter, cards, showPlus()));
            } else if (card.type.equals(COVER_WEB)) {
                items.add(new Web(card, index, adapter, cards, showPlus()));
            } else if (card.type.equals(PICTURE)) {
                items.add(new Picture(card, index, adapter, cards, showPlus()));
            }
        }
        if (cards.size() > 0) {
            items.add(new Footer());
        } else {
            items.add(new EmptyChannel());
        }
        adapter.notifyDataSetChanged();
    }

    @Override
    public Items items() {
        return iItems.items();
    }

    @Override
    public boolean showPlus() {
        return iItems.showPlus();
    }

} public interface IData {

    void addData(MultiTypeAdapter adapter, List<Card> cards);

    interface IItems extends IData {

        Items items();

        /**
         * 是否显示点赞
         * @return true 为显示， false 为不显示
         */
        boolean showPlus();

    }

} public interface Callback {

    void callback(Card card, String tag);

}
 public class Card implements Parcelable {

    public String id;
    public String title;
    public String cover;
    public String ratio;
    @SerializedName("head_icon")
    public String headIcon;
    @SerializedName("head_name")
    public String headName;
    public String source;
    public int remains;
    public int sum;
    public int timestamp;
    public boolean refined;
    public String type;
    public String description;
    public String distance;
    public String uuid;
    public String link;
    @SerializedName("author_name")
    public String authorName;

    public Card() {
    }

    protected Card(Parcel in) {
        id = in.readString();
        title = in.readString();
        cover = in.readString();
        ratio = in.readString();
        headIcon = in.readString();
        headName = in.readString();
        source = in.readString();
        remains = in.readInt();
        sum = in.readInt();
        timestamp = in.readInt();
        type = in.readString();
        description = in.readString();
        distance = in.readString();
        uuid = in.readString();
        link = in.readString();
        authorName = in.readString();
    }

    public static final Creator<Card> CREATOR = new Creator<Card>() {
        @Override
        public Card createFromParcel(Parcel in) {
            return new Card(in);
        }

        @Override
        public Card[] newArray(int size) {
            return new Card[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(id);
        parcel.writeString(title);
        parcel.writeString(cover);
        parcel.writeString(ratio);
        parcel.writeString(headIcon);
        parcel.writeString(headName);
        parcel.writeString(source);
        parcel.writeInt(remains);
        parcel.writeInt(sum);
        parcel.writeInt(timestamp);
        parcel.writeString(type);
        parcel.writeString(description);
        parcel.writeString(distance);
        parcel.writeString(uuid);
        parcel.writeString(link);
        parcel.writeString(authorName);
    }
} public class MusicViewProvider extends ViewProvider<Music, MusicViewProvider.Holder> {

    private boolean isPlus = false;

    public MusicViewProvider(@NonNull DataService dataService) {
        super(dataService);
    }

    @NonNull @Override
    protected Holder onCreateViewHolder(@NonNull LayoutInflater inflater,
                                        @NonNull ViewGroup parent) {
        View view = inflater.inflate(R.layout.item_music, parent, false);
        return new Holder(view);
    }

    @Override
    protected void onBindViewHolder(@NonNull CardHolder cardHolder, @NonNull Music musicItem) {
        Holder holder = (Holder) cardHolder;
        cards = musicItem.cards;
        Card card = musicItem.card;
        multiTypeAdapter = musicItem.adapter;
        holder.index = musicItem.index;
        holder.card = card;
        Glide.with(holder.cover.getContext())
                .load(card.cover)
                .transform(new CircleTransform(holder.cover.getContext()))
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                .into(holder.cover);
        holder.musicTitle.setText(card.title);
        holder.singer.setText(card.authorName);
        bindCommonData(card, holder, musicItem.showPlus);

        if (!isPlus) {
            holder.springView.setWidth((float) card.remains / (float) card.sum);
        } else {
            holder.springView.startSpring((float) card.remains / (float) card.sum);
            isPlus = false;
        }
    }

    @Override
    public void updateCard(Card card, int index) {
        isPlus = true;
        updateItemCard(card, index);
    }

    class Holder extends CardHolder implements View.OnClickListener {

        LinearLayout musicContent;
        ImageView cover;
        TextView musicTitle;
        TextView singer;
        int index;
        Card card;

        Holder(View itemView) {
            super(itemView);
            cover = (ImageView) itemView.findViewById(R.id.cover);
            musicTitle = (TextView) itemView.findViewById(R.id.music_title);
            singer = (TextView) itemView.findViewById(R.id.singer);
            plus.setOnClickListener(this);
            musicContent = (LinearLayout) itemView.findViewById(R.id.music_content);
            musicContent.setOnClickListener(this);
            more.setOnClickListener(this);

            more.setOnLongClickListener((view) -> more.performClick());
        }

        @Override
        public void onClick(View v)  {
            onActionClick(v, MusicViewProvider.this, card, index, "music");
        }
    }
}
 public class PictureViewProvider extends
        ViewProvider<Picture, PictureViewProvider.Holder> {

    private static final String RATIO_1_1 = "1:1";
    private static final String RATIO_16_9 = "16:9";
    private boolean isPlus = false;


    public PictureViewProvider(@NonNull DataService dataService) {
        super(dataService);
    }

    @NonNull @Override
    protected Holder onCreateViewHolder(@NonNull LayoutInflater inflater,
                                        @NonNull ViewGroup parent) {
        View view = inflater.inflate(R.layout.item_picture, parent, false);
        return new Holder(view);
    }

    @Override
    protected void onBindViewHolder(@NonNull CardHolder cardHolder, @NonNull Picture pictureItem) {
        Holder holder = (Holder) cardHolder;
        cards = pictureItem.cards;
        Card card = pictureItem.card;
        holder.card = card;
        multiTypeAdapter = pictureItem.adapter;
        holder.index = pictureItem.index;

        setImageDesc(holder, card);

        if (card.ratio.equals(RATIO_1_1)) {
            holder.picture.setOriginalSize(1, 1);
        } else if (card.ratio.equals(RATIO_16_9)){
            holder.picture.setOriginalSize(16, 9);
        }

        Glide.with(holder.picture.getContext())
                .load(card.cover)
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                .into(holder.picture);
        bindCommonData(card, holder, pictureItem.showPlus);

        if (!isPlus) {
            holder.springView.setWidth((float) card.remains / (float) card.sum);
        } else {
            holder.springView.startSpring((float) card.remains / (float) card.sum);
            isPlus = false;
        }
    }

    @Override
    public void updateCard(Card card, int index) {
        isPlus = true;
        updateItemCard(card, index);
    }

    private void setImageDesc(Holder holder, Card card) {
        if (!TextUtils.isEmpty(card.description)) {
            holder.pictureDesc.setVisibility(View.VISIBLE);
            holder.pictureDesc.setText(card.description);
        } else {
            holder.pictureDesc.setVisibility(View.GONE);
        }
    }

    class Holder extends CardHolder implements View.OnClickListener {

        RatioImageView picture;
        TextView pictureDesc;
        int index;
        Card card;

        Holder(View itemView) {
            super(itemView);
            picture = (RatioImageView) itemView.findViewById(R.id.picture);
            pictureDesc = (TextView) itemView.findViewById(R.id.picture_desc);
            plus.setOnClickListener(this);
            picture.setOnClickListener(this);
            more.setOnClickListener(this);

            more.setOnLongClickListener((view) -> more.performClick());
        }

        @Override
        public void onClick(View v) {
            onActionClick(v, PictureViewProvider.this, card, index, "image");
        }
    }
}
 public class VideoViewProvider extends ViewProvider<Video, VideoViewProvider.Holder> {

    private boolean isPlus = false;

    public VideoViewProvider(@NonNull DataService dataService) {
        super(dataService);
    }

    @NonNull
    @Override
    protected Holder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
        View view = inflater.inflate(R.layout.item_video, parent, false);
        return new Holder(view);
    }

    @Override
    protected void onBindViewHolder(@NonNull CardHolder cardHolder, @NonNull Video videoItem) {
        Holder holder = (Holder) cardHolder;
        multiTypeAdapter = videoItem.adapter;
        cards = videoItem.cards;
        Card card = videoItem.card;
        holder.card = card;
        holder.index = videoItem.index;

        holder.videoCover.setOriginalSize(16, 9);
        Glide.with(holder.videoCover.getContext())
                .load(card.cover)
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                .into(holder.videoCover);
        holder.videoTitle.setText(card.title);
        bindCommonData(card, holder, videoItem.showPlus);

        if (!isPlus) {
            holder.springView.setWidth((float) card.remains / (float) card.sum);
        } else {
            holder.springView.startSpring((float) card.remains / (float) card.sum);
            isPlus = false;
        }
    }

    @Override
    public void updateCard(Card card, int index) {
        isPlus = true;
        updateItemCard(card, index);
    }

    class Holder extends CardHolder implements View.OnClickListener {

        FrameLayout movieContent;
        RatioImageView videoCover;
        ImageButton play;
        TextView videoTitle;
        int index;
        Card card;

        Holder(View itemView) {
            super(itemView);
            videoCover = (RatioImageView) itemView.findViewById(R.id.video_cover);
            play = (ImageButton) itemView.findViewById(R.id.play);
            videoTitle = (TextView) itemView.findViewById(R.id.video_title);
            movieContent = (FrameLayout) itemView.findViewById(R.id.movie_content);
            movieContent.setOnClickListener(this);
            plus.setOnClickListener(this);
            more.setOnClickListener(this);

            more.setOnLongClickListener((view) -> more.performClick());
        }

        @Override
        public void onClick(View v) {
            onActionClick(v, VideoViewProvider.this, card, index, "video");
        }
    }
}
 public class WebViewProvider extends ViewProvider<Web, WebViewProvider.Holder> {

    private boolean isPlus = false;

    public WebViewProvider(@NonNull DataService dataService) {
        super(dataService);
    }

    @NonNull
    @Override
    protected Holder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
        View view = inflater.inflate(R.layout.item_web, parent, false);
        return new Holder(view);
    }

    @Override
    protected void onBindViewHolder(@NonNull CardHolder cardHolder, @NonNull Web webItem) {
        Holder holder = (Holder) cardHolder;
        cards = webItem.cards;
        Card card = webItem.card;
        holder.card = card;
        multiTypeAdapter = webItem.adapter;
        holder.index = webItem.index;

        showTitleAndDesc(holder, card);
        bindCommonData(card, holder, webItem.showPlus);

        if (!isPlus) {
            holder.springView.setWidth((float) card.remains / (float) card.sum);
        } else {
            holder.springView.startSpring(
                    (float) card.remains / (float) card.sum);
            isPlus = false;
        }

        holder.webCover.setOriginalSize(16, 9);
        if (card.cover != null) {
            if (TextUtils.isEmpty(card.cover)) {
                holder.webCover.setVisibility(View.GONE);
            } else {
                holder.webCover.setVisibility(View.VISIBLE);
                Glide.with(holder.webCover.getContext())
                        .load(card.cover)
                        .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                        .into(holder.webCover);
            }
        } else {
            holder.webCover.setVisibility(View.GONE);
        }
    }

    @Override
    public void updateCard(Card card, int index) {
        isPlus = true;
        updateItemCard(card, index);
    }

    private void showTitleAndDesc(Holder holder, Card card) {
        if (card == null)
            return;

        if (TextUtils.isEmpty(card.title)) {
            holder.webTitle.setVisibility(View.GONE);
        } else {
            holder.webTitle.setVisibility(View.VISIBLE);
            holder.webTitle.setText(card.title);
        }

        if (TextUtils.isEmpty(card.description)) {
            holder.webDesc.setVisibility(View.GONE);
        } else {
            holder.webDesc.setVisibility(View.VISIBLE);
            holder.webDesc.setText(card.description);
        }
    }

    class Holder extends CardHolder implements View.OnClickListener {

        LinearLayout webContent;
        RatioImageView webCover;
        TextView webTitle;
        TextView webDesc;
        int index;
        Card card;

        public Holder(View itemView) {
            super(itemView);
            webCover = (RatioImageView) itemView.findViewById(R.id.web_cover);
            webTitle = (TextView) itemView.findViewById(R.id.web_title);
            webDesc = (TextView) itemView.findViewById(R.id.web_desc);
            webContent = (LinearLayout) itemView.findViewById(R.id.web_content);
            plus.setOnClickListener(this);
            webContent.setOnClickListener(this);
            more.setOnClickListener(this);

            more.setOnLongClickListener((view) -> more.performClick());
        }

        @Override
        public void onClick(View v) {
            onActionClick(v, WebViewProvider.this, card, index, "web");
        }
    }
}
 public class DataService {

    private Context context;

    public DataService(@NonNull Context context) {
        this.context = context;
    }

    public void inflateMenu(View actionView, String itemId) {
        PopupMenu actionMenu = new PopupMenu(context, actionView, Gravity.END);
        actionMenu.inflate(R.menu.menu_report);
        actionMenu.setOnMenuItemClickListener(item -> {
            if (item.getItemId() == R.id.report) {
                report(itemId);
            }
            return true;
        });
        actionMenu.show();
    }

    private void report(String itemId) {
        cardApi().report(itemId)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(response -> {
                    if (response.code() >= 200 && response.code() < 300) {
                        ToastUtils.longs(context, R.string.feedback_text);
                    }
                }, throwable -> ToastUtils.shorts(context, throwable.getMessage()));
    }

    //起飞
    public void fly(Card card, String tag) {
        if (tag.contains(PICTURE)) {
            flyToImageActivity(card);
        } else if (tag.contains(WEB)) {
            flyToBrowserActivity(card);
        }
    }

    private void flyToImageActivity(Card card) {
        final Intent pictureIntent = new Intent(context, ImageActivity.class);
        pictureIntent.setData(Uri.parse(card.cover));
        pictureIntent.putExtra("uuid", card.uuid);
        context.startActivity(pictureIntent);
    }

    private void flyToBrowserActivity(Card card) {
        final Intent browserIntent = new Intent(context, BrowserActivity.class);
        browserIntent.setData(Uri.parse(card.source));
        if (card.refined)
            browserIntent.putExtra(BrowserActivity.REFINED_URL, card.link);
        browserIntent.putExtra(BrowserActivity.TITLE, card.title);
        context.startActivity(browserIntent);
    }

    public void vote(ViewProvider provider, String itemId, final int index) {
        queryAPI().mutation(QueryField.vote(itemId))
                .filter(newData -> newData != null)
                .map(newData -> newData.voteCard.card)
                .compose(RxMainThread.mainThread())
                .subscribe(card -> updateCardItem(provider, card, index),
                        throwable -> {
                            if (throwable instanceof HttpException) {
                                HttpException exception = (HttpException) throwable;
                                if (exception.code() == 400) {// 不可以进行重复续命,不对用户做提示}
                                } else {
                                    ToastUtils.shorts(context, throwable.getMessage());
                                }
                                provider.updateCard(null, index);
                            }
                        });
    }

    private void updateCardItem(ViewProvider viewProvider, Card card, int index) {
        viewProvider.updateCard(card, index);
    }

    private CardApi cardApi() {
        return ExcitedRetrofitFactory.getRetrofit(context).createApi(CardApi.class);
    }

    private QueryAPI queryAPI() {
        return ExcitedRetrofitFactory.getRetrofit(context).createApi(QueryAPI.class);
    }

}
 public class Music extends CardItem {

    public Music(@NonNull Card card, int index, @NonNull MultiTypeAdapter adapter,
                 @NonNull List<Card> cards, boolean showPlus) {
        super(card, index, adapter, cards, showPlus);
    }

}
 public class Picture extends CardItem {

    public Picture(@NonNull Card card, int index, @NonNull MultiTypeAdapter adapter,
                   @NonNull List<Card> cards, boolean showPlus) {
        super(card, index, adapter, cards, showPlus);
    }

}
 public class Video extends CardItem {

    public Video(@NonNull Card card, int index, @NonNull MultiTypeAdapter adapter,
                 @NonNull List<Card> cards, boolean showPlus) {
        super(card, index, adapter, cards, showPlus);
    }

}
 public class Web extends CardItem {

    public Web(@NonNull Card card, int index, @NonNull MultiTypeAdapter adapter,
               @NonNull List<Card> cards, boolean showPlus) {
        super(card, index, adapter, cards, showPlus);
    }

}
 public interface OnItemClickListener {

    void onItemClick(Card card, String tag);

}
 public class RxMainThread {

    public static <T> Observable.Transformer<T, T> mainThread() {
        return tObservable -> tObservable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

} public class NoItemAnimator extends SimpleItemAnimator{

    private static final boolean DEBUG = false;

    private ArrayList<RecyclerView.ViewHolder> mPendingRemovals = new ArrayList<>();
    private ArrayList<RecyclerView.ViewHolder> mPendingAdditions = new ArrayList<>();
    private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();

    private ArrayList<ArrayList<RecyclerView.ViewHolder>> mAdditionsList = new ArrayList<>();
    private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
    private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();

    private ArrayList<RecyclerView.ViewHolder> mAddAnimations = new ArrayList<>();
    private ArrayList<RecyclerView.ViewHolder> mMoveAnimations = new ArrayList<>();
    private ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<>();
    private ArrayList<RecyclerView.ViewHolder> mChangeAnimations = new ArrayList<>();

    private static class MoveInfo {
        public RecyclerView.ViewHolder holder;
        public int fromX, fromY, toX, toY;

        private MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
            this.holder = holder;
            this.fromX = fromX;
            this.fromY = fromY;
            this.toX = toX;
            this.toY = toY;
        }
    }

    private static class ChangeInfo {
        public RecyclerView.ViewHolder oldHolder, newHolder;
        public int fromX, fromY, toX, toY;
        private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {
            this.oldHolder = oldHolder;
            this.newHolder = newHolder;
        }

        private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
                           int fromX, int fromY, int toX, int toY) {
            this(oldHolder, newHolder);
            this.fromX = fromX;
            this.fromY = fromY;
            this.toX = toX;
            this.toY = toY;
        }

        @Override
        public String toString() {
            return "ChangeInfo{" +
                    "oldHolder=" + oldHolder +
                    ", newHolder=" + newHolder +
                    ", fromX=" + fromX +
                    ", fromY=" + fromY +
                    ", toX=" + toX +
                    ", toY=" + toY +
                    '}';
        }
    }

    @Override
    public void runPendingAnimations() {
        boolean removalsPending = !mPendingRemovals.isEmpty();
        boolean movesPending = !mPendingMoves.isEmpty();
        boolean changesPending = !mPendingChanges.isEmpty();
        boolean additionsPending = !mPendingAdditions.isEmpty();
        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
            // nothing to animate
            return;
        }
        // First, remove stuff
        for (RecyclerView.ViewHolder holder : mPendingRemovals) {
            animateRemoveImpl(holder);
        }
        mPendingRemovals.clear();
        // Next, move stuff
        if (movesPending) {
            final ArrayList<MoveInfo> moves = new ArrayList<>();
            moves.addAll(mPendingMoves);
            mMovesList.add(moves);
            mPendingMoves.clear();
            Runnable mover = new Runnable() {
                @Override
                public void run() {
                    for (MoveInfo moveInfo : moves) {
                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
                                moveInfo.toX, moveInfo.toY);
                    }
                    moves.clear();
                    mMovesList.remove(moves);
                }
            };
            if (removalsPending) {
                View view = moves.get(0).holder.itemView;
                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
            } else {
                mover.run();
            }
        }
        // Next, change stuff, to run in parallel with move animations
        if (changesPending) {
            final ArrayList<ChangeInfo> changes = new ArrayList<>();
            changes.addAll(mPendingChanges);
            mChangesList.add(changes);
            mPendingChanges.clear();
            Runnable changer = new Runnable() {
                @Override
                public void run() {
                    for (ChangeInfo change : changes) {
                        animateChangeImpl(change);
                    }
                    changes.clear();
                    mChangesList.remove(changes);
                }
            };
            if (removalsPending) {
                RecyclerView.ViewHolder holder = changes.get(0).oldHolder;
                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
            } else {
                changer.run();
            }
        }
        // Next, add stuff
        if (additionsPending) {
            final ArrayList<RecyclerView.ViewHolder> additions = new ArrayList<>();
            additions.addAll(mPendingAdditions);
            mAdditionsList.add(additions);
            mPendingAdditions.clear();
            Runnable adder = new Runnable() {
                @Override
                public void run() {
                    for (RecyclerView.ViewHolder holder : additions) {
                        animateAddImpl(holder);
                    }
                    additions.clear();
                    mAdditionsList.remove(additions);
                }
            };
            if (removalsPending || movesPending || changesPending) {
                long removeDuration = removalsPending ? getRemoveDuration() : 0;
                long moveDuration = movesPending ? getMoveDuration() : 0;
                long changeDuration = changesPending ? getChangeDuration() : 0;
                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
                View view = additions.get(0).itemView;
                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
            } else {
                adder.run();
            }
        }
    }

    @Override
    public boolean animateRemove(final RecyclerView.ViewHolder holder) {
        resetAnimation(holder);
        mPendingRemovals.add(holder);
        return true;
    }

    private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
        final View view = holder.itemView;
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        mRemoveAnimations.add(holder);
        animation.setDuration(getRemoveDuration())
                .alpha(0).setListener(new VpaListenerAdapter() {
            @Override
            public void onAnimationStart(View view) {
                dispatchRemoveStarting(holder);
            }

            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);
                ViewCompat.setAlpha(view, 1);
                dispatchRemoveFinished(holder);
                mRemoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }

    @Override
    public boolean animateAdd(final RecyclerView.ViewHolder holder) {
        resetAnimation(holder);
        ViewCompat.setAlpha(holder.itemView, 0);
        mPendingAdditions.add(holder);
        return true;
    }

    private void animateAddImpl(final RecyclerView.ViewHolder holder) {
        final View view = holder.itemView;
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        mAddAnimations.add(holder);
        animation.alpha(1).setDuration(getAddDuration()).
                setListener(new VpaListenerAdapter() {
                    @Override
                    public void onAnimationStart(View view) {
                        dispatchAddStarting(holder);
                    }
                    @Override
                    public void onAnimationCancel(View view) {
                        ViewCompat.setAlpha(view, 1);
                    }

                    @Override
                    public void onAnimationEnd(View view) {
                        animation.setListener(null);
                        dispatchAddFinished(holder);
                        mAddAnimations.remove(holder);
                        dispatchFinishedWhenDone();
                    }
                }).start();
    }

    @Override
    public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
                               int toX, int toY) {
        final View view = holder.itemView;
        fromX += ViewCompat.getTranslationX(holder.itemView);
        fromY += ViewCompat.getTranslationY(holder.itemView);
        resetAnimation(holder);
        int deltaX = toX - fromX;
        int deltaY = toY - fromY;
        if (deltaX == 0 && deltaY == 0) {
            dispatchMoveFinished(holder);
            return false;
        }
        if (deltaX != 0) {
            ViewCompat.setTranslationX(view, -deltaX);
        }
        if (deltaY != 0) {
            ViewCompat.setTranslationY(view, -deltaY);
        }
        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
        return true;
    }

    private void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
        final View view = holder.itemView;
        final int deltaX = toX - fromX;
        final int deltaY = toY - fromY;
        if (deltaX != 0) {
            ViewCompat.animate(view).translationX(0);
        }
        if (deltaY != 0) {
            ViewCompat.animate(view).translationY(0);
        }
        // TODO: make EndActions end listeners instead, since end actions aren't called when
        // vpas are canceled (and can't end them. why?)
        // need listener functionality in VPACompat for this. Ick.
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        mMoveAnimations.add(holder);
        animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
            @Override
            public void onAnimationStart(View view) {
                dispatchMoveStarting(holder);
            }
            @Override
            public void onAnimationCancel(View view) {
                if (deltaX != 0) {
                    ViewCompat.setTranslationX(view, 0);
                }
                if (deltaY != 0) {
                    ViewCompat.setTranslationY(view, 0);
                }
            }
            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);
                dispatchMoveFinished(holder);
                mMoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }

    @Override
    public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
                                 int fromX, int fromY, int toX, int toY) {
        if (oldHolder == newHolder) {
            // Don't know how to run change animations when the same view holder is re-used.
            // run a move animation to handle position changes.
            return animateMove(oldHolder, fromX, fromY, toX, toY);
        }
        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
        resetAnimation(oldHolder);
        int deltaX = (int) (toX - fromX - prevTranslationX);
        int deltaY = (int) (toY - fromY - prevTranslationY);
        // recover prev translation state after ending animation
        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
        if (newHolder != null) {
            // carry over translation values
            resetAnimation(newHolder);
            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
            ViewCompat.setAlpha(newHolder.itemView, 0);
        }
        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
        return true;
    }

    private void animateChangeImpl(final ChangeInfo changeInfo) {
        final RecyclerView.ViewHolder holder = changeInfo.oldHolder;
        final View view = holder == null ? null : holder.itemView;
        final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;
        final View newView = newHolder != null ? newHolder.itemView : null;
        if (view != null) {
            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
                    getChangeDuration());
            mChangeAnimations.add(changeInfo.oldHolder);
            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
            oldViewAnim.setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchChangeStarting(changeInfo.oldHolder, true);
                }

                @Override
                public void onAnimationEnd(View view) {
                    oldViewAnim.setListener(null);
                    ViewCompat.setAlpha(view, 1);
                    ViewCompat.setTranslationX(view, 0);
                    ViewCompat.setTranslationY(view, 0);
                    dispatchChangeFinished(changeInfo.oldHolder, true);
                    mChangeAnimations.remove(changeInfo.oldHolder);
                    dispatchFinishedWhenDone();
                }
            }).start();
        }
        if (newView != null) {
            final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
            mChangeAnimations.add(changeInfo.newHolder);
            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
                    .setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchChangeStarting(changeInfo.newHolder, false);
                }
                @Override
                public void onAnimationEnd(View view) {
                    newViewAnimation.setListener(null);
                    ViewCompat.setAlpha(newView, 1);
                    ViewCompat.setTranslationX(newView, 0);
                    ViewCompat.setTranslationY(newView, 0);
                    dispatchChangeFinished(changeInfo.newHolder, false);
                    mChangeAnimations.remove(changeInfo.newHolder);
                    dispatchFinishedWhenDone();
                }
            }).start();
        }
    }

    private void endChangeAnimation(List<ChangeInfo> infoList, RecyclerView.ViewHolder item) {
        for (int i = infoList.size() - 1; i >= 0; i--) {
            ChangeInfo changeInfo = infoList.get(i);
            if (endChangeAnimationIfNecessary(changeInfo, item)) {
                if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
                    infoList.remove(changeInfo);
                }
            }
        }
    }

    private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
        if (changeInfo.oldHolder != null) {
            endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
        }
        if (changeInfo.newHolder != null) {
            endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
        }
    }
    private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) {
        boolean oldItem = false;
        if (changeInfo.newHolder == item) {
            changeInfo.newHolder = null;
        } else if (changeInfo.oldHolder == item) {
            changeInfo.oldHolder = null;
            oldItem = true;
        } else {
            return false;
        }
        ViewCompat.setAlpha(item.itemView, 1);
        ViewCompat.setTranslationX(item.itemView, 0);
        ViewCompat.setTranslationY(item.itemView, 0);
        dispatchChangeFinished(item, oldItem);
        return true;
    }

    @Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        final View view = item.itemView;
        // this will trigger end callback which should set properties to their target values.
        ViewCompat.animate(view).cancel();
        // TODO if some other animations are chained to end, how do we cancel them as well?
        for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
            MoveInfo moveInfo = mPendingMoves.get(i);
            if (moveInfo.holder == item) {
                ViewCompat.setTranslationY(view, 0);
                ViewCompat.setTranslationX(view, 0);
                dispatchMoveFinished(item);
                mPendingMoves.remove(i);
            }
        }
        endChangeAnimation(mPendingChanges, item);
        if (mPendingRemovals.remove(item)) {
            ViewCompat.setAlpha(view, 1);
            dispatchRemoveFinished(item);
        }
        if (mPendingAdditions.remove(item)) {
            ViewCompat.setAlpha(view, 1);
            dispatchAddFinished(item);
        }

        for (int i = mChangesList.size() - 1; i >= 0; i--) {
            ArrayList<ChangeInfo> changes = mChangesList.get(i);
            endChangeAnimation(changes, item);
            if (changes.isEmpty()) {
                mChangesList.remove(i);
            }
        }
        for (int i = mMovesList.size() - 1; i >= 0; i--) {
            ArrayList<MoveInfo> moves = mMovesList.get(i);
            for (int j = moves.size() - 1; j >= 0; j--) {
                MoveInfo moveInfo = moves.get(j);
                if (moveInfo.holder == item) {
                    ViewCompat.setTranslationY(view, 0);
                    ViewCompat.setTranslationX(view, 0);
                    dispatchMoveFinished(item);
                    moves.remove(j);
                    if (moves.isEmpty()) {
                        mMovesList.remove(i);
                    }
                    break;
                }
            }
        }
        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
            ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
            if (additions.remove(item)) {
                ViewCompat.setAlpha(view, 1);
                dispatchAddFinished(item);
                if (additions.isEmpty()) {
                    mAdditionsList.remove(i);
                }
            }
        }

        // animations should be ended by the cancel above.
        //noinspection PointlessBooleanExpression,ConstantConditions
        if (mRemoveAnimations.remove(item) && DEBUG) {
            throw new IllegalStateException("after animation is cancelled, item should not be in "
                    + "mRemoveAnimations list");
        }

        //noinspection PointlessBooleanExpression,ConstantConditions
        if (mAddAnimations.remove(item) && DEBUG) {
            throw new IllegalStateException("after animation is cancelled, item should not be in "
                    + "mAddAnimations list");
        }

        //noinspection PointlessBooleanExpression,ConstantConditions
        if (mChangeAnimations.remove(item) && DEBUG) {
            throw new IllegalStateException("after animation is cancelled, item should not be in "
                    + "mChangeAnimations list");
        }

        //noinspection PointlessBooleanExpression,ConstantConditions
        if (mMoveAnimations.remove(item) && DEBUG) {
            throw new IllegalStateException("after animation is cancelled, item should not be in "
                    + "mMoveAnimations list");
        }
        dispatchFinishedWhenDone();
    }

    private void resetAnimation(RecyclerView.ViewHolder holder) {
        AnimatorCompatHelper.clearInterpolator(holder.itemView);
        endAnimation(holder);
    }

    @Override
    public boolean isRunning() {
        return (!mPendingAdditions.isEmpty() ||
                !mPendingChanges.isEmpty() ||
                !mPendingMoves.isEmpty() ||
                !mPendingRemovals.isEmpty() ||
                !mMoveAnimations.isEmpty() ||
                !mRemoveAnimations.isEmpty() ||
                !mAddAnimations.isEmpty() ||
                !mChangeAnimations.isEmpty() ||
                !mMovesList.isEmpty() ||
                !mAdditionsList.isEmpty() ||
                !mChangesList.isEmpty());
    }

    /**
     * Check the state of currently pending and running animations. If there are none
     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
     * listeners.
     */
    private void dispatchFinishedWhenDone() {
        if (!isRunning()) {
            dispatchAnimationsFinished();
        }
    }

    @Override
    public void endAnimations() {
        int count = mPendingMoves.size();
        for (int i = count - 1; i >= 0; i--) {
            MoveInfo item = mPendingMoves.get(i);
            View view = item.holder.itemView;
            ViewCompat.setTranslationY(view, 0);
            ViewCompat.setTranslationX(view, 0);
            dispatchMoveFinished(item.holder);
            mPendingMoves.remove(i);
        }
        count = mPendingRemovals.size();
        for (int i = count - 1; i >= 0; i--) {
            RecyclerView.ViewHolder item = mPendingRemovals.get(i);
            dispatchRemoveFinished(item);
            mPendingRemovals.remove(i);
        }
        count = mPendingAdditions.size();
        for (int i = count - 1; i >= 0; i--) {
            RecyclerView.ViewHolder item = mPendingAdditions.get(i);
            View view = item.itemView;
            ViewCompat.setAlpha(view, 1);
            dispatchAddFinished(item);
            mPendingAdditions.remove(i);
        }
        count = mPendingChanges.size();
        for (int i = count - 1; i >= 0; i--) {
            endChangeAnimationIfNecessary(mPendingChanges.get(i));
        }
        mPendingChanges.clear();
        if (!isRunning()) {
            return;
        }

        int listCount = mMovesList.size();
        for (int i = listCount - 1; i >= 0; i--) {
            ArrayList<MoveInfo> moves = mMovesList.get(i);
            count = moves.size();
            for (int j = count - 1; j >= 0; j--) {
                MoveInfo moveInfo = moves.get(j);
                RecyclerView.ViewHolder item = moveInfo.holder;
                View view = item.itemView;
                ViewCompat.setTranslationY(view, 0);
                ViewCompat.setTranslationX(view, 0);
                dispatchMoveFinished(moveInfo.holder);
                moves.remove(j);
                if (moves.isEmpty()) {
                    mMovesList.remove(moves);
                }
            }
        }
        listCount = mAdditionsList.size();
        for (int i = listCount - 1; i >= 0; i--) {
            ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
            count = additions.size();
            for (int j = count - 1; j >= 0; j--) {
                RecyclerView.ViewHolder item = additions.get(j);
                View view = item.itemView;
                ViewCompat.setAlpha(view, 1);
                dispatchAddFinished(item);
                additions.remove(j);
                if (additions.isEmpty()) {
                    mAdditionsList.remove(additions);
                }
            }
        }
        listCount = mChangesList.size();
        for (int i = listCount - 1; i >= 0; i--) {
            ArrayList<ChangeInfo> changes = mChangesList.get(i);
            count = changes.size();
            for (int j = count - 1; j >= 0; j--) {
                endChangeAnimationIfNecessary(changes.get(j));
                if (changes.isEmpty()) {
                    mChangesList.remove(changes);
                }
            }
        }

        cancelAll(mRemoveAnimations);
        cancelAll(mMoveAnimations);
        cancelAll(mAddAnimations);
        cancelAll(mChangeAnimations);

        dispatchAnimationsFinished();
    }

    void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {
        for (int i = viewHolders.size() - 1; i >= 0; i--) {
            ViewCompat.animate(viewHolders.get(i).itemView).cancel();
        }
    }


    @Override
    public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
                                             @NonNull List<Object> payloads) {
        return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
    }

    private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
        @Override
        public void onAnimationStart(View view) {}

        @Override
        public void onAnimationEnd(View view) {}

        @Override
        public void onAnimationCancel(View view) {}
    }
}
 public class SnackbarUtils {
    public static void shorts(View container, int resId) {
        shorts(container, resId, null);
    }

    public static void indefinite(View container, int resId) {
        shorts(container, resId, null);
    }

    public static void longs(View container, int resId) {
        shorts(container, resId, null);
    }

    /**
     * To show a SnackBar for a short period of time with a particular padding.
     *
     * @param container  The view to find a parent from.
     * @param resId  The resource id of the string resource to use. Can be formatted text.
     * @param rect The padding(without top) that view need to add, if null then won't add padding.
     */
    public static void shorts(View container, int resId, Rect rect) {
        Snackbar snackbar = Snackbar.make(container, resId, Snackbar.LENGTH_SHORT);
        if (rect != null) {
            snackbar.getView().setPadding(rect.left, 0, rect.right, rect.bottom);
        }
        snackbar.show();
    }

    public static void indefinite(View container, int resId, Rect rect) {
        Snackbar snackbar = Snackbar.make(container, resId, Snackbar.LENGTH_INDEFINITE);
        if (rect != null) {
            snackbar.getView().setPadding(rect.left, 0, rect.right, rect.bottom);
        }
        snackbar.show();
    }

    public static void longs(View container, int resId, Rect rect) {
        Snackbar snackbar = Snackbar.make(container, resId, Snackbar.LENGTH_SHORT);
        if (rect != null) {
            snackbar.getView().setPadding(rect.left, 0, rect.right, rect.bottom);
        }
        snackbar.show();
    }

    public static void shorts(View container, String message, Rect rect) {
        Snackbar snackbar = Snackbar.make(container, message, Snackbar.LENGTH_SHORT);
        if (rect != null) {
            snackbar.getView().setPadding(rect.left, 0, rect.right, rect.bottom);
        }
        snackbar.show();
    }

    public static void indefinite(View container, String message, Rect rect) {
        Snackbar snackbar = Snackbar.make(container, message, Snackbar.LENGTH_INDEFINITE);
        if (rect != null) {
            snackbar.getView().setPadding(rect.left, 0, rect.right, rect.bottom);
        }
        snackbar.show();
    }

    public static void longs(View container, String message, Rect rect) {
        Snackbar snackbar = Snackbar.make(container, message, Snackbar.LENGTH_SHORT);
        if (rect != null) {
            snackbar.getView().setPadding(rect.left, 0, rect.right, rect.bottom);
        }
        snackbar.show();
    }
}
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import me.drakeet.multitype.Items;
import me.drakeet.multitype.MultiTypeAdapter;
import ooo.oxo.excited.ExcitedRetrofitFactory;
import ooo.oxo.excited.R;
import ooo.oxo.excited.Register;
import ooo.oxo.excited.api.QueryAPI;
import ooo.oxo.excited.api.QueryField;
import ooo.oxo.excited.data.DataObserver;
import ooo.oxo.excited.data.IData;
import ooo.oxo.excited.fragment.callback.Callback;
import ooo.oxo.excited.model.Card;
import ooo.oxo.excited.provider.MusicViewProvider;
import ooo.oxo.excited.provider.PictureViewProvider;
import ooo.oxo.excited.provider.VideoViewProvider;
import ooo.oxo.excited.provider.WebViewProvider;
import ooo.oxo.excited.provider.data.DataService;
import ooo.oxo.excited.provider.item.Music;
import ooo.oxo.excited.provider.item.Picture;
import ooo.oxo.excited.provider.item.Video;
import ooo.oxo.excited.provider.item.Web;
import ooo.oxo.excited.provider.listener.OnItemClickListener;
import ooo.oxo.excited.rx.RxMainThread;
import ooo.oxo.excited.utils.NoItemAnimator;
import ooo.oxo.excited.utils.SnackbarUtils;

package ooo.oxo.excited.fragment;





public class ExcitedFragment extends BaseFragment implements OnItemClickListener, IData.IItems {

    public static final String TAG = "Excited";

    private RecyclerView list;
    private SwipeRefreshLayout refreshLayout;
    private View convertView;

    private MultiTypeAdapter adapter;
    private Items items = new Items();
    private IItems iItems;

    private QueryAPI queryAPI;
    private Callback callback;


    public void registerCallback(Callback callback) {
        this.callback = callback;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        iItems = new DataObserver(this);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        if (appBarCallback != null) {
            appBarCallback.setExpanded(false, false);
        }
        if (convertView == null) {
            View view = inflater.inflate(R.layout.excited_fragment, container, false);
            list = (RecyclerView) view.findViewById(R.id.list);
            refreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refreshLayout);
            refreshLayout.setOnRefreshListener(() -> loadData(true));
            convertView = view;
        }
        if (containerCallback != null)
            list.setPadding(0, list.getPaddingTop(),
                    0, containerCallback.getWindowInsetsRect().bottom);
        return convertView;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        queryAPI = ExcitedRetrofitFactory.getRetrofit(context).createApi(QueryAPI.class);

        if (adapter == null) {
            adapter = new MultiTypeAdapter(items);

            Register.registerItem(adapter, new DataService(context));

            list.setItemAnimator(new NoItemAnimator());
