summaryrefslogtreecommitdiffstats
path: root/ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java')
-rw-r--r--ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java347
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;
+ }
+}