getSuggestions() {
+ return mSuggestions;
+ }
+
+ public int getSuggestionMode() {
+ return mSuggestionMode;
+ }
+
+ public Condition getExpandedCondition() {
+ return mExpandedCondition;
+ }
+
+ /**
+ * Find the position of the object in mItems list, using the equals method to compare
+ *
+ * @param entity the object that need to be found in list
+ * @return position of the object, return POSITION_NOT_FOUND if object isn't in the list
+ */
+ public int getPositionByEntity(Object entity) {
+ if (entity == null) return POSITION_NOT_FOUND;
+
+ final int size = mItems.size();
+ for (int i = 0; i < size; i++) {
+ final Object item = mItems.get(i).entity;
+ if (entity.equals(item)) {
+ return i;
+ }
+ }
+
+ return POSITION_NOT_FOUND;
+ }
+
+ /**
+ * Find the position of the Tile object.
+ *
+ * First, try to find the exact identical instance of the tile object, if not found,
+ * then try to find a tile has the same title.
+ *
+ * @param tile tile that need to be found
+ * @return position of the object, return INDEX_NOT_FOUND if object isn't in the list
+ */
+ public int getPositionByTile(Tile tile) {
+ final int size = mItems.size();
+ for (int i = 0; i < size; i++) {
+ final Object entity = mItems.get(i).entity;
+ if (entity == tile) {
+ return i;
+ } else if (entity instanceof Tile && tile.title.equals(((Tile) entity).title)) {
+ return i;
+ }
+ }
+
+ return POSITION_NOT_FOUND;
+ }
+
+ /**
+ * Get the count of suggestions to display
+ *
+ * The displayable count mainly depends on the {@link #mSuggestionMode}
+ * and the size of suggestions list.
+ *
+ * When in default mode, displayable count couldn't larger than
+ * {@link #DEFAULT_SUGGESTION_COUNT}.
+ *
+ * When in expanded mode, display all the suggestions.
+ * @return the count of suggestions to display
+ */
+ public int getDisplayableSuggestionCount() {
+ final int suggestionSize = mSuggestions.size();
+ return mSuggestionMode == SUGGESTION_MODE_DEFAULT
+ ? Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize)
+ : mSuggestionMode == SUGGESTION_MODE_EXPANDED
+ ? suggestionSize : 0;
+ }
+
+ public boolean hasMoreSuggestions() {
+ return mSuggestionMode == SUGGESTION_MODE_COLLAPSED
+ || (mSuggestionMode == SUGGESTION_MODE_DEFAULT
+ && mSuggestions.size() > DEFAULT_SUGGESTION_COUNT);
+ }
+
+ private void resetCount() {
+ mId = 0;
+ }
+
+ /**
+ * Count the item and add it into list when {@paramref add} is true.
+ *
+ * Note that {@link #mId} will increment automatically and the real
+ * id stored in {@link Item} is shifted by {@paramref nameSpace}. This is a
+ * simple way to keep the id stable.
+ *
+ * @param object maybe {@link Condition}, {@link Tile}, {@link DashboardCategory} or null
+ * @param type type of the item, and value is the layout id
+ * @param add flag about whether to add item into list
+ * @param nameSpace namespace based on the type
+ */
+ private void countItem(Object object, int type, boolean add, int nameSpace) {
+ if (add) {
+ mItems.add(new Item(object, type, mId + nameSpace, object == mExpandedCondition));
+ }
+ mId++;
+ }
+
+ /**
+ * Build the mItems list using mConditions, mSuggestions, mCategories data
+ * and mIsShowingAll, mSuggestionMode flag.
+ */
+ private void buildItemsData() {
+ boolean hasConditions = false;
+ for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
+ boolean shouldShow = mConditions.get(i).shouldShow();
+ hasConditions |= shouldShow;
+ countItem(mConditions.get(i), R.layout.condition_card, shouldShow, NS_CONDITION);
+ }
+
+ resetCount();
+ final boolean hasSuggestions = mSuggestions != null && mSuggestions.size() != 0;
+ countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions, NS_SPACER);
+ countItem(buildSuggestionHeaderData(), R.layout.suggestion_header, hasSuggestions,
+ NS_SPACER);
+
+ resetCount();
+ if (mSuggestions != null) {
+ int maxSuggestions = getDisplayableSuggestionCount();
+ for (int i = 0; i < mSuggestions.size(); i++) {
+ countItem(mSuggestions.get(i), R.layout.suggestion_tile, i < maxSuggestions,
+ NS_SUGGESTION);
+ }
+ }
+ resetCount();
+ for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
+ DashboardCategory category = mCategories.get(i);
+ countItem(category, R.layout.dashboard_category,
+ !TextUtils.isEmpty(category.title), NS_ITEMS);
+ for (int j = 0; j < category.tiles.size(); j++) {
+ Tile tile = category.tiles.get(j);
+ countItem(tile, R.layout.dashboard_tile, true, NS_ITEMS);
+ }
+ }
+ }
+
+ private SuggestionHeaderData buildSuggestionHeaderData() {
+ SuggestionHeaderData data;
+ if (mSuggestions == null) {
+ data = new SuggestionHeaderData();
+ } else {
+ final boolean hasMoreSuggestions = hasMoreSuggestions();
+ final int suggestionSize = mSuggestions.size();
+ final int undisplayedSuggestionCount = suggestionSize - getDisplayableSuggestionCount();
+ data = new SuggestionHeaderData(hasMoreSuggestions, suggestionSize,
+ undisplayedSuggestionCount);
+ }
+
+ return data;
+ }
+
+ /**
+ * Builder used to build the ItemsData
+ *
+ * {@link #mExpandedCondition} and {@link #mSuggestionMode} have default value
+ * while others are not.
+ */
+ public static class Builder {
+ private int mSuggestionMode = SUGGESTION_MODE_DEFAULT;
+ private Condition mExpandedCondition = null;
+
+ private List mCategories;
+ private List mConditions;
+ private List mSuggestions;
+
+
+ public Builder() {
+ }
+
+ public Builder(DashboardData dashboardData) {
+ mCategories = dashboardData.mCategories;
+ mConditions = dashboardData.mConditions;
+ mSuggestions = dashboardData.mSuggestions;
+ mSuggestionMode = dashboardData.mSuggestionMode;
+ mExpandedCondition = dashboardData.mExpandedCondition;
+ }
+
+ public Builder setCategories(List categories) {
+ this.mCategories = categories;
+ return this;
+ }
+
+ public Builder setConditions(List conditions) {
+ this.mConditions = conditions;
+ return this;
+ }
+
+ public Builder setSuggestions(List suggestions) {
+ this.mSuggestions = suggestions;
+ return this;
+ }
+
+ public Builder setSuggestionMode(int suggestionMode) {
+ this.mSuggestionMode = suggestionMode;
+ return this;
+ }
+
+ public Builder setExpandedCondition(Condition expandedCondition) {
+ this.mExpandedCondition = expandedCondition;
+ return this;
+ }
+
+ public DashboardData build() {
+ return new DashboardData(this);
+ }
+ }
+
+ /**
+ * A DiffCallback to calculate the difference between old and new Item
+ * List in DashboardData
+ */
+ public static class ItemsDataDiffCallback extends DiffUtil.Callback {
+ final private List- mOldItems;
+ final private List
- mNewItems;
+
+ public ItemsDataDiffCallback(List
- oldItems, List
- newItems) {
+ mOldItems = oldItems;
+ mNewItems = newItems;
+ }
+
+ @Override
+ public int getOldListSize() {
+ return mOldItems.size();
+ }
+
+ @Override
+ public int getNewListSize() {
+ return mNewItems.size();
+ }
+
+ @Override
+ public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+ return mOldItems.get(oldItemPosition).id == mNewItems.get(newItemPosition).id;
+ }
+
+ @Override
+ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+ return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
+ }
+ }
+
+ /**
+ * An item contains the data needed in the DashboardData.
+ */
+ private static class Item {
+ // valid types in field type
+ private static final int TYPE_DASHBOARD_CATEGORY = R.layout.dashboard_category;
+ private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
+ private static final int TYPE_SUGGESTION_HEADER = R.layout.suggestion_header;
+ private static final int TYPE_SUGGESTION_TILE = R.layout.suggestion_tile;
+ private static final int TYPE_CONDITION_CARD = R.layout.condition_card;
+ private static final int TYPE_DASHBOARD_SPACER = R.layout.dashboard_spacer;
+
+ @IntDef({TYPE_DASHBOARD_CATEGORY, TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_HEADER,
+ TYPE_SUGGESTION_TILE, TYPE_CONDITION_CARD, TYPE_DASHBOARD_SPACER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ItemTypes{}
+
+ /**
+ * The main data object in item, usually is a {@link Tile}, {@link Condition} or
+ * {@link DashboardCategory} object. This object can also be null when the
+ * item is an divider line. Please refer to {@link #buildItemsData()} for
+ * detail usage of the Item.
+ */
+ public final Object entity;
+
+ /**
+ * The type of item, value inside is the layout id(e.g. R.layout.dashboard_tile)
+ */
+ public final @ItemTypes int type;
+
+ /**
+ * Id of this item, used in the {@link ItemsDataDiffCallback} to identify the same item.
+ */
+ public final int id;
+
+ /**
+ * To store whether the condition is expanded, useless when {@link #type} is not
+ * {@link #TYPE_CONDITION_CARD}
+ */
+ public final boolean conditionExpanded;
+
+ public Item(Object entity, @ItemTypes int type, int id, boolean conditionExpanded) {
+ this.entity = entity;
+ this.type = type;
+ this.id = id;
+ this.conditionExpanded = conditionExpanded;
+ }
+
+ /**
+ * Override it to make comparision in the {@link ItemsDataDiffCallback}
+ * @param obj object to compared with
+ * @return true if the same object or has equal value.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Item)) {
+ return false;
+ }
+
+ final Item targetItem = (Item) obj;
+ if (type != targetItem.type || id != targetItem.id) {
+ return false;
+ }
+
+ switch (type) {
+ case TYPE_DASHBOARD_CATEGORY:
+ // Only check title for dashboard category
+ return TextUtils.equals(((DashboardCategory)entity).title,
+ ((DashboardCategory) targetItem.entity).title);
+ case TYPE_DASHBOARD_TILE:
+ final Tile localTile = (Tile)entity;
+ final Tile targetTile = (Tile)targetItem.entity;
+
+ // Only check title and summary for dashboard tile
+ return TextUtils.equals(localTile.title, targetTile.title)
+ && TextUtils.equals(localTile.summary, targetTile.summary);
+ case TYPE_CONDITION_CARD:
+ // First check conditionExpanded for quick return
+ if (conditionExpanded != targetItem.conditionExpanded) {
+ return false;
+ }
+ // After that, go to default to do final check
+ default:
+ return entity == null ? targetItem.entity == null
+ : entity.equals(targetItem.entity);
+ }
+ }
+ }
+
+ /**
+ * This class contains the data needed to build the header. The data can also be
+ * used to check the diff in DiffUtil.Callback
+ */
+ public static class SuggestionHeaderData {
+ public final boolean hasMoreSuggestions;
+ public final int suggestionSize;
+ public final int undisplayedSuggestionCount;
+
+ public SuggestionHeaderData(boolean moreSuggestions, int suggestionSize, int
+ undisplayedSuggestionCount) {
+ this.hasMoreSuggestions = moreSuggestions;
+ this.suggestionSize = suggestionSize;
+ this.undisplayedSuggestionCount = undisplayedSuggestionCount;
+ }
+
+ public SuggestionHeaderData() {
+ hasMoreSuggestions = false;
+ suggestionSize = 0;
+ undisplayedSuggestionCount = 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof SuggestionHeaderData)) {
+ return false;
+ }
+
+ SuggestionHeaderData targetData = (SuggestionHeaderData) obj;
+
+ return hasMoreSuggestions == targetData.hasMoreSuggestions
+ && suggestionSize == targetData.suggestionSize
+ && undisplayedSuggestionCount == targetData.undisplayedSuggestionCount;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java
new file mode 100644
index 00000000000..52c2103cd9d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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.android.settings.dashboard;
+
+import android.support.annotation.NonNull;
+import android.support.v7.util.DiffUtil;
+import android.support.v7.util.ListUpdateCallback;
+import com.android.settings.TestConfig;
+import com.android.settings.dashboard.conditional.AirplaneModeCondition;
+import com.android.settings.dashboard.conditional.Condition;
+import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.Tile;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DashboardDataTest {
+ private static final String TEST_SUGGESTION_TITLE = "Use fingerprint";
+ private static final String TEST_CATEGORY_TILE_TITLE = "Display";
+
+ private DashboardData mDashboardDataWithOneConditions;
+ private DashboardData mDashboardDataWithTwoConditions;
+ private DashboardData mDashboardDataWithNoItems;
+ @Mock
+ private Tile mTestCategoryTile;
+ @Mock
+ private Tile mTestSuggestion;
+ @Mock
+ private DashboardCategory mDashboardCategory;
+ @Mock
+ private Condition mTestCondition;
+ @Mock
+ private Condition mSecondCondition; // condition used to test insert in DiffUtil
+
+ @Before
+ public void SetUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // Build suggestions
+ final List suggestions = new ArrayList<>();
+ mTestSuggestion.title = TEST_SUGGESTION_TITLE;
+ suggestions.add(mTestSuggestion);
+
+ // Build oneItemConditions
+ final List oneItemConditions = new ArrayList<>();
+ when(mTestCondition.shouldShow()).thenReturn(true);
+ oneItemConditions.add(mTestCondition);
+
+ // Build twoItemConditions
+ final List twoItemsConditions = new ArrayList<>();
+ when(mSecondCondition.shouldShow()).thenReturn(true);
+ twoItemsConditions.add(mTestCondition);
+ twoItemsConditions.add(mSecondCondition);
+
+ // Build categories
+ final List categories = new ArrayList<>();
+ mTestCategoryTile.title = TEST_CATEGORY_TILE_TITLE;
+ mDashboardCategory.title = "test";
+ mDashboardCategory.tiles = new ArrayList<>();
+ mDashboardCategory.tiles.add(mTestCategoryTile);
+ categories.add(mDashboardCategory);
+
+ // Build DashboardData
+ mDashboardDataWithOneConditions = new DashboardData.Builder()
+ .setConditions(oneItemConditions)
+ .setCategories(categories)
+ .setSuggestions(suggestions)
+ .build();
+
+ mDashboardDataWithTwoConditions = new DashboardData.Builder()
+ .setConditions(twoItemsConditions)
+ .setCategories(categories)
+ .setSuggestions(suggestions)
+ .build();
+
+ mDashboardDataWithNoItems = new DashboardData.Builder()
+ .setConditions(null)
+ .setCategories(null)
+ .setSuggestions(null)
+ .build();
+ }
+
+ @Test
+ public void testBuildItemsData_containsAllData() {
+ final DashboardData.SuggestionHeaderData data =
+ new DashboardData.SuggestionHeaderData(false, 1, 0);
+ final Object[] expectedObjects = {mTestCondition, null, data, mTestSuggestion,
+ mDashboardCategory, mTestCategoryTile};
+ final int expectedSize = expectedObjects.length;
+
+ assertThat(mDashboardDataWithOneConditions.getItemList().size())
+ .isEqualTo(expectedSize);
+ for (int i = 0; i < expectedSize; i++) {
+ if (mDashboardDataWithOneConditions.getItemEntityByPosition(i)
+ instanceof DashboardData.SuggestionHeaderData) {
+ // SuggestionHeaderData is created inside when build, we can only use isEqualTo
+ assertThat(mDashboardDataWithOneConditions.getItemEntityByPosition(i))
+ .isEqualTo(expectedObjects[i]);
+ } else {
+ assertThat(mDashboardDataWithOneConditions.getItemEntityByPosition(i))
+ .isSameAs(expectedObjects[i]);
+ }
+ }
+ }
+
+ @Test
+ public void testGetPositionByEntity_selfInstance_returnPositionFound() {
+ final int position = mDashboardDataWithOneConditions
+ .getPositionByEntity(mTestCondition);
+ assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testGetPositionByEntity_notExisted_returnNotFound() {
+ final Condition condition = mock(AirplaneModeCondition.class);
+ final int position = mDashboardDataWithOneConditions.getPositionByEntity(condition);
+ assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testGetPositionByTile_selfInstance_returnPositionFound() {
+ final int position = mDashboardDataWithOneConditions
+ .getPositionByTile(mTestCategoryTile);
+ assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testGetPositionByTile_equalTitle_returnPositionFound() {
+ final Tile tile = mock(Tile.class);
+ tile.title = TEST_CATEGORY_TILE_TITLE;
+ final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
+ assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testGetPositionByTile_notExisted_returnNotFound() {
+ final Tile tile = mock(Tile.class);
+ tile.title = "";
+ final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
+ assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testDiffUtil_DataEqual_noResultData() {
+ List testResultData = new ArrayList<>();
+ testDiffUtil(mDashboardDataWithOneConditions,
+ mDashboardDataWithOneConditions, testResultData);
+ }
+
+ @Test
+ public void testDiffUtil_InsertOneCondition_ResultDataOneInserted() {
+ //Build testResultData
+ final List testResultData = new ArrayList<>();
+ testResultData.add(new ListUpdateResult.ResultData(
+ ListUpdateResult.ResultData.TYPE_OPERATION_INSERT, 1, 1));
+
+ testDiffUtil(mDashboardDataWithOneConditions,
+ mDashboardDataWithTwoConditions, testResultData);
+ }
+
+ @Test
+ public void testDiffUtil_DeleteAllData_ResultDataOneDeleted() {
+ //Build testResultData
+ final List testResultData = new ArrayList<>();
+ testResultData.add(new ListUpdateResult.ResultData(
+ ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 6));
+
+ testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
+ }
+
+ /**
+ * Test when using the
+ * {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
+ * to transfer List from {@paramref baseDashboardData} to {@paramref diffDashboardData}, whether
+ * the transform data result is equals to {@paramref testResultData}
+ *
+ * The steps are described below:
+ * 1. Calculate a {@link android.support.v7.util.DiffUtil.DiffResult} from
+ * {@paramref baseDashboardData} to {@paramref diffDashboardData}
+ *
+ * 2. Dispatch the {@link android.support.v7.util.DiffUtil.DiffResult} calculated from step 1
+ * into {@link ListUpdateResult}
+ *
+ * 3. Get result data(a.k.a. baseResultData) from {@link ListUpdateResult} and compare it to
+ * {@paramref testResultData}
+ *
+ * Because baseResultData and {@paramref testResultData} don't have sequence. When do the
+ * comparison, we will sort them first and then compare the inside data from them one by one.
+ *
+ * @param baseDashboardData
+ * @param diffDashboardData
+ * @param testResultData
+ */
+ private void testDiffUtil(DashboardData baseDashboardData, DashboardData diffDashboardData,
+ List testResultData) {
+ final DiffUtil.DiffResult diffUtilResult = DiffUtil.calculateDiff(
+ new DashboardData.ItemsDataDiffCallback(
+ baseDashboardData.getItemList(), diffDashboardData.getItemList()));
+
+ // Dispatch to listUpdateResult, then listUpdateResult will have result data
+ final ListUpdateResult listUpdateResult = new ListUpdateResult();
+ diffUtilResult.dispatchUpdatesTo(listUpdateResult);
+
+ final List baseResultData = listUpdateResult.getResultData();
+ assertThat(testResultData.size()).isEqualTo(baseResultData.size());
+
+ // Sort them so we can compare them one by one using a for loop
+ Collections.sort(baseResultData);
+ Collections.sort(testResultData);
+ final int size = baseResultData.size();
+ for (int i = 0; i < size; i++) {
+ // Refer to equals method in ResultData
+ assertThat(baseResultData.get(i)).isEqualTo(testResultData.get(i));
+ }
+ }
+
+ /**
+ * This class contains the result about how the changes made to convert one
+ * list to another list. It implements ListUpdateCallback to record the result data.
+ */
+ private static class ListUpdateResult implements ListUpdateCallback {
+ final private List mResultData;
+
+ public ListUpdateResult() {
+ mResultData = new ArrayList<>();
+ }
+
+ public List getResultData() {
+ return mResultData;
+ }
+
+ @Override
+ public void onInserted(int position, int count) {
+ mResultData.add(new ResultData(ResultData.TYPE_OPERATION_INSERT, position, count));
+ }
+
+ @Override
+ public void onRemoved(int position, int count) {
+ mResultData.add(new ResultData(ResultData.TYPE_OPERATION_REMOVE, position, count));
+ }
+
+ @Override
+ public void onMoved(int fromPosition, int toPosition) {
+ mResultData.add(
+ new ResultData(ResultData.TYPE_OPERATION_MOVE, fromPosition, toPosition));
+ }
+
+ @Override
+ public void onChanged(int position, int count, Object payload) {
+ mResultData.add(new ResultData(ResultData.TYPE_OPERATION_CHANGE, position, count));
+ }
+
+ /**
+ * This class contains general type and field to record the operation data generated
+ * in {@link ListUpdateCallback}. Please refer to {@link ListUpdateCallback} for more info.
+ *
+ * The following are examples about the data stored in this class:
+ *
+ * "The data starts from position(arg1) with count number(arg2) is changed(operation)"
+ * or "The data is moved(operation) from position1(arg1) to position2(arg2)"
+ */
+ private static class ResultData implements Comparable {
+ public static final int TYPE_OPERATION_INSERT = 0;
+ public static final int TYPE_OPERATION_REMOVE = 1;
+ public static final int TYPE_OPERATION_MOVE = 2;
+ public static final int TYPE_OPERATION_CHANGE = 3;
+
+ public final int operation;
+ public final int arg1;
+ public final int arg2;
+
+ public ResultData(int operation, int arg1, int arg2) {
+ this.operation = operation;
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof ResultData)) {
+ return false;
+ }
+
+ ResultData targetData = (ResultData) obj;
+
+ return operation == targetData.operation && arg1 == targetData.arg1
+ && arg2 == targetData.arg2;
+ }
+
+ @Override
+ public int compareTo(@NonNull ResultData resultData) {
+ if (this.operation != resultData.operation) {
+ return operation - resultData.operation;
+ }
+
+ if (arg1 != resultData.arg1) {
+ return arg1 - resultData.arg1;
+ }
+
+ return arg2 - resultData.arg2;
+ }
+ }
+ }
+}