public abstract class AbsDelegate<Data, AVH extends AbsViewHolder> implements DelegateImpl<Data, AVH> {

    private Data data;

    private Bundle bundle;

    private OnViewEventListener<Data, AVH> onViewEventListener;

    /**
     * @param data the source data item
     */
    public AbsDelegate (Data data) {
        this(data, null, null);
    }

    public AbsDelegate (Data data, Bundle bundle) {
        this(data, bundle, null);
    }

    public AbsDelegate (Data data, OnViewEventListener<Data, AVH> viewEventListener) {
        this (data, null, viewEventListener);
    }

    public AbsDelegate(Data data, Bundle bundle, OnViewEventListener<Data, AVH> viewEventListener) {
        this.data = data;
        this.bundle = bundle;
        this.onViewEventListener = viewEventListener;
    }

    public void setOnViewEventListener(OnViewEventListener<Data, AVH> viewEventListener) {
        this.onViewEventListener = viewEventListener;
    }

    @Override
    public Data getSource () {
        return data;
    }

    public void setSource (Data data) {
        this.data = data;
    }

    public Bundle bundle() {
        if (bundle == null) {
            bundle = new Bundle();
        }
        return bundle;
    }

    @Override
    public void actionViewEvent(int eventCode, View view, AVH viewHolder,
                                int position, DelegateAdapter adapter) {
        if (onViewEventListener != null) {
            onViewEventListener.onViewEvent(eventCode, view, getSource(),
                    bundle, viewHolder, position, adapter);
        }
    }

} public class DelegateAdapter extends RecyclerView.Adapter<AbsViewHolder>{

    public static final int ITEM_VIEW_ID = Integer.MIN_VALUE;

    private Context mContext = null;
    private List<LayoutImpl> mDelegateImplList = null;  //DataSource
    private SparseArrayCompat<Class<? extends AbsViewHolder>> mTypeHolderMap = null; // key -- layout, value -- holderClass

    private Bundle mBundle;

    public DelegateAdapter (Context context) {
        this(context, null);
    }

    public DelegateAdapter (Context context, Bundle bundle) {
        this (context, bundle, new ArrayList<LayoutImpl>());
    }

    public DelegateAdapter (Context context, Bundle bundle, List<LayoutImpl> dataLayoutList) {
        mContext = context;
        mDelegateImplList = dataLayoutList;
        mTypeHolderMap = new SparseArrayCompat<Class<? extends AbsViewHolder>>();
        mBundle = bundle;
    }

    public Context getContext () {
        return mContext;
    }

    public Bundle bundle () {
        if (mBundle == null) {
            mBundle = new Bundle();
        }
        return mBundle;
    }

    @Override
    public final AbsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(mContext).inflate(viewType, null);
        return getHolder(viewType, itemView);
    }

    /**
     * make an {@link AbsViewHolder} instance for {@param viewType} with {@param itemView}
     * @param viewType
     * @param itemView
     * @return
     */
    private AbsViewHolder getHolder (int viewType, View itemView) {
        if (mTypeHolderMap.indexOfKey(viewType) >= 0) {
            Class<? extends AbsViewHolder> clz = mTypeHolderMap.get(viewType);
            if (clz != null) {
                try {
                    Constructor<? extends AbsViewHolder> constructor = clz.getConstructor(View.class);
                    return constructor.newInstance(itemView);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        return onHolderClassNotFound(viewType, itemView);
    }

    public AbsViewHolder onHolderClassNotFound (int viewType, View itemView) {
        throw new IllegalStateException("no holder found for viewType(0x" + Integer.toHexString(viewType) + ") at getHolder in " + this.getClass().getName());
    }

    @Override
    public void onBindViewHolder(final AbsViewHolder holder, final int position) {
        final LayoutImpl layoutImpl = mDelegateImplList.get(position);
        holder.onBindView(mContext, layoutImpl, position, this);
    }

    @Override
    public int getItemCount() {
        return mDelegateImplList.size();
    }

    @Override
    public int getItemViewType(int position) {
        LayoutImpl impl = mDelegateImplList.get(position);
        int type = impl.getLayout();
        if (type == 0) {
            type = getLayoutFromAnnotation(impl);
        }
        if (type <= 0) {
            throw new IllegalStateException("layout not be defined by class(" + impl.getClass().getName()
                    + "), please define a layout resource id by getLayout or LayoutID or LayoutInfo");
        }
        /*if (type != 0) {
            if (mTypeHolderMap.indexOfKey(type) > 0 && mTypeHolderMap.get(type) != null) {

            }
        }*/
        Class<? extends AbsViewHolder> holderClass = impl.getHolderClass();
        if (holderClass == null) {
            holderClass = getHolderClassFromAnnotation(impl);
        }
        /*if (holderClass == null) {
            throw new IllegalStateException("holderClass not be defined by class(" + impl.getClass().getName()
                    + "), please define a holderClass by getHolderClass or HolderClass or LayoutInfo");
        }*/
        if (mTypeHolderMap.indexOfKey(type) < 0 && holderClass != null) {
            mTypeHolderMap.put(type, holderClass);
        }
        return type;
    }

    @Override
    public void onViewAttachedToWindow(AbsViewHolder holder) {
        holder.onViewAttachedToWindow(this, mContext);
    }

    @Override
    public void onViewDetachedFromWindow(AbsViewHolder holder) {
        holder.onViewDetachedFromWindow(this, mContext);
    }

    @Override
    public void onViewRecycled(AbsViewHolder holder) {
        holder.onViewRecycled(this, mContext);
    }

    @Override
    public boolean onFailedToRecycleView(AbsViewHolder holder) {
        return holder.onFailedToRecycleView(this, mContext);
    }

    /**
     * return a very powerful query tool instance of {@link Selector}
     * @param tClass the item's class contains in this DelegateAdapter.
     * @param <T>
     * @return A {@link Selector} instance for querying data
     */
    public <T> ListSelector<T> selector (Class<T> tClass) {
        return Selector.selector(tClass, mDelegateImplList);
    }

    public boolean isEmpty () {
        return mDelegateImplList.isEmpty();
    }

    public DataChange clear () {
        int count = getItemCount();
        mDelegateImplList.clear();
        return new DataChange(this, 0, count, DataChange.TYPE_ITEM_RANGE_REMOVED);
    }

    public DataChange set (int position, DelegateImpl impl) {
        mDelegateImplList.set(position, impl);
        return new DataChange(this, position, DataChange.TYPE_ITEM_CHANGED);
    }

    public DataChange add(LayoutImpl impl) {
        int sizeBefore = getItemCount();
        mDelegateImplList.add(impl);
        return new DataChange(this, sizeBefore, DataChange.TYPE_ITEM_INSERTED);
    }

    public <T> DataChange add (T t, DelegateListParser<T> parser) {
        return addAll(parser.parse(this, t));
    }

    public <T> DataChange add (int position, T t, DelegateListParser<T> parser) {
        return addAll(position, parser.parse(this, t));
    }

    public DataChange addIfNotExist (@NonNull LayoutImpl impl) {
        if (contains(impl)) {
            return DataChange.doNothingInstance();
        }
        return add(impl);
    }

    public DataChange addIfNotExist (int position, @NonNull LayoutImpl impl) {
        if (contains(impl)) {
            return DataChange.doNothingInstance();
        }
        return add(position, impl);
    }

    public DataChange add(int position, @NonNull LayoutImpl impl) {
        mDelegateImplList.add(position, impl);
        return new DataChange(this, position, DataChange.TYPE_ITEM_INSERTED);
    }

    public DataChange addAll(@NonNull Collection<? extends LayoutImpl> list) {
        int sizeBefore = getItemCount();
        mDelegateImplList.addAll(list);
        return new DataChange(this, sizeBefore, list.size(), DataChange.TYPE_ITEM_RANGE_INSERTED);
    }

    public DataChange addAll(int position, @NonNull Collection<? extends LayoutImpl> list) {
        mDelegateImplList.addAll(position, list);
        return new DataChange(this, position, list.size(), DataChange.TYPE_ITEM_RANGE_INSERTED);
    }

    public DataChange addAll(LayoutImpl[] array) {
        return addAll(Arrays.asList(array));
        /*mDelegateImplList.addAll();
        return new DataChange(this, 0, array.length, DataChange.TYPE_ITEM_RANGE_INSERTED);*/
    }

    public DataChange addAll(int position, LayoutImpl[] array) {
        mDelegateImplList.addAll(position, Arrays.asList(array));
        return new DataChange(this, position, array.length, DataChange.TYPE_ITEM_RANGE_INSERTED);
    }

    public <T> DataChange addAll (T[] tArray, DelegateParser<T> parser) {
        Collection<T> collection = Arrays.asList(tArray);
        List<LayoutImpl> delegates = generateDelegateImpls(collection, parser);
        if (delegates != null) {
            return addAll(delegates);
        }
        return DataChange.doNothingInstance();
    }

    public <T> DataChange addAll (int position, T[] tArray, DelegateParser<T> parser) {
        Collection<T> collection = Arrays.asList(tArray);
        List<LayoutImpl> delegates = generateDelegateImpls(collection, parser);
        if (delegates != null) {
            return addAll(position, delegates);
        }
        return DataChange.doNothingInstance();
    }

    public <T> DataChange addAll(Collection<T> data, DelegateParser<T> parser) {
        List<LayoutImpl> delegates = generateDelegateImpls(data, parser);
        if (delegates != null) {
            return addAll(delegates);
        }
        return DataChange.doNothingInstance();
    }

    public <T> DataChange addAll(int position, Collection<T> data, DelegateParser<T> parser) {
        List<LayoutImpl> delegates = generateDelegateImpls(data, parser);
        if (delegates != null) {
            return addAll(position, delegates);
        }
        return DataChange.doNothingInstance();
    }

    public <T> DataChange addAll (T[] tArray, DelegateListParser<T> parser) {
        Collection<T> collection = Arrays.asList(tArray);
        List<LayoutImpl> delegates = generateDelegateImpls(collection, parser);
        if (delegates != null) {
            return addAll(delegates);
        }
        return DataChange.doNothingInstance();
    }

    public <T> DataChange addAll (int position, T[] tArray, DelegateListParser<T> parser) {
        Collection<T> collection = Arrays.asList(tArray);
        List<LayoutImpl> delegates = generateDelegateImpls(collection, parser);
        if (delegates != null) {
            return addAll(position, delegates);
        }
        return DataChange.doNothingInstance();
    }

    public <T> DataChange addAll(Collection<T> data, DelegateListParser<T> parser) {
        List<LayoutImpl> delegates = generateDelegateImpls(data, parser);
        if (delegates != null) {
            return addAll(delegates);
        }
        return DataChange.doNothingInstance();
    }

    public <T> DataChange addAll(int position, Collection<T> data, DelegateListParser<T> parser) {
        List<LayoutImpl> delegates = generateDelegateImpls(data, parser);
        if (delegates != null) {
            return addAll(position, delegates);
        }
        return DataChange.doNothingInstance();
    }

    public List<? extends LayoutImpl> getList() {
        return mDelegateImplList;
    }

    public LayoutImpl get (int position) {
        return mDelegateImplList.get(position);
    }

    /**
     * @param indexFrom start index, include this index;
     * @param indexTo end index
     * @return return a sub collection of [indexFrom, indexTo)
     */
    public List<LayoutImpl> getSubList (int indexFrom, int indexTo) {
        return mDelegateImplList.subList(indexFrom, indexTo);
    }

    public <T> List<LayoutImpl> generateDelegateImpls (Collection<T> data, @NonNull DelegateParser<T> parser) {
        if (data == null || data.isEmpty()) {
            return null;
        }
        List<LayoutImpl> delegates = new ArrayList<>();
        for (T obj : data) {
            LayoutImpl delegate = parser.parse(this, obj);
            if (delegate == null) {
                continue;
            }
            delegates.add(delegate);
        }
        return delegates;
    }

    public <T> List<LayoutImpl> generateDelegateImpls (Collection<T> data, @NonNull DelegateListParser<T> parser) {
        if (data == null || data.isEmpty()) {
            return null;
        }
        List<LayoutImpl> delegates = new ArrayList<>();
        for (T obj : data) {
            List<LayoutImpl> ds = parser.parse(this, obj);
            if (ds == null) {
                continue;
            }
            delegates.addAll(ds);
        }
        
        return delegates;
    }

    public int indexOf (LayoutImpl impl) {
        if (impl == null) {
            return -1;
        }
        return mDelegateImplList.indexOf(impl);
    }

    public DataChange remove (int position) {
        mDelegateImplList.remove(position);
        return new DataChange(this, position, 0, DataChange.TYPE_ITEM_REMOVED);
    }

    public DataChange remove (int position, @NonNull DoAfter after) {
        DataChange change = remove(position);
        after.doAfter(this);
        return change;
    }

    public DataChange remove (LayoutImpl impl) {
        int index = mDelegateImplList.indexOf(impl);

        if (index >= 0) {
            mDelegateImplList.remove(impl);
            return new DataChange(this, index, DataChange.TYPE_ITEM_REMOVED);
        }
        return DataChange.doNothingInstance();
    }

    public DataChange remove (LayoutImpl impl, @NonNull DoAfter after) {
        DataChange change = remove(impl);
        after.doAfter(this);
        return change;
    }

    public int actionWith (@NonNull DelegateAction action) {
        int count = 0;
        for (LayoutImpl impl : mDelegateImplList) {
            action.onAction(impl);
            count++;
        }
        return count;
    }

    public boolean contains (LayoutImpl delegate) {
        return mDelegateImplList.contains(delegate);
    }

    public boolean endWith (LayoutImpl delegate) {
        if (mDelegateImplList.isEmpty()) {
            return false;
        }
        return mDelegateImplList.get(getItemCount() - 1).equals(delegate);
    }

    public boolean startWith (LayoutImpl delegate) {
        if (mDelegateImplList.isEmpty()) {
            return false;
        }
        return mDelegateImplList.get(0).equals(delegate);
    }

    public static int getLayoutFromAnnotation (LayoutImpl impl) {
        Class clz = impl.getClass();
        int layoutID = 0;
        Annotation anno = clz.getAnnotation(DelegateInfo.class);
        if (anno != null) {
            Class<? extends Annotation> annoClz = anno.annotationType();
            try {
                Method method = annoClz.getMethod("layoutID");
                layoutID = (int)method.invoke(anno);
                if (layoutID != 0) {
                    return layoutID;
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        layoutID = getLayoutFromAnnotation(clz, impl);
        return layoutID;
    }

    private static int getLayoutFromAnnotation (Class clz, LayoutImpl impl) {
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            LayoutID anno = field.getAnnotation(LayoutID.class);
            if (anno != null) {
                try {
                    return field.getInt(impl);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return 0;
    }

    public static <AVH extends AbsViewHolder> Class<AVH> getHolderClassFromAnnotation (LayoutImpl impl) {
        Class clz = impl.getClass();

        Annotation anno = clz.getAnnotation(DelegateInfo.class);
        Class<AVH> holderClass;
        if (anno != null) {
            Class<? extends Annotation> annoClz = anno.annotationType();
            try {
                Method method = annoClz.getMethod("holderClass");
                holderClass = (Class<AVH>)method.invoke(anno);
                if (holderClass != null && !holderClass.equals(NullHolder.class)) {
                    return holderClass;
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        holderClass = getHolderClassFromAnnotation(clz, impl);
        return holderClass;
    }

    private static Class<? extends AbsViewHolder> getHolderClassFromAnnotation (Class<? extends AnnotationDelegate> clz, LayoutImpl impl) {
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            HolderClass anno = field.getAnnotation(HolderClass.class);
            if (anno != null) {
                try {
                    return (Class<? extends AbsViewHolder>)field.get(impl);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

} public interface DelegateParser<T> {
    LayoutImpl parse (DelegateAdapter adapter, T data);
} public interface OnViewEventListener<Data, AVH> {
    void onViewEvent (int eventCode, View view, Data data, Bundle bundle, AVH viewHolder,
                      int position, DelegateAdapter adapter);
} public class SuperAdapter<Empty extends LayoutImpl, Tail extends LayoutImpl> extends DelegateAdapter {

    private Controller mController;

    private Empty empty;
    private Tail tail;

    public SuperAdapter(Context context) {
        super(context);
    }

    public SuperAdapter(Context context, Bundle bundle) {
        super(context, bundle);
    }

    public SuperAdapter(Context context, Bundle bundle, List<LayoutImpl> dataLayoutList) {
        super(context, bundle, dataLayoutList);
    }

    public void setEmptyItem (Empty empty) {
        this.empty = empty;
        if (onlyContainsTailItem()) {
            super.remove(0).autoNotify();
        }
        if (isEmpty()) {
            super.add(empty).autoNotify();
        }
    }

    public Empty getEmptyItem() {
        return empty;
    }

    public boolean hasEmptyItem () {
        return empty != null;
    }

    public void setTailItem(Tail footer) {
        this.tail = footer;
        if (!hasEmptyItem()) {
            super.add(tail).autoNotify();
        }
    }

    public Tail getTailItem() {
        return tail;
    }

    public boolean hasTailItem() {
        return  tail != null;
    }

    public boolean containsEmptyItem () {
        return hasEmptyItem() && contains(empty);
    }

    private boolean removeEmptyItemIfNeed() {
        if (containsEmptyItem()) {
            super.remove(empty).autoNotify();
            return true;
        }
        return false;
    }

    public boolean onlyContainsTailItem() {
        return containsTailItem() && getItemCount() == 1;
    }

    public boolean onlyContainsEmptyItem () {
        return containsEmptyItem() && getItemCount() == 1;
    }

    private boolean manageEmptyState() {
        if (onlyContainsTailItem()) {
            if (hasEmptyItem()) {
                super.remove(0).autoNotify();
                super.add(empty).autoNotify();
            }
            return true;
        }
        return false;
    }

    public boolean containsTailItem() {
        return hasTailItem() && contains(tail);
    }

    public boolean isEmptyExceptEmptyAndTail() {
        return getItemCountExceptEmptyAndTail() == 0;
    }

    public int getItemCountExceptEmptyAndTail() {
        int count = getItemCount();
        if (containsEmptyItem()) {
            count--;
        }
        if (containsTailItem()) {
            count--;
        }
        return count;
    }

    @Override
    public DataChange add(LayoutImpl impl) {
        removeEmptyItemIfNeed();
        DataChange change;
        if (containsTailItem()) {
            change = super.add(getItemCount() - 1, impl);
        } else {
            super.add(impl);
            int changeCount = 1;
            if (hasTailItem()) {
                super.add(tail);
                changeCount++;
            }
            change = new DataChange(this, getItemCount() - changeCount, changeCount, DataChange.TYPE_ITEM_INSERTED);
        }
        return change;
    }

    @Override
    public <T> DataChange add(int position, T t, DelegateListParser<T> parser) {
        return super.add(position, t, parser);
    }

    @Override
    public DataChange add(int position, @NonNull LayoutImpl impl) {
        removeEmptyItemIfNeed();
        DataChange change;
        if (containsTailItem() && position == getItemCount()) {
            change = super.add(position - 1, impl);
        } else {
            if (position >= getItemCount()) {
                throw new IndexOutOfBoundsException("you can not add elements after the footer element.");
            }
            super.add(position, impl);
            if (hasTailItem()) {
                super.add(tail);
            }
            change = DataChange.notifyDataSetChangeInstance(this);
        }
        return change;
    }

    @Override
    public DataChange addAll(@NonNull Collection<? extends LayoutImpl> list) {
        if (list.isEmpty()) {
            //TODO need tell empty state if need
            if (isEmptyExceptEmptyAndTail()) {
                //TODO need tell empty state if need
                return DataChange.doNothingInstance();
            } else {
                if (!containsTailItem() && hasTailItem()) {
                    return super.add(tail);
                }
                //TODO tell footer empty if need
                //TODO return footer {@link DataChange}
            }
        } else {
            removeEmptyItemIfNeed();
            DataChange change = null;
            if (containsTailItem()) {
                change = super.addAll(getItemCount() - 1, list);
            } else {
                int changeCount = list.size();
                super.addAll(list);
                if (hasTailItem()) {
                    super.add(tail);
                    changeCount++;
                }
                change = new DataChange(this, getItemCount() - changeCount, changeCount, DataChange.TYPE_ITEM_RANGE_INSERTED);
            }
            return change;
        }
        return DataChange.doNothingInstance();
    }

    @Override
    public DataChange addAll(int position, @NonNull Collection<? extends LayoutImpl> list) {
        if (list.isEmpty()) {
            if (isEmptyExceptEmptyAndTail()) {
                return DataChange.doNothingInstance();
            } else {
                if (!containsTailItem() && hasTailItem()) {
                    return super.add(tail);
                } else {
                    return DataChange.doNothingInstance();
                }
            }
        } else {
            removeEmptyItemIfNeed();
            if (containsTailItem()) {
                if (position >= getItemCount()) {
                    throw new IndexOutOfBoundsException("you can not add elements after the footer element.");
                }
                int index = indexOf(tail);
                if (index == position) {
                    return super.addAll(position - 1, list);
                } else {
                    return super.addAll(position, list);
                }
            } else {
                super.addAll(position, list);
                if (hasTailItem()) {
                    super.add(tail);
                }
                return DataChange.notifyDataSetChangeInstance(this);
            }
//            super.addAll()
        }
    }

    @Override
    public DataChange remove(int position) {
        DataChange change = super.remove(position);
        if (manageEmptyState()) {
            return DataChange.notifyDataSetChangeInstance(this);
        }
        return change;
    }

    @Override
    public DataChange remove(LayoutImpl impl) {
        DataChange change = super.remove(impl);
        if (manageEmptyState()) {
            return DataChange.notifyDataSetChangeInstance(this);
        }
        return change;
    }

    @Override
    public DataChange clear() {
        super.clear();
        manageEmptyState();
        return DataChange.notifyDataSetChangeInstance(this);
    }

    public void notifyEmpty () {
        if (containsEmptyItem()) {
            notifyItemChanged(indexOf(empty));
        }
    }

    public void notifyTail () {
        if (containsTailItem()) {
            notifyItemChanged(indexOf(tail));
        }
    }

    @Override
    public long getItemId(int position) {
        if (hasStableIds()) {
            return position;
        }
        return super.getItemId(position);
    }

    public void notifyDataSetChangedSafety() {
        if (isEmptyExceptEmptyAndTail()) {
            if (hasEmptyItem()) {
                super.add(0, empty);
                if (containsTailItem()) {
                    super.remove(tail);
                }
            }

        } else {
            if (hasTailItem()) {
                if (containsTailItem()) {
                    if (!endWith(tail)) {
                        super.remove(tail);
                        super.add(tail);
                    }
                } else {
                    super.add(tail);
                }
            }
        }
        notifyDataSetChanged();
    }

    /**
     * go into under control single selection mode, you can choose items.
     * @return {@link SingleController}
     */
    public SingleController singleControl () {
        if (mController != null && mController instanceof SingleController) {
            return (SingleController)mController;
        }
        SingleController singleController = new SingleController(this);
        mController = singleController;
//        notifyDataSetChanged();
        return singleController;
    }

    /**
     * go into under control multiple selection mode, you can choose items.
     * @return {@link MultipleController}
     */
    public MultipleController multipleControl () {
        if (mController != null && mController instanceof MultipleController) {
            return (MultipleController)mController;
        }
        MultipleController multipleController = new MultipleController(this);
        mController = multipleController;
//        notifyDataSetChanged();
        return multipleController;
    }

    /**
     * check if already under control
     * @return true if already under control.
     */
    public boolean isUnderControl () {
        return mController != null;
    }

    /*private  <C extends Controller> C takeControlWith (C c) {
        if (isUnderControl() && c.getClass().isInstance(mController)) {
            return (C)mController;
        }
        mController = c;
        notifyDataSetChanged();
        return c;
    }*/

    /**
     * release the control mode
     */
    /*public void dismissControl () {
        mController = null;
        selector(Checkable.class).map(new Action<Checkable>() {
            @Override
            public void action(int i, Checkable checkable) {
                checkable.setChecked(false);
            }
        });
        notifyDataSetChanged();
    }*/
} public abstract class OnScrollBottomListener extends RecyclerView.OnScrollListener {

    private static final String TAG = OnScrollBottomListener.class.getSimpleName();

    private int mLastDy = 0;

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        View lastChild = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
        final int positionInAdapter = recyclerView.getChildAdapterPosition(lastChild);
        if (recyclerView.getAdapter() != null
                && positionInAdapter == recyclerView.getAdapter().getItemCount() - 1
                && newState == RecyclerView.SCROLL_STATE_IDLE && mLastDy >= 0) {
            onScrollBottom(recyclerView, newState);
        }
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        mLastDy = dy;
    }

    public abstract void onScrollBottom (RecyclerView recyclerView, int newState);

} public interface LayoutImpl<AVH extends AbsViewHolder> {

    @LayoutRes int getLayout ();
    Class<AVH> getHolderClass ();

} @SuppressWarnings("unchecked")
public class PhotoActivity extends BaseActivity {

    private static final String TAG = PhotoActivity.class.getSimpleName();

    private AppCompatImageView mThumbIv;
    private Toolbar mTb;
    private View mSetFabLayout;
    private FloatingActionButton mShareFab, mSetFab;
    private CircularProgressView mCpv;
    private RecyclerView mRv;

    private DelegateAdapter mAdapter;

    private Photo mPhoto;

    private StreamService mService;
    private org.xutils.common.Callback.Cancelable mCancelable;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.v(TAG, "onServiceConnected ");
            mService = ((StreamService.StreamBinder)iBinder).getService();
            download();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.v(TAG, "onServiceDisconnected ");
        }
    };

    private boolean isDownloading = false;
    private File mFile = null;
    private org.xutils.common.Callback.ProgressCallback<File> mFileCallback =
            new org.xutils.common.Callback.ProgressCallback<File>() {
        @Override
        public void onWaiting() {
            isDownloading = true;
            mSetFab.setImageResource(R.drawable.ic_close);
            showProgress();
        }

        @Override
        public void onStarted() {
            mCpv.startAnimation();
            PhotoManager.getInstance().onStart(mPhoto);
        }

        @Override
        public void onLoading(long total, long current, boolean isDownloading) {
            long progress = current * 100 / total;
            mCpv.setProgress(progress);
            Log.v(TAG, "onLoading " + progress);
        }

        @Override
        public void onSuccess(File result) {
            isDownloading = false;
            mSetFab.setImageResource(R.drawable.ic_format_paint);
            mCpv.stopAnimation();
            mFile = result;
            PhotoManager.getInstance().onFinish(mPhoto);
        }

        @Override
        public void onError(Throwable ex, boolean isOnCallback) {
            isDownloading = false;
            mCpv.stopAnimation();

        }

        @Override
        public void onCancelled(CancelledException cex) {
            isDownloading = false;
            mCpv.stopAnimation();
        }

        @Override
        public void onFinished() {
            isDownloading = false;
            hideProgress();
        }
    };

    private View.OnClickListener mClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.photo_set_fab:
                    onSetFabClick(view);
                    break;
                case R.id.photo_share_fab:
                    Intents.shareText(PhotoActivity.this, "Share to:", mPhoto.links.html);
                    break;
            }
        }
    };

    private OnViewEventListener<User, UserRowHolder> userRowEventListener =
            new OnViewEventListener<User, UserRowHolder>() {
                @Override
                public void onViewEvent(int eventCode, final View view, final User user, Bundle bundle,
                                        final UserRowHolder viewHolder, int position, DelegateAdapter adapter) {
                    switch (eventCode) {
                        case UserRowDelegate.PROFILE_CLICK:
                            if (isFabAnimating) {
                                return;
                            }
                            hideFabs(new Animator.AnimatorListener() {
                                @Override
                                public void onAnimationStart(Animator animator) {

                                }

                                @Override
                                public void onAnimationEnd(Animator animator) {
                                    animator.removeAllListeners();
                                    Intent it = new Intent(PhotoActivity.this, ProfileActivity.class);
                                    Pair<View, String> p1 = new Pair<View, String>(mThumbIv, getString(R.string.translation_name_photo));
                                    Pair<View, String> p2 = new Pair<View, String>(view, getString(R.string.translation_name_profile));
                                    Pair<View, String> p3 = new Pair<View, String>(viewHolder.nameTv, getString(R.string.translation_name_name));
                                    Pair<View, String> p4 = new Pair<View, String>(mTb, getString(R.string.translation_name_toolbar));

                                    ActivityOptionsCompat optionsCompat =
                                            ActivityOptionsCompat.makeSceneTransitionAnimation(PhotoActivity.this, p1, p2, p3, p4);
                                    it.putExtra("user", user);
                                    it.putExtra("photo", mPhoto);
                                    PhotoActivity.this.startActivity(it, optionsCompat.toBundle());
                                }

                                @Override
                                public void onAnimationCancel(Animator animator) {

                                }

                                @Override
                                public void onAnimationRepeat(Animator animator) {

                                }
                            });
                            break;
                    }
                }

    };

    private boolean isFabsShowing = false;

    private float mShareTransX = 0, mSetTransX = 0;

    private boolean isFabAnimating = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photo);

        if (savedInstanceState == null) {
            mPhoto = getIntent().getParcelableExtra("photo");
        } else {
            mPhoto = savedInstanceState.getParcelable("photo");
        }

        Api.photoDetail(this, mPhoto).enqueue(new Callback<Photo>() {
            @Override
            public void onResponse(@NonNull Call<Photo> call, @NonNull Response<Photo> response) {
                String limit = response.headers().get("X-Ratelimit-Limit");
                String remaining = response.headers().get("X-Ratelimit-Remaining");
                Log.v(TAG, "limit=" + limit + " remaining=" + remaining);
                if (response.isSuccessful()) {
                    mPhoto = response.body();
                    refreshDetail();

                }
            }

            @Override
            public void onFailure(@NonNull Call<Photo> call, @NonNull Throwable t) {
                t.printStackTrace();
            }
        });

        mThumbIv = (AppCompatImageView)findViewById(R.id.photo_thumb);
        mThumbIv.setBackgroundColor(mPhoto.getBgColor());
        Glide.with(this).load(mPhoto.urls.regular)
                .transition(DrawableTransitionOptions.withCrossFade()).into(mThumbIv);

        mTb = (Toolbar)findViewById(R.id.photo_tb);
        setSupportActionBar(mTb);
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setDisplayShowHomeEnabled(true);

            setTitle("");
        }

        mRv = (RecyclerView)findViewById(R.id.photo_info_rv);
        mAdapter = new DelegateAdapter(this);
        mRv.setAdapter(mAdapter);

        mSetFabLayout = findViewById(R.id.photo_set_fab_layout);
        mShareFab = (FloatingActionButton)findViewById(R.id.photo_share_fab);
        mSetFab = (FloatingActionButton)findViewById(R.id.photo_set_fab);
        mCpv = (CircularProgressView)findViewById(R.id.photo_progress);

        mSetFab.setOnClickListener(mClickListener);
        mShareFab.setOnClickListener(mClickListener);

        refreshDetail();

        getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
//                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)mSetFab.getLayoutParams();
                mSetTransX = mSetFabLayout.getWidth();
                mShareTransX = mSetTransX + mShareFab.getWidth();

                mSetFabLayout.setTranslationX(mSetTransX);
                mShareFab.setTranslationX(mShareTransX);

            }
        });

        if (PhotoManager.getInstance().isDownloadComplete(mPhoto)) {
            mFile = PhotoManager.getInstance().getFile(mPhoto);
            mSetFab.setImageResource(R.drawable.ic_format_paint);
        }

    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!isFabsShowing) {
            showFabs();
        }
    }

    @Override
    public void onBackPressed() {
        if (isFabAnimating) {
            return;
        }
        hideFabs(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {
                if (isDestroyed()) {
                    return;
                }
                animator.removeAllListeners();
                PhotoActivity.super.onBackPressed();
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                animator.removeAllListeners();
            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putParcelable("photo", mPhoto);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onStop() {
        if (mCancelable != null && !mCancelable.isCancelled()) {
            mCancelable.cancel();
        }
        if (mService != null) {
            unbindService(mConnection);
            mService = null;
            Intent it = new Intent(this, StreamService.class);
            stopService(it);
        }
        super.onStop();
    }

    @Override
    protected void onDestroy() {

        super.onDestroy();
    }

    public void onSetFabClick(View view) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) {
            if (mFile != null && mFile.exists()) {
                Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
                intent.addCategory(Intent.CATEGORY_DEFAULT);
                intent.setDataAndType(Uri.fromFile(mFile), "image/jpeg");
                intent.putExtra("mimeType", "image/jpeg");
                this.startActivity(Intent.createChooser(intent, "Set as:"));
                return;
            }
            if (mService != null) {
                if (isDownloading) {
                    ToastCenter.with(PhotoActivity.this).text(R.string.toast_download_is_executing).showShort();
                    return;
                }
                download();
            } else {
                bindStreamService();
            }
        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 100 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            bindStreamService();
        }
    }

    private void bindStreamService () {
        Intent it = new Intent(this, StreamService.class);
        startService(it);
        bindService(it, mConnection, Context.BIND_AUTO_CREATE);
    }

    private void download () {
        String url = mPhoto.urls.full;

        File outputFile = PhotoManager.getInstance().getFile(mPhoto);

        Log.v(TAG, "download url=" + url + " outputFile=" + outputFile.getAbsolutePath());
        mCancelable = mService.download(url, outputFile.getAbsolutePath(), mFileCallback);
    }

    private void refreshDetail () {
        mAdapter.clear();
        mAdapter.notifyDataSetChanged();
        mAdapter.add(new UserRowDelegate(mPhoto.user, userRowEventListener)).autoNotify();
        if (!TextUtils.isEmpty(mPhoto.description) || mPhoto.location != null) {
            mAdapter.add(new PhotoInfoDelegate(mPhoto)).autoNotify();
        }
        if (mPhoto.exif != null && mPhoto.exif.isValid()) {
            mAdapter.add(new ExifDelegate(mPhoto.exif)).autoNotify();
        }

    }

    private void showFabs () {
        isFabAnimating = true;
        isFabsShowing = true;
        mSetFabLayout.postDelayed(new Runnable() {
            @Override
            public void run() {
                final AnimatorSet animatorSet = new AnimatorSet();

                ObjectAnimator shareAnim = ObjectAnimator.ofFloat(mShareFab, "translationX", mShareFab.getTranslationX(), 0);
                ObjectAnimator setAnim = ObjectAnimator.ofFloat(mSetFabLayout, "translationX", mSetFabLayout.getTranslationX(), 0);

                animatorSet.play(shareAnim).with(setAnim);
                animatorSet.setInterpolator(new OvershootInterpolator());
                animatorSet.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
                animatorSet.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animator) {

                    }

                    @Override
                    public void onAnimationEnd(Animator animator) {
                        isFabAnimating = false;
                    }

                    @Override
                    public void onAnimationCancel(Animator animator) {
                        isFabAnimating = false;
                    }

                    @Override
                    public void onAnimationRepeat(Animator animator) {

                    }
                });
                animatorSet.start();
            }
        }, getResources().getInteger(android.R.integer.config_mediumAnimTime));
    }

    private void hideFabs (Animator.AnimatorListener listener) {
        final AnimatorSet animatorSet = new AnimatorSet();

        ObjectAnimator shareAnim = ObjectAnimator.ofFloat(mShareFab, "translationX", mShareFab.getTranslationX(), mShareTransX);
        ObjectAnimator setAnim = ObjectAnimator.ofFloat(mSetFabLayout, "translationX", mSetFabLayout.getTranslationX(), mSetTransX);

        animatorSet.play(shareAnim).with(setAnim);
        animatorSet.setInterpolator(new AccelerateInterpolator());
        animatorSet.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime));

        animatorSet.addListener(listener);
        animatorSet.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {
                isFabAnimating = false;
                animatorSet.removeAllListeners();
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                isFabAnimating = false;
            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        animatorSet.start();
        isFabAnimating = true;
        isFabsShowing = false;
    }

    private ObjectAnimator mRotate = null;
    private void showProgress () {
        ObjectAnimator alpha = ObjectAnimator.ofFloat(mCpv, "alpha", 0f, 1f);

        alpha.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                mCpv.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                animator.removeAllListeners();
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                animator.removeAllListeners();
            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        alpha.start();

        mSetFab.setPivotX(mSetFab.getWidth() * 1f / 2);
        mSetFab.setPivotY(mSetFab.getHeight() * 1f / 2);
        mRotate = ObjectAnimator.ofFloat(mSetFab, "rotation", 0, 360);
        mRotate.setDuration(2000);
        mRotate.setRepeatMode(ValueAnimator.RESTART);
        mRotate.setRepeatCount(1000);
        mRotate.start();
    }

    private void hideProgress () {

        if (mRotate != null && mRotate.isRunning()) {
            mRotate.cancel();
            mSetFab.setPivotX(mSetFab.getWidth() * 1f / 2);
            mSetFab.setPivotY(mSetFab.getHeight() * 1f / 2);
            mRotate = ObjectAnimator.ofFloat(mSetFab, "rotation", mSetFab.getRotation(), 0);
            mRotate.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime));
            mRotate.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {

                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    mSetFab.setRotation(0);
                    mRotate = null;
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
            mRotate.start();
        }

        ObjectAnimator alpha = ObjectAnimator.ofFloat(mCpv, "alpha", 1f, 0f);

        alpha.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                mCpv.setVisibility(View.GONE);
                animator.removeAllListeners();
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                animator.removeAllListeners();
            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        alpha.start();
    }

} public class EmptyDelegate extends StateDelegate<String, EmptyHolder> {


    public EmptyDelegate(String t, OnViewEventListener<String, EmptyHolder> onViewEventListener) {
        super(t, onViewEventListener);
    }

    @Override
    public int getLayout() {
        return R.layout.layout_empty;
    }

    @Override
    public Class<EmptyHolder> getHolderClass() {
        return EmptyHolder.class;
    }
} public class FooterDelegate extends StateDelegate<String, FooterHolder> {

    public FooterDelegate(String t) {
        super(t);
    }

    @Override
    public int getLayout() {
        return R.layout.layout_footer;
    }

    @Override
    public Class<FooterHolder> getHolderClass() {
        return FooterHolder.class;
    }
} public class PhotoDelegate extends AbsDelegate<Photo, PhotoHolder> {

    public static final int EVENT_CLICK = 1, EVENT_LONG_CLICK = 2;

    public PhotoDelegate(Photo t) {
        super(t);
    }

    @Override
    public int getLayout() {
        return R.layout.layout_photo;
    }

    @Override
    public Class<PhotoHolder> getHolderClass() {
        return PhotoHolder.class;
    }
} public class EmptyHolder extends AbsViewHolder<EmptyDelegate> {

    private LottieAnimationView lottieView;
    private View failedLayout;
    private AppCompatTextView detailTv;
    private AppCompatButton retryBtn;

    public EmptyHolder(View itemView) {
        super(itemView);

        lottieView = findViewById(R.id.empty_lottie);
        failedLayout = findViewById(R.id.empty_failed_layout);
        detailTv = findViewById(R.id.empty_failed_detail);
        retryBtn = findViewById(R.id.empty_retry_btn);
    }

    @Override
    public void onViewAttachedToWindow(DelegateAdapter adapter, Context context) {
        super.onViewAttachedToWindow(adapter, context);
        lottieView.playAnimation();
    }

    @Override
    public void onViewDetachedFromWindow(DelegateAdapter adapter, Context context) {
        super.onViewDetachedFromWindow(adapter, context);
        lottieView.cancelAnimation();
    }

    @Override
    public void onBindView(Context context, final EmptyDelegate emptyDelegate, final int position, final DelegateAdapter adapter) {

        if (emptyDelegate.isFailed()) {
            final String detail = emptyDelegate.getSource();
            failedLayout.setVisibility(View.VISIBLE);
            detailTv.setText(detail);
            retryBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    emptyDelegate.actionViewEvent(0, view, EmptyHolder.this, position, adapter);
                }
            });
        } else {
            failedLayout.setVisibility(View.GONE);
            retryBtn.setOnClickListener(null);
        }

    }
} public class PhotoHolder extends AbsViewHolder<PhotoDelegate> {

    private AppCompatImageView thumbIv;
    private AppCompatTextView authorTv;

    private BitmapTransitionOptions options;

    private View.OnClickListener clickListener = null;

    public PhotoHolder(View itemView) {
        super(itemView);

        thumbIv = findViewById(R.id.photo_thumb);
        authorTv = findViewById(R.id.photo_author);
        options = BitmapTransitionOptions.withCrossFade();
    }

    @Override
    public void onBindView(final Context context, final PhotoDelegate t, final int position, final DelegateAdapter adapter) {
        final Photo photo = t.getSource();
        itemView.setBackgroundColor(photo.getBgColor());
        authorTv.setText(context.getString(R.string.text_unsplash, photo.user.getFullName()));
        Glide.with(context).asBitmap().load(photo.urls.regular)
                .transition(options).into(thumbIv);

        clickListener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final int id = view.getId();
                if (id == itemView.getId()) {
                    t.actionViewEvent(
                            PhotoDelegate.EVENT_CLICK,
                            view, PhotoHolder.this, position, adapter);
                } else if (id == authorTv.getId()) {
                    Intent it = new Intent(context, ProfileActivity.class);
                    it.putExtra("photo", photo);
                    it.putExtra("user", photo.user);
                    context.startActivity(it);
                }
            }
        };
        authorTv.setOnClickListener(clickListener);
        itemView.setOnClickListener(clickListener);
    }
} public class Photo implements Parcelable {

    public String id;
//    public Date created_at, updated_at;
    public int width, height;
    public String color;
    public int downloads;
    public int likes;
    public int views;
    public boolean liked_by_user;
    public String description;
    public Exif exif;
    public Location location;
    public Urls urls;
    public Links links;
    public User user;

    private int bgColor;

    public int getBgColor () {
        try {
            return Color.parseColor(color);
        } catch (Exception e) {
            return Color.LTGRAY;
        }
    }

    protected Photo(Parcel in) {
        id = in.readString();
        width = in.readInt();
        height = in.readInt();
        color = in.readString();
        downloads = in.readInt();
        likes = in.readInt();
        views = in.readInt();
        liked_by_user = in.readByte() != 0;
        description = in.readString();
        exif = in.readParcelable(Exif.class.getClassLoader());
        location = in.readParcelable(Location.class.getClassLoader());
        urls = in.readParcelable(Urls.class.getClassLoader());
        links = in.readParcelable(Links.class.getClassLoader());
        user = in.readParcelable(User.class.getClassLoader());
        bgColor = in.readInt();
    }

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

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

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(id);
        parcel.writeInt(width);
        parcel.writeInt(height);
        parcel.writeString(color);
        parcel.writeInt(downloads);
        parcel.writeInt(likes);
        parcel.writeInt(views);
        parcel.writeByte((byte) (liked_by_user ? 1 : 0));
        parcel.writeString(description);
        parcel.writeParcelable(exif, i);
        parcel.writeParcelable(location, i);
        parcel.writeParcelable(urls, i);
        parcel.writeParcelable(links, i);
        parcel.writeParcelable(user, i);
        parcel.writeInt(bgColor);
    }
}
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.github.boybeak.adapter.AbsDelegate;
import com.github.boybeak.adapter.DelegateAdapter;
import com.github.boybeak.adapter.DelegateParser;
import com.github.boybeak.adapter.OnViewEventListener;
import com.github.boybeak.adapter.extension.SuperAdapter;
import com.github.boybeak.adapter.extension.callback.OnScrollBottomListener;
import com.github.boybeak.adapter.impl.LayoutImpl;
import com.github.boybeak.selector.Path;
import com.github.boybeak.timepaper.R;
import com.github.boybeak.timepaper.activity.PhotoActivity;
import com.github.boybeak.timepaper.adapter.delegate.EmptyDelegate;
import com.github.boybeak.timepaper.adapter.delegate.FooterDelegate;
import com.github.boybeak.timepaper.adapter.delegate.PhotoDelegate;
import com.github.boybeak.timepaper.adapter.holder.EmptyHolder;
import com.github.boybeak.timepaper.adapter.holder.PhotoHolder;
import com.github.boybeak.timepaper.model.Photo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
package com.github.boybeak.timepaper.fragment;





/**
 * Created by gaoyunfei on 2017/9/4.
 */

public abstract class PaperFragment extends Fragment implements Callback<List<Photo>>,
        SwipeRefreshLayout.OnRefreshListener, DelegateParser<Photo>, OnViewEventListener<Photo, PhotoHolder> {

    private static final String TAG = PaperFragment.class.getSimpleName();

    private SwipeRefreshLayout mSrl;
    private RecyclerView mRv;

    private SuperAdapter<EmptyDelegate, FooterDelegate> mAdapter;