/* * 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.tv.dvr.ui; import android.support.annotation.VisibleForTesting; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.PresenterSelector; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Keeps a set of items sorted * *

{@code T} must have stable IDs. */ public abstract class SortedArrayAdapter extends ArrayObjectAdapter { private final Comparator mComparator; private final int mMaxItemCount; private int mExtraItemCount; SortedArrayAdapter(PresenterSelector presenterSelector, Comparator comparator) { this(presenterSelector, comparator, Integer.MAX_VALUE); } SortedArrayAdapter(PresenterSelector presenterSelector, Comparator comparator, int maxItemCount) { super(presenterSelector); mComparator = comparator; mMaxItemCount = maxItemCount; } /** * Sets the objects in the given collection to the adapter keeping the elements sorted. * * @param items A {@link Collection} of items to be set. */ @VisibleForTesting final void setInitialItems(List items) { List itemsCopy = new ArrayList<>(items); Collections.sort(itemsCopy, mComparator); addAll(0, itemsCopy.subList(0, Math.min(mMaxItemCount, itemsCopy.size()))); } /** * Adds an item in sorted order to the adapter. * * @param item The item to add in sorted order to the adapter. */ @Override public final void add(Object item) { add((T) item, false); } public boolean isEmpty() { return size() == 0; } /** * Adds an item in sorted order to the adapter. * * @param item The item to add in sorted order to the adapter. * @param insertToEnd If items are inserted in a more or less sorted fashion, * sets this parameter to {@code true} to search insertion position from * the end to save search time. */ public final void add(T item, boolean insertToEnd) { int i; if (insertToEnd) { i = findInsertPosition(item); } else { i = findInsertPositionBinary(item); } super.add(i, item); if (size() > mMaxItemCount + mExtraItemCount) { removeItems(mMaxItemCount, size() - mMaxItemCount - mExtraItemCount); } } /** * Adds an extra item to the end of the adapter. The items will not be subjected to the sorted * order or the maximum number of items. One or more extra items can be added to the adapter. * They will be presented in their insertion order. */ public int addExtraItem(T item) { super.add(item); return ++mExtraItemCount; } /** * Removes an item which has the same ID as {@code item}. */ public boolean removeWithId(T item) { int index = indexWithTypeAndId(item); return index >= 0 && index < size() && remove(get(index)); } /** * Change an item in the list. * @param item The item to change. */ public final void change(T item) { int oldIndex = indexWithTypeAndId(item); if (oldIndex != -1) { T old = (T) get(oldIndex); if (mComparator.compare(old, item) == 0) { replace(oldIndex, item); return; } removeItems(oldIndex, 1); } add(item); } /** * Returns the id of the the given {@code item}, which will be used in {@link #change} to * decide if the given item is already existed in the adapter. * * The id must be stable. */ abstract long getId(T item); private int indexWithTypeAndId(T item) { long id = getId(item); for (int i = 0; i < size() - mExtraItemCount; i++) { T r = (T) get(i); if (r.getClass() == item.getClass() && getId(r) == id) { return i; } } return -1; } /** * Finds the position that the given item should be inserted to keep the sorted order. */ public int findInsertPosition(T item) { for (int i = size() - mExtraItemCount - 1; i >=0; i--) { T r = (T) get(i); if (mComparator.compare(r, item) <= 0) { return i + 1; } } return 0; } private int findInsertPositionBinary(T item) { int lb = 0; int ub = size() - mExtraItemCount - 1; while (lb <= ub) { int mid = (lb + ub) / 2; T r = (T) get(mid); int compareResult = mComparator.compare(item, r); if (compareResult == 0) { return mid; } else if (compareResult > 0) { lb = mid + 1; } else { ub = mid - 1; } } return lb; } }