diff options
Diffstat (limited to 'ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java')
-rw-r--r-- | ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java b/ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java new file mode 100644 index 00000000..88ffdee3 --- /dev/null +++ b/ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2010 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.providers.downloads.ui; + +import android.content.Context; +import android.database.ContentObserver; +import android.database.Cursor; +import android.database.DataSetObserver; +import android.net.DownloadManager; +import android.os.Handler; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.DateSorter; +import android.widget.ExpandableListAdapter; +import android.widget.ExpandableListView; +import android.widget.TextView; + +import java.util.Vector; + +/** + * ExpandableListAdapter which separates data into categories based on date. Copied from + * packages/apps/Browser. + */ +public class DateSortedExpandableListAdapter implements ExpandableListAdapter { + // Array for each of our bins. Each entry represents how many items are + // in that bin. + private int mItemMap[]; + // This is our GroupCount. We will have at most DateSorter.DAY_COUNT + // bins, less if the user has no items in one or more bins. + private int mNumberOfBins; + private Vector<DataSetObserver> mObservers; + private Cursor mCursor; + private DateSorter mDateSorter; + private int mDateIndex; + private int mIdIndex; + private Context mContext; + + private class ChangeObserver extends ContentObserver { + public ChangeObserver() { + super(new Handler()); + } + + @Override + public boolean deliverSelfNotifications() { + return true; + } + + @Override + public void onChange(boolean selfChange) { + refreshData(); + } + } + + public DateSortedExpandableListAdapter(Context context, Cursor cursor, + int dateIndex) { + mContext = context; + mDateSorter = new DateSorter(context); + mObservers = new Vector<DataSetObserver>(); + mCursor = cursor; + mIdIndex = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); + cursor.registerContentObserver(new ChangeObserver()); + mDateIndex = dateIndex; + buildMap(); + } + + /** + * Set up the bins for determining which items belong to which groups. + */ + private void buildMap() { + // The cursor is sorted by date + // The ItemMap will store the number of items in each bin. + int array[] = new int[DateSorter.DAY_COUNT]; + // Zero out the array. + for (int j = 0; j < DateSorter.DAY_COUNT; j++) { + array[j] = 0; + } + mNumberOfBins = 0; + int dateIndex = -1; + if (mCursor.moveToFirst() && mCursor.getCount() > 0) { + while (!mCursor.isAfterLast()) { + long date = getLong(mDateIndex); + int index = mDateSorter.getIndex(date); + if (index > dateIndex) { + mNumberOfBins++; + if (index == DateSorter.DAY_COUNT - 1) { + // We are already in the last bin, so it will + // include all the remaining items + array[index] = mCursor.getCount() + - mCursor.getPosition(); + break; + } + dateIndex = index; + } + array[dateIndex]++; + mCursor.moveToNext(); + } + } + mItemMap = array; + } + + /** + * Get the byte array at cursorIndex from the Cursor. Assumes the Cursor + * has already been moved to the correct position. Along with + * {@link #getInt} and {@link #getString}, these are provided so the client + * does not need to access the Cursor directly + * @param cursorIndex Index to query the Cursor. + * @return corresponding byte array from the Cursor. + */ + /* package */ byte[] getBlob(int cursorIndex) { + return mCursor.getBlob(cursorIndex); + } + + /* package */ Context getContext() { + return mContext; + } + + /** + * Get the integer at cursorIndex from the Cursor. Assumes the Cursor has + * already been moved to the correct position. Along with + * {@link #getBlob} and {@link #getString}, these are provided so the client + * does not need to access the Cursor directly + * @param cursorIndex Index to query the Cursor. + * @return corresponding integer from the Cursor. + */ + /* package */ int getInt(int cursorIndex) { + return mCursor.getInt(cursorIndex); + } + + /** + * Get the long at cursorIndex from the Cursor. Assumes the Cursor has + * already been moved to the correct position. + */ + /* package */ long getLong(int cursorIndex) { + return mCursor.getLong(cursorIndex); + } + + /** + * Get the String at cursorIndex from the Cursor. Assumes the Cursor has + * already been moved to the correct position. Along with + * {@link #getInt} and {@link #getInt}, these are provided so the client + * does not need to access the Cursor directly + * @param cursorIndex Index to query the Cursor. + * @return corresponding String from the Cursor. + */ + /* package */ String getString(int cursorIndex) { + return mCursor.getString(cursorIndex); + } + + /** + * Determine which group an item belongs to. + * @param childId ID of the child view in question. + * @return int Group position of the containing group. + /* package */ int groupFromChildId(long childId) { + int group = -1; + for (mCursor.moveToFirst(); !mCursor.isAfterLast(); + mCursor.moveToNext()) { + if (getLong(mIdIndex) == childId) { + int bin = mDateSorter.getIndex(getLong(mDateIndex)); + // bin is the same as the group if the number of bins is the + // same as DateSorter + if (DateSorter.DAY_COUNT == mNumberOfBins) return bin; + // There are some empty bins. Find the corresponding group. + group = 0; + for (int i = 0; i < bin; i++) { + if (mItemMap[i] != 0) group++; + } + break; + } + } + return group; + } + + /** + * Translates from a group position in the ExpandableList to a bin. This is + * necessary because some groups have no history items, so we do not include + * those in the ExpandableList. + * @param groupPosition Position in the ExpandableList's set of groups + * @return The corresponding bin that holds that group. + */ + private int groupPositionToBin(int groupPosition) { + if (groupPosition < 0 || groupPosition >= DateSorter.DAY_COUNT) { + throw new AssertionError("group position out of range"); + } + if (DateSorter.DAY_COUNT == mNumberOfBins || 0 == mNumberOfBins) { + // In the first case, we have exactly the same number of bins + // as our maximum possible, so there is no need to do a + // conversion + // The second statement is in case this method gets called when + // the array is empty, in which case the provided groupPosition + // will do fine. + return groupPosition; + } + int arrayPosition = -1; + while (groupPosition > -1) { + arrayPosition++; + if (mItemMap[arrayPosition] != 0) { + groupPosition--; + } + } + return arrayPosition; + } + + /** + * Move the cursor to the position indicated. + * @param packedPosition Position in packed position representation. + * @return True on success, false otherwise. + */ + boolean moveCursorToPackedChildPosition(long packedPosition) { + if (ExpandableListView.getPackedPositionType(packedPosition) != + ExpandableListView.PACKED_POSITION_TYPE_CHILD) { + return false; + } + int groupPosition = ExpandableListView.getPackedPositionGroup( + packedPosition); + int childPosition = ExpandableListView.getPackedPositionChild( + packedPosition); + return moveCursorToChildPosition(groupPosition, childPosition); + } + + /** + * Move the cursor the the position indicated. + * @param groupPosition Index of the group containing the desired item. + * @param childPosition Index of the item within the specified group. + * @return boolean False if the cursor is closed, so the Cursor was not + * moved. True on success. + */ + /* package */ boolean moveCursorToChildPosition(int groupPosition, + int childPosition) { + if (mCursor.isClosed()) return false; + groupPosition = groupPositionToBin(groupPosition); + int index = childPosition; + for (int i = 0; i < groupPosition; i++) { + index += mItemMap[i]; + } + return mCursor.moveToPosition(index); + } + + /* package */ void refreshData() { + if (mCursor.isClosed()) { + return; + } + mCursor.requery(); + buildMap(); + for (DataSetObserver o : mObservers) { + o.onChanged(); + } + } + + public View getGroupView(int groupPosition, boolean isExpanded, + View convertView, ViewGroup parent) { + TextView item; + if (null == convertView || !(convertView instanceof TextView)) { + LayoutInflater factory = LayoutInflater.from(mContext); + item = (TextView) factory.inflate(R.layout.list_group_header, null); + } else { + item = (TextView) convertView; + } + String label = mDateSorter.getLabel(groupPositionToBin(groupPosition)); + item.setText(label); + return item; + } + + public View getChildView(int groupPosition, int childPosition, + boolean isLastChild, View convertView, ViewGroup parent) { + return null; + } + + public boolean areAllItemsEnabled() { + return true; + } + + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + + public int getGroupCount() { + return mNumberOfBins; + } + + public int getChildrenCount(int groupPosition) { + return mItemMap[groupPositionToBin(groupPosition)]; + } + + public Object getGroup(int groupPosition) { + return null; + } + + public Object getChild(int groupPosition, int childPosition) { + return null; + } + + public long getGroupId(int groupPosition) { + return groupPosition; + } + + public long getChildId(int groupPosition, int childPosition) { + if (moveCursorToChildPosition(groupPosition, childPosition)) { + return getLong(mIdIndex); + } + return 0; + } + + public boolean hasStableIds() { + return true; + } + + public void registerDataSetObserver(DataSetObserver observer) { + mObservers.add(observer); + } + + public void unregisterDataSetObserver(DataSetObserver observer) { + mObservers.remove(observer); + } + + public void onGroupExpanded(int groupPosition) { + } + + public void onGroupCollapsed(int groupPosition) { + } + + public long getCombinedChildId(long groupId, long childId) { + return childId; + } + + public long getCombinedGroupId(long groupId) { + return groupId; + } + + public boolean isEmpty() { + return mCursor.isClosed() || mCursor.getCount() == 0; + } +} |