/* * Copyright (C) 2015 Haruki Hasegawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.h6ah4i.android.widget.advrecyclerview.expandable; import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; import com.h6ah4i.android.widget.advrecyclerview.adapter.SimpleWrapperAdapter; import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemConstants; import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder; import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange; import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager; import com.h6ah4i.android.widget.advrecyclerview.swipeable.SwipeableItemAdapter; import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultAction; import com.h6ah4i.android.widget.advrecyclerview.adapter.ItemIdComposer; import com.h6ah4i.android.widget.advrecyclerview.adapter.ItemViewTypeComposer; import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; import java.util.List; class ExpandableRecyclerViewWrapperAdapter extends SimpleWrapperAdapter<RecyclerView.ViewHolder> implements DraggableItemAdapter<RecyclerView.ViewHolder>, SwipeableItemAdapter<RecyclerView.ViewHolder> { private static final String TAG = "ARVExpandableWrapper"; // // NOTE: Make accessible with short name private interface Constants extends ExpandableItemConstants { } private static final int VIEW_TYPE_FLAG_IS_GROUP = ItemViewTypeComposer.BIT_MASK_EXPANDABLE_FLAG; private static final int STATE_FLAG_INITIAL_VALUE = -1; private ExpandableItemAdapter mExpandableItemAdapter; private RecyclerViewExpandableItemManager mExpandableListManager; private ExpandablePositionTranslator mPositionTranslator; private int mDraggingItemGroupRangeStart = RecyclerView.NO_POSITION; private int mDraggingItemGroupRangeEnd = RecyclerView.NO_POSITION; private int mDraggingItemChildRangeStart = RecyclerView.NO_POSITION; private int mDraggingItemChildRangeEnd = RecyclerView.NO_POSITION; private RecyclerViewExpandableItemManager.OnGroupExpandListener mOnGroupExpandListener; private RecyclerViewExpandableItemManager.OnGroupCollapseListener mOnGroupCollapseListener; public ExpandableRecyclerViewWrapperAdapter(RecyclerViewExpandableItemManager manager, RecyclerView.Adapter<RecyclerView.ViewHolder> adapter, long[] expandedItemsSavedState) { super(adapter); mExpandableItemAdapter = getExpandableItemAdapter(adapter); if (mExpandableItemAdapter == null) { throw new IllegalArgumentException("adapter does not implement ExpandableItemAdapter"); } if (manager == null) { throw new IllegalArgumentException("manager cannot be null"); } mExpandableListManager = manager; mPositionTranslator = new ExpandablePositionTranslator(); mPositionTranslator.build( mExpandableItemAdapter, ExpandablePositionTranslator.BUILD_OPTION_DEFAULT, mExpandableListManager.getDefaultGroupsExpandedState()); if (expandedItemsSavedState != null) { // NOTE: do not call hook routines and listener methods mPositionTranslator.restoreExpandedGroupItems(expandedItemsSavedState, null, null, null); } } @Override protected void onRelease() { super.onRelease(); mExpandableItemAdapter = null; mExpandableListManager = null; mOnGroupExpandListener = null; mOnGroupCollapseListener = null; } @Override public int getItemCount() { return mPositionTranslator.getItemCount(); } @Override public long getItemId(int position) { if (mExpandableItemAdapter == null) { return RecyclerView.NO_ID; } final long expandablePosition = mPositionTranslator.getExpandablePosition(position); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); if (childPosition == RecyclerView.NO_POSITION) { final long groupId = mExpandableItemAdapter.getGroupId(groupPosition); return ItemIdComposer.composeExpandableGroupId(groupId); } else { final long groupId = mExpandableItemAdapter.getGroupId(groupPosition); final long childId = mExpandableItemAdapter.getChildId(groupPosition, childPosition); return ItemIdComposer.composeExpandableChildId(groupId, childId); } } @Override public int getItemViewType(int position) { if (mExpandableItemAdapter == null) { return 0; } final long expandablePosition = mPositionTranslator.getExpandablePosition(position); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); final int type; if (childPosition == RecyclerView.NO_POSITION) { type = mExpandableItemAdapter.getGroupItemViewType(groupPosition); } else { type = mExpandableItemAdapter.getChildItemViewType(groupPosition, childPosition); } if ((type & VIEW_TYPE_FLAG_IS_GROUP) != 0) { throw new IllegalStateException("Illegal view type (type = " + Integer.toHexString(type) + ")"); } return (childPosition == RecyclerView.NO_POSITION) ? (type | VIEW_TYPE_FLAG_IS_GROUP) : (type); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (mExpandableItemAdapter == null) { return null; } final int maskedViewType = (viewType & (~VIEW_TYPE_FLAG_IS_GROUP)); final RecyclerView.ViewHolder holder; if ((viewType & VIEW_TYPE_FLAG_IS_GROUP) != 0) { holder = mExpandableItemAdapter.onCreateGroupViewHolder(parent, maskedViewType); } else { holder = mExpandableItemAdapter.onCreateChildViewHolder(parent, maskedViewType); } if (holder instanceof ExpandableItemViewHolder) { ((ExpandableItemViewHolder) holder).setExpandStateFlags(STATE_FLAG_INITIAL_VALUE); } return holder; } @SuppressWarnings("unchecked") @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) { if (mExpandableItemAdapter == null) { return; } final long expandablePosition = mPositionTranslator.getExpandablePosition(position); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); final int viewType = (holder.getItemViewType() & (~VIEW_TYPE_FLAG_IS_GROUP)); // update flags int flags = 0; if (childPosition == RecyclerView.NO_POSITION) { flags |= Constants.STATE_FLAG_IS_GROUP; } else { flags |= Constants.STATE_FLAG_IS_CHILD; } if (mPositionTranslator.isGroupExpanded(groupPosition)) { flags |= Constants.STATE_FLAG_IS_EXPANDED; } safeUpdateExpandStateFlags(holder, flags); correctItemDragStateFlags(holder, groupPosition, childPosition); if (childPosition == RecyclerView.NO_POSITION) { mExpandableItemAdapter.onBindGroupViewHolder(holder, groupPosition, viewType, payloads); } else { mExpandableItemAdapter.onBindChildViewHolder(holder, groupPosition, childPosition, viewType, payloads); } } private void rebuildPositionTranslator() { if (mPositionTranslator != null) { long[] savedState = mPositionTranslator.getSavedStateArray(); mPositionTranslator.build( mExpandableItemAdapter, ExpandablePositionTranslator.BUILD_OPTION_DEFAULT, mExpandableListManager.getDefaultGroupsExpandedState()); // NOTE: do not call hook routines and listener methods mPositionTranslator.restoreExpandedGroupItems(savedState, null, null, null); } } @Override public void onViewRecycled(RecyclerView.ViewHolder holder, int viewType) { if (holder instanceof ExpandableItemViewHolder) { ((ExpandableItemViewHolder) holder).setExpandStateFlags(STATE_FLAG_INITIAL_VALUE); } super.onViewRecycled(holder, viewType); } @Override protected void onHandleWrappedAdapterChanged() { rebuildPositionTranslator(); super.onHandleWrappedAdapterChanged(); } @Override protected void onHandleWrappedAdapterItemRangeChanged(int positionStart, int itemCount) { super.onHandleWrappedAdapterItemRangeChanged(positionStart, itemCount); } @Override protected void onHandleWrappedAdapterItemRangeInserted(int positionStart, int itemCount) { rebuildPositionTranslator(); super.onHandleWrappedAdapterItemRangeInserted(positionStart, itemCount); } @Override protected void onHandleWrappedAdapterItemRangeRemoved(int positionStart, int itemCount) { if (itemCount == 1) { final long expandablePosition = mPositionTranslator.getExpandablePosition(positionStart); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); if (childPosition == RecyclerView.NO_POSITION) { mPositionTranslator.removeGroupItem(groupPosition); } else { mPositionTranslator.removeChildItem(groupPosition, childPosition); } } else { rebuildPositionTranslator(); } super.onHandleWrappedAdapterItemRangeRemoved(positionStart, itemCount); } @Override protected void onHandleWrappedAdapterRangeMoved(int fromPosition, int toPosition, int itemCount) { rebuildPositionTranslator(); super.onHandleWrappedAdapterRangeMoved(fromPosition, toPosition, itemCount); } @SuppressWarnings("unchecked") @Override public boolean onCheckCanStartDrag(RecyclerView.ViewHolder holder, int position, int x, int y) { if (!(mExpandableItemAdapter instanceof ExpandableDraggableItemAdapter)) { return false; } final ExpandableDraggableItemAdapter adapter = (ExpandableDraggableItemAdapter) mExpandableItemAdapter; //noinspection UnnecessaryLocalVariable final int flatPosition = position; final long expandablePosition = mPositionTranslator.getExpandablePosition(flatPosition); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); boolean canStart; if (childPosition == RecyclerView.NO_POSITION) { canStart = adapter.onCheckGroupCanStartDrag(holder, groupPosition, x, y); } else { canStart = adapter.onCheckChildCanStartDrag(holder, groupPosition, childPosition, x, y); } mDraggingItemGroupRangeStart = RecyclerView.NO_POSITION; mDraggingItemGroupRangeEnd = RecyclerView.NO_POSITION; mDraggingItemChildRangeStart = RecyclerView.NO_POSITION; mDraggingItemChildRangeEnd = RecyclerView.NO_POSITION; return canStart; } @SuppressWarnings("unchecked") @Override public ItemDraggableRange onGetItemDraggableRange(RecyclerView.ViewHolder holder, int position) { if (!(mExpandableItemAdapter instanceof ExpandableDraggableItemAdapter)) { return null; } if (mExpandableItemAdapter.getGroupCount() < 1) { return null; } final ExpandableDraggableItemAdapter adapter = (ExpandableDraggableItemAdapter) mExpandableItemAdapter; //noinspection UnnecessaryLocalVariable final int flatPosition = position; final long expandablePosition = mPositionTranslator.getExpandablePosition(flatPosition); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); if (childPosition == RecyclerView.NO_POSITION) { // group final ItemDraggableRange range = adapter.onGetGroupItemDraggableRange(holder, groupPosition); if (range == null) { final int lastGroup = Math.max(0, mExpandableItemAdapter.getGroupCount() - 1); final int start = 0; final int end = Math.max(start, mPositionTranslator.getItemCount() - mPositionTranslator.getVisibleChildCount(lastGroup) - 1); return new ItemDraggableRange(start, end); } else if (isGroupPositionRange(range)) { final long startPackedGroupPosition = ExpandableAdapterHelper.getPackedPositionForGroup(range.getStart()); final long endPackedGroupPosition = ExpandableAdapterHelper.getPackedPositionForGroup(range.getEnd()); final int start = mPositionTranslator.getFlatPosition(startPackedGroupPosition); int end = mPositionTranslator.getFlatPosition(endPackedGroupPosition); if (range.getEnd() > groupPosition) { end += mPositionTranslator.getVisibleChildCount(range.getEnd()); } mDraggingItemGroupRangeStart = range.getStart(); mDraggingItemGroupRangeEnd = range.getEnd(); return new ItemDraggableRange(start, end); } else { throw new IllegalStateException("Invalid range specified: " + range); } } else { // child final ItemDraggableRange range = adapter.onGetChildItemDraggableRange(holder, groupPosition, childPosition); // NOTE: // This method returns actual drag-sortable range, but the visual drag-sortable range would be different. // Thus appending the STATE_FLAG_IS_IN_RANGE flag at correctItemDragStateFlags() to avoid visual corruption. if (range == null) { final int start = 1; // 1 --- to avoid swapping with the first group item return new ItemDraggableRange(start, Math.max(start, mPositionTranslator.getItemCount() - 1)); } else if (isGroupPositionRange(range)) { final long startPackedGroupPosition = ExpandableAdapterHelper.getPackedPositionForGroup(range.getStart()); final long endPackedGroupPosition = ExpandableAdapterHelper.getPackedPositionForGroup(range.getEnd()); final int end = mPositionTranslator.getFlatPosition(endPackedGroupPosition) + mPositionTranslator.getVisibleChildCount(range.getEnd()); int start = mPositionTranslator.getFlatPosition(startPackedGroupPosition) + 1; start = Math.min(start, end); mDraggingItemGroupRangeStart = range.getStart(); mDraggingItemGroupRangeEnd = range.getEnd(); return new ItemDraggableRange(start, end); } else if (isChildPositionRange(range)) { final int maxChildrenPos = Math.max(mPositionTranslator.getVisibleChildCount(groupPosition) - 1, 0); final int childStart = Math.min(range.getStart(), maxChildrenPos); final int childEnd = Math.min(range.getEnd(), maxChildrenPos); final long startPackedChildPosition = ExpandableAdapterHelper.getPackedPositionForChild(groupPosition, childStart); final long endPackedChildPosition = ExpandableAdapterHelper.getPackedPositionForChild(groupPosition, childEnd); final int start = mPositionTranslator.getFlatPosition(startPackedChildPosition); final int end = mPositionTranslator.getFlatPosition(endPackedChildPosition); mDraggingItemChildRangeStart = childStart; mDraggingItemChildRangeEnd = childEnd; return new ItemDraggableRange(start, end); } else { throw new IllegalStateException("Invalid range specified: " + range); } } } @SuppressWarnings("unchecked") @Override public boolean onCheckCanDrop(int draggingPosition, int dropPosition) { if (!(mExpandableItemAdapter instanceof ExpandableDraggableItemAdapter)) { return true; } if (mExpandableItemAdapter.getGroupCount() < 1) { return false; } final ExpandableDraggableItemAdapter adapter = (ExpandableDraggableItemAdapter) mExpandableItemAdapter; //noinspection UnnecessaryLocalVariable final int draggingFlatPosition = draggingPosition; final long draggingExpandablePosition = mPositionTranslator.getExpandablePosition(draggingFlatPosition); final int draggingGroupPosition = ExpandableAdapterHelper.getPackedPositionGroup(draggingExpandablePosition); final int draggingChildPosition = ExpandableAdapterHelper.getPackedPositionChild(draggingExpandablePosition); //noinspection UnnecessaryLocalVariable final int dropFlatPosition = dropPosition; final long dropExpandablePosition = mPositionTranslator.getExpandablePosition(dropFlatPosition); final int dropGroupPosition = ExpandableAdapterHelper.getPackedPositionGroup(dropExpandablePosition); final int dropChildPosition = ExpandableAdapterHelper.getPackedPositionChild(dropExpandablePosition); final boolean draggingIsGroup = (draggingChildPosition == RecyclerView.NO_POSITION); final boolean dropIsGroup = (dropChildPosition == RecyclerView.NO_POSITION); if (draggingIsGroup) { // dragging: group boolean canDrop; if (draggingGroupPosition == dropGroupPosition) { canDrop = dropIsGroup; } else if (draggingFlatPosition < dropFlatPosition) { final boolean isDropGroupExpanded = mPositionTranslator.isGroupExpanded(dropGroupPosition); final int dropGroupVisibleChildren = mPositionTranslator.getVisibleChildCount(dropGroupPosition); if (dropIsGroup) { canDrop = (!isDropGroupExpanded); } else { canDrop = (dropChildPosition == (dropGroupVisibleChildren - 1)); } } else { // draggingFlatPosition > dropFlatPosition canDrop = dropIsGroup; } if (canDrop) { return adapter.onCheckGroupCanDrop(draggingGroupPosition, dropGroupPosition); } else { return false; } } else { // dragging: child final boolean isDropGroupExpanded = mPositionTranslator.isGroupExpanded(dropGroupPosition); boolean canDrop; int modDropGroupPosition = dropGroupPosition; int modDropChildPosition = dropChildPosition; if (draggingFlatPosition < dropFlatPosition) { canDrop = true; if (dropIsGroup) { if (isDropGroupExpanded) { modDropChildPosition = 0; } else { modDropChildPosition = mPositionTranslator.getChildCount(modDropGroupPosition); } } } else { // draggingFlatPosition > dropFlatPosition if (dropIsGroup) { if (modDropGroupPosition > 0) { modDropGroupPosition -= 1; modDropChildPosition = mPositionTranslator.getChildCount(modDropGroupPosition); canDrop = true; } else { canDrop = false; } } else { canDrop = true; } } if (canDrop) { return adapter.onCheckChildCanDrop(draggingGroupPosition, draggingChildPosition, modDropGroupPosition, modDropChildPosition); } else { return false; } } } private static boolean isGroupPositionRange(ItemDraggableRange range) { if (range.getClass().equals(GroupPositionItemDraggableRange.class)) { return true; } else if (range.getClass().equals(ItemDraggableRange.class)) { // NOTE: ItemDraggableRange is regarded as group position return true; } return false; } private static boolean isChildPositionRange(ItemDraggableRange range) { return range.getClass().equals(ChildPositionItemDraggableRange.class); } @Override public void onMoveItem(int fromPosition, int toPosition) { if (!(mExpandableItemAdapter instanceof ExpandableDraggableItemAdapter)) { return; } mDraggingItemGroupRangeStart = RecyclerView.NO_POSITION; mDraggingItemGroupRangeEnd = RecyclerView.NO_POSITION; mDraggingItemChildRangeStart = RecyclerView.NO_POSITION; mDraggingItemChildRangeEnd = RecyclerView.NO_POSITION; if (fromPosition == toPosition) { return; } final ExpandableDraggableItemAdapter adapter = (ExpandableDraggableItemAdapter) mExpandableItemAdapter; final long expandableFromPosition = mPositionTranslator.getExpandablePosition(fromPosition); final int fromGroupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandableFromPosition); final int fromChildPosition = ExpandableAdapterHelper.getPackedPositionChild(expandableFromPosition); final long expandableToPosition = mPositionTranslator.getExpandablePosition(toPosition); final int toGroupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandableToPosition); final int toChildPosition = ExpandableAdapterHelper.getPackedPositionChild(expandableToPosition); final boolean fromIsGroup = (fromChildPosition == RecyclerView.NO_POSITION); final boolean toIsGroup = (toChildPosition == RecyclerView.NO_POSITION); int actualToFlatPosition = fromPosition; if (fromIsGroup && toIsGroup) { adapter.onMoveGroupItem(fromGroupPosition, toGroupPosition); mPositionTranslator.moveGroupItem(fromGroupPosition, toGroupPosition); actualToFlatPosition = toPosition; } else if (!fromIsGroup && !toIsGroup) { int modToChildPosition; // correct child position if (fromGroupPosition == toGroupPosition) { modToChildPosition = toChildPosition; } else { if (fromPosition < toPosition) { modToChildPosition = toChildPosition + 1; } else { modToChildPosition = toChildPosition; } } actualToFlatPosition = mPositionTranslator.getFlatPosition( ExpandableAdapterHelper.getPackedPositionForChild(fromGroupPosition, modToChildPosition)); adapter.onMoveChildItem(fromGroupPosition, fromChildPosition, toGroupPosition, modToChildPosition); mPositionTranslator.moveChildItem(fromGroupPosition, fromChildPosition, toGroupPosition, modToChildPosition); } else if (!fromIsGroup /*&& toIsGroup NOTE: toIsGroup is always true here*/) { int modToGroupPosition; int modToChildPosition; if (toPosition < fromPosition) { if (toGroupPosition == 0) { // insert at the top modToGroupPosition = toGroupPosition; modToChildPosition = 0; } else { // insert at the end modToGroupPosition = toGroupPosition - 1; modToChildPosition = mPositionTranslator.getChildCount(modToGroupPosition); } } else { if (mPositionTranslator.isGroupExpanded(toGroupPosition)) { // insert at the top modToGroupPosition = toGroupPosition; modToChildPosition = 0; } else { // insert at the end modToGroupPosition = toGroupPosition; modToChildPosition = mPositionTranslator.getChildCount(modToGroupPosition); } } if (fromGroupPosition == modToGroupPosition) { final int lastIndex = Math.max(0, mPositionTranslator.getChildCount(modToGroupPosition) - 1); modToChildPosition = Math.min(modToChildPosition, lastIndex); } if (!((fromGroupPosition == modToGroupPosition) && (fromChildPosition == modToChildPosition))) { if (mPositionTranslator.isGroupExpanded(toGroupPosition)) { actualToFlatPosition = toPosition; } else { actualToFlatPosition = RecyclerView.NO_POSITION; } adapter.onMoveChildItem(fromGroupPosition, fromChildPosition, modToGroupPosition, modToChildPosition); mPositionTranslator.moveChildItem(fromGroupPosition, fromChildPosition, modToGroupPosition, modToChildPosition); } } else { // if (fromIsGroup && !toIsGroup) if (fromGroupPosition != toGroupPosition) { actualToFlatPosition = mPositionTranslator.getFlatPosition(ExpandableAdapterHelper.getPackedPositionForGroup(toGroupPosition)); adapter.onMoveGroupItem(fromGroupPosition, toGroupPosition); mPositionTranslator.moveGroupItem(fromGroupPosition, toGroupPosition); } } if (actualToFlatPosition != fromPosition) { if (actualToFlatPosition != RecyclerView.NO_POSITION) { notifyItemMoved(fromPosition, actualToFlatPosition); } else { notifyItemRemoved(fromPosition); } } } @SuppressWarnings("unchecked") @Override public int onGetSwipeReactionType(RecyclerView.ViewHolder holder, int position, int x, int y) { if (!(mExpandableItemAdapter instanceof BaseExpandableSwipeableItemAdapter)) { return RecyclerViewSwipeManager.REACTION_CAN_NOT_SWIPE_ANY; } final BaseExpandableSwipeableItemAdapter adapter = (BaseExpandableSwipeableItemAdapter) mExpandableItemAdapter; //noinspection UnnecessaryLocalVariable final int flatPosition = position; final long expandablePosition = mPositionTranslator.getExpandablePosition(flatPosition); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); if (childPosition == RecyclerView.NO_POSITION) { return adapter.onGetGroupItemSwipeReactionType(holder, groupPosition, x, y); } else { return adapter.onGetChildItemSwipeReactionType(holder, groupPosition, childPosition, x, y); } } @SuppressWarnings("unchecked") @Override public void onSetSwipeBackground(RecyclerView.ViewHolder holder, int position, int type) { if (!(mExpandableItemAdapter instanceof BaseExpandableSwipeableItemAdapter)) { return; } final BaseExpandableSwipeableItemAdapter adapter = (BaseExpandableSwipeableItemAdapter) mExpandableItemAdapter; //noinspection UnnecessaryLocalVariable final int flatPosition = position; final long expandablePosition = mPositionTranslator.getExpandablePosition(flatPosition); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); if (childPosition == RecyclerView.NO_POSITION) { adapter.onSetGroupItemSwipeBackground(holder, groupPosition, type); } else { adapter.onSetChildItemSwipeBackground(holder, groupPosition, childPosition, type); } } @SuppressWarnings("unchecked") @Override public SwipeResultAction onSwipeItem(RecyclerView.ViewHolder holder, int position, int result) { if (!(mExpandableItemAdapter instanceof BaseExpandableSwipeableItemAdapter)) { return null; } if (position == RecyclerView.NO_POSITION) { return null; } final BaseExpandableSwipeableItemAdapter<?, ?> adapter = (BaseExpandableSwipeableItemAdapter<?, ?>) mExpandableItemAdapter; //noinspection UnnecessaryLocalVariable final int flatPosition = position; final long expandablePosition = mPositionTranslator.getExpandablePosition(flatPosition); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); return ExpandableSwipeableItemInternalUtils.invokeOnSwipeItem( adapter, holder, groupPosition, childPosition, result); } // NOTE: This method is called from RecyclerViewExpandableItemManager /*package*/ @SuppressWarnings("unchecked") boolean onTapItem(RecyclerView.ViewHolder holder, int position, int x, int y) { if (mExpandableItemAdapter == null) { return false; } //noinspection UnnecessaryLocalVariable final int flatPosition = position; final long expandablePosition = mPositionTranslator.getExpandablePosition(flatPosition); final int groupPosition = ExpandableAdapterHelper.getPackedPositionGroup(expandablePosition); final int childPosition = ExpandableAdapterHelper.getPackedPositionChild(expandablePosition); if (childPosition != RecyclerView.NO_POSITION) { return false; } final boolean expand = !(mPositionTranslator.isGroupExpanded(groupPosition)); boolean result = mExpandableItemAdapter.onCheckCanExpandOrCollapseGroup(holder, groupPosition, x, y, expand); if (!result) { return false; } if (expand) { expandGroup(groupPosition, true, null); } else { collapseGroup(groupPosition, true, null); } return true; } /*package*/ void expandAll() { if (!mPositionTranslator.isEmpty() && !mPositionTranslator.isAllExpanded()) { mPositionTranslator.build( mExpandableItemAdapter, ExpandablePositionTranslator.BUILD_OPTION_EXPANDED_ALL, mExpandableListManager.getDefaultGroupsExpandedState()); notifyDataSetChanged(); } } /*package*/ void collapseAll() { if (!mPositionTranslator.isEmpty() && !mPositionTranslator.isAllCollapsed()) { mPositionTranslator.build( mExpandableItemAdapter, ExpandablePositionTranslator.BUILD_OPTION_COLLAPSED_ALL, mExpandableListManager.getDefaultGroupsExpandedState()); notifyDataSetChanged(); } } /*package*/ boolean collapseGroup(int groupPosition, boolean fromUser, Object payload) { if (!mPositionTranslator.isGroupExpanded(groupPosition)) { return false; } // call hook method if (!mExpandableItemAdapter.onHookGroupCollapse(groupPosition, fromUser, payload)) { return false; } if (mPositionTranslator.collapseGroup(groupPosition)) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); final int childCount = mPositionTranslator.getChildCount(groupPosition); notifyItemRangeRemoved(flatPosition + 1, childCount); } { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); notifyItemChanged(flatPosition, payload); } // raise onGroupCollapse() event if (mOnGroupCollapseListener != null) { mOnGroupCollapseListener.onGroupCollapse(groupPosition, fromUser, payload); } return true; } /*package*/ boolean expandGroup(int groupPosition, boolean fromUser, Object payload) { if (mPositionTranslator.isGroupExpanded(groupPosition)) { return false; } // call hook method if (!mExpandableItemAdapter.onHookGroupExpand(groupPosition, fromUser, payload)) { return false; } if (mPositionTranslator.expandGroup(groupPosition)) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); final int childCount = mPositionTranslator.getChildCount(groupPosition); notifyItemRangeInserted(flatPosition + 1, childCount); } { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); notifyItemChanged(flatPosition, payload); } // raise onGroupExpand() event if (mOnGroupExpandListener != null) { mOnGroupExpandListener.onGroupExpand(groupPosition, fromUser, payload); } return true; } /*package*/ boolean isGroupExpanded(int groupPosition) { return mPositionTranslator.isGroupExpanded(groupPosition); } /*package*/ long getExpandablePosition(int flatPosition) { return mPositionTranslator.getExpandablePosition(flatPosition); } /*package*/ int getFlatPosition(long packedPosition) { return mPositionTranslator.getFlatPosition(packedPosition); } /*package*/ long[] getExpandedItemsSavedStateArray() { if (mPositionTranslator != null) { return mPositionTranslator.getSavedStateArray(); } else { return null; } } /*package*/ void setOnGroupExpandListener(RecyclerViewExpandableItemManager.OnGroupExpandListener listener) { mOnGroupExpandListener = listener; } /*package*/ void setOnGroupCollapseListener(RecyclerViewExpandableItemManager.OnGroupCollapseListener listener) { mOnGroupCollapseListener = listener; } /*package*/ void restoreState(long[] adapterSavedState, boolean callHook, boolean callListeners) { mPositionTranslator.restoreExpandedGroupItems( adapterSavedState, (callHook ? mExpandableItemAdapter : null), (callListeners ? mOnGroupExpandListener : null), (callListeners ? mOnGroupCollapseListener : null)); } /*package*/ void notifyGroupItemChanged(int groupPosition, Object payload) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); if (flatPosition != RecyclerView.NO_POSITION) { notifyItemChanged(flatPosition, payload); } } /*package*/ void notifyGroupAndChildrenItemsChanged(int groupPosition, Object payload) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); final int visibleChildCount = mPositionTranslator.getVisibleChildCount(groupPosition); if (flatPosition != RecyclerView.NO_POSITION) { notifyItemRangeChanged(flatPosition, 1 + visibleChildCount, payload); } } /*package*/ void notifyChildrenOfGroupItemChanged(int groupPosition, Object payload) { final int visibleChildCount = mPositionTranslator.getVisibleChildCount(groupPosition); // notify if the group is expanded if (visibleChildCount > 0) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForChild(groupPosition, 0); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); if (flatPosition != RecyclerView.NO_POSITION) { notifyItemRangeChanged(flatPosition, visibleChildCount, payload); } } } /*package*/ void notifyChildItemChanged(int groupPosition, int childPosition, Object payload) { notifyChildItemRangeChanged(groupPosition, childPosition, 1, payload); } /*package*/ void notifyChildItemRangeChanged(int groupPosition, int childPositionStart, int itemCount, Object payload) { final int visibleChildCount = mPositionTranslator.getVisibleChildCount(groupPosition); // notify if the group is expanded if ((visibleChildCount > 0) && (childPositionStart < visibleChildCount)) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForChild(groupPosition, 0); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); if (flatPosition != RecyclerView.NO_POSITION) { final int startPosition = flatPosition + childPositionStart; final int count = Math.min(itemCount, (visibleChildCount - childPositionStart)); notifyItemRangeChanged(startPosition, count, payload); } } } /*package*/ void notifyChildItemInserted(int groupPosition, int childPosition) { mPositionTranslator.insertChildItem(groupPosition, childPosition); final long packedPosition = ExpandableAdapterHelper.getPackedPositionForChild(groupPosition, childPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); if (flatPosition != RecyclerView.NO_POSITION) { notifyItemInserted(flatPosition); } } /*package*/ void notifyChildItemRangeInserted(int groupPosition, int childPositionStart, int itemCount) { mPositionTranslator.insertChildItems(groupPosition, childPositionStart, itemCount); final long packedPosition = ExpandableAdapterHelper.getPackedPositionForChild(groupPosition, childPositionStart); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); if (flatPosition != RecyclerView.NO_POSITION) { notifyItemRangeInserted(flatPosition, itemCount); } } /*package*/ void notifyChildItemRemoved(int groupPosition, int childPosition) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForChild(groupPosition, childPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); mPositionTranslator.removeChildItem(groupPosition, childPosition); if (flatPosition != RecyclerView.NO_POSITION) { notifyItemRemoved(flatPosition); } } /*package*/ void notifyChildItemRangeRemoved(int groupPosition, int childPositionStart, int itemCount) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForChild(groupPosition, childPositionStart); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); mPositionTranslator.removeChildItems(groupPosition, childPositionStart, itemCount); if (flatPosition != RecyclerView.NO_POSITION) { notifyItemRangeRemoved(flatPosition, itemCount); } } /*package*/ void notifyGroupItemInserted(int groupPosition, boolean expanded) { int insertedCount = mPositionTranslator.insertGroupItem(groupPosition, expanded); if (insertedCount > 0) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); notifyItemInserted(flatPosition); // raise onGroupExpand() event raiseOnGroupExpandedSequentially(groupPosition, 1, false, null); } } /*package*/ void notifyGroupItemRangeInserted(int groupPositionStart, int count, boolean expanded) { int insertedCount = mPositionTranslator.insertGroupItems(groupPositionStart, count, expanded); if (insertedCount > 0) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPositionStart); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); notifyItemRangeInserted(flatPosition, insertedCount); raiseOnGroupExpandedSequentially(groupPositionStart, count, false, null); } } /*package*/ void notifyGroupItemMoved(int fromGroupPosition, int toGroupPosition) { long packedFrom = RecyclerViewExpandableItemManager.getPackedPositionForGroup(fromGroupPosition); long packedTo = RecyclerViewExpandableItemManager.getPackedPositionForGroup(toGroupPosition); int flatFrom = getFlatPosition(packedFrom); int flatTo = getFlatPosition(packedTo); boolean fromExpanded = isGroupExpanded(fromGroupPosition); boolean toExpanded = isGroupExpanded(toGroupPosition); mPositionTranslator.moveGroupItem(fromGroupPosition, toGroupPosition); if (!fromExpanded && !toExpanded) { notifyItemMoved(flatFrom, flatTo); } else { notifyDataSetChanged(); } } /*package*/ void notifyChildItemMoved(int groupPosition, int fromChildPosition, int toChildPosition) { notifyChildItemMoved(groupPosition, fromChildPosition, groupPosition, toChildPosition); } /*package*/ void notifyChildItemMoved(int fromGroupPosition, int fromChildPosition, int toGroupPosition, int toChildPosition) { long packedFrom = RecyclerViewExpandableItemManager.getPackedPositionForChild(fromGroupPosition, fromChildPosition); long packedTo = RecyclerViewExpandableItemManager.getPackedPositionForChild(toGroupPosition, toChildPosition); int flatFrom = getFlatPosition(packedFrom); int flatTo = getFlatPosition(packedTo); mPositionTranslator.moveChildItem(fromGroupPosition, fromChildPosition, toGroupPosition, toChildPosition); if (flatFrom != RecyclerView.NO_POSITION && flatTo != RecyclerView.NO_POSITION) { notifyItemMoved(flatFrom, flatTo); } else if (flatFrom != RecyclerView.NO_POSITION) { notifyItemRemoved(flatFrom); } else if (flatTo != RecyclerView.NO_POSITION) { notifyItemInserted(flatTo); } } private void raiseOnGroupExpandedSequentially(int groupPositionStart, int count, boolean fromUser, Object payload) { if (mOnGroupExpandListener != null) { for (int i = 0; i < count; i++) { mOnGroupExpandListener.onGroupExpand(groupPositionStart + i, fromUser, payload); } } } /*package*/ void notifyGroupItemRemoved(int groupPosition) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPosition); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); int removedCount = mPositionTranslator.removeGroupItem(groupPosition); if (removedCount > 0) { notifyItemRangeRemoved(flatPosition, removedCount); } } /*package*/ void notifyGroupItemRangeRemoved(int groupPositionStart, int count) { final long packedPosition = ExpandableAdapterHelper.getPackedPositionForGroup(groupPositionStart); final int flatPosition = mPositionTranslator.getFlatPosition(packedPosition); int removedCount = mPositionTranslator.removeGroupItems(groupPositionStart, count); if (removedCount > 0) { notifyItemRangeRemoved(flatPosition, removedCount); } } /*package*/ int getGroupCount() { return mExpandableItemAdapter.getGroupCount(); } /*package*/ int getChildCount(int groupPosition) { return mExpandableItemAdapter.getChildCount(groupPosition); } /*package*/ int getExpandedGroupsCount() { return mPositionTranslator.getExpandedGroupsCount(); } /*package*/ int getCollapsedGroupsCount() { return mPositionTranslator.getCollapsedGroupsCount(); } /*package*/ boolean isAllGroupsExpanded() { return mPositionTranslator.isAllExpanded(); } /*package*/ boolean isAllGroupsCollapsed() { return mPositionTranslator.isAllCollapsed(); } private static ExpandableItemAdapter getExpandableItemAdapter(RecyclerView.Adapter adapter) { return WrapperAdapterUtils.findWrappedAdapter(adapter, ExpandableItemAdapter.class); } private static void safeUpdateExpandStateFlags(RecyclerView.ViewHolder holder, int flags) { if (!(holder instanceof ExpandableItemViewHolder)) { return; } final ExpandableItemViewHolder holder2 = (ExpandableItemViewHolder) holder; final int curFlags = holder2.getExpandStateFlags(); final int mask = ~Constants.STATE_FLAG_IS_UPDATED; // append HAS_EXPANDED_STATE_CHANGED flag if ((curFlags != STATE_FLAG_INITIAL_VALUE) && (((curFlags ^ flags) & Constants.STATE_FLAG_IS_EXPANDED) != 0)) { flags |= Constants.STATE_FLAG_HAS_EXPANDED_STATE_CHANGED; } // append UPDATED flag if ((curFlags == STATE_FLAG_INITIAL_VALUE) || (((curFlags ^ flags) & mask) != 0)) { flags |= Constants.STATE_FLAG_IS_UPDATED; } holder2.setExpandStateFlags(flags); } private void correctItemDragStateFlags(RecyclerView.ViewHolder holder, int groupPosition, int childPosition) { if (!(holder instanceof DraggableItemViewHolder)) { return; } final DraggableItemViewHolder holder2 = (DraggableItemViewHolder) holder; final boolean groupRangeSpecified = (mDraggingItemGroupRangeStart != RecyclerView.NO_POSITION) && (mDraggingItemGroupRangeEnd != RecyclerView.NO_POSITION); final boolean childRangeSpecified = (mDraggingItemChildRangeStart != RecyclerView.NO_POSITION) && (mDraggingItemChildRangeEnd != RecyclerView.NO_POSITION); final boolean isInGroupRange = (groupPosition >= mDraggingItemGroupRangeStart) && (groupPosition <= mDraggingItemGroupRangeEnd); final boolean isInChildRange = (groupPosition != RecyclerView.NO_POSITION) && (childPosition >= mDraggingItemChildRangeStart) && (childPosition <= mDraggingItemChildRangeEnd); final int flags = holder2.getDragStateFlags(); boolean needCorrection = false; if (((flags & DraggableItemConstants.STATE_FLAG_DRAGGING) != 0) && ((flags & DraggableItemConstants.STATE_FLAG_IS_IN_RANGE) == 0)) { if (!groupRangeSpecified || isInGroupRange) { if (!childRangeSpecified || (childRangeSpecified && isInChildRange)) { needCorrection = true; } } } if (needCorrection) { holder2.setDragStateFlags( flags | DraggableItemConstants.STATE_FLAG_IS_IN_RANGE | DraggableItemConstants.STATE_FLAG_IS_UPDATED); } } }