diff options
author | Leon Scroggins <scroggo@google.com> | 2010-01-13 13:43:35 -0500 |
---|---|---|
committer | Leon Scroggins <scroggo@google.com> | 2010-01-13 15:07:53 -0500 |
commit | 2483745674f95f60b8c3b8c9e817f2df1776a0b5 (patch) | |
tree | fbf330b990834b40031103302b0daf3df6e14966 | |
parent | a3935ac40ac3767b13c92b192a0eb69d837a4093 (diff) | |
download | packages_apps_Browser-2483745674f95f60b8c3b8c9e817f2df1776a0b5.zip packages_apps_Browser-2483745674f95f60b8c3b8c9e817f2df1776a0b5.tar.gz packages_apps_Browser-2483745674f95f60b8c3b8c9e817f2df1776a0b5.tar.bz2 |
Show the downloads list divided into groups based on how recently downloaded.
Fixes http://b/issue?id=2367226
-rw-r--r-- | res/layout/browser_downloads_page.xml | 4 | ||||
-rw-r--r-- | src/com/android/browser/BrowserDownloadAdapter.java | 74 | ||||
-rw-r--r-- | src/com/android/browser/BrowserDownloadPage.java | 150 | ||||
-rw-r--r-- | src/com/android/browser/BrowserHistoryPage.java | 17 | ||||
-rw-r--r-- | src/com/android/browser/DateSortedExpandableListAdapter.java | 83 |
5 files changed, 194 insertions, 134 deletions
diff --git a/res/layout/browser_downloads_page.xml b/res/layout/browser_downloads_page.xml index 1d4d4e6..06c27c6 100644 --- a/res/layout/browser_downloads_page.xml +++ b/res/layout/browser_downloads_page.xml @@ -19,8 +19,8 @@ */ --> <merge xmlns:android="http://schemas.android.com/apk/res/android"> - <ListView - android:id="@+id/list" + <ExpandableListView + android:id="@android:id/list" android:layout_width="fill_parent" android:layout_height="fill_parent"/> <ViewStub diff --git a/src/com/android/browser/BrowserDownloadAdapter.java b/src/com/android/browser/BrowserDownloadAdapter.java index 16cb982..85539c3 100644 --- a/src/com/android/browser/BrowserDownloadAdapter.java +++ b/src/com/android/browser/BrowserDownloadAdapter.java @@ -30,10 +30,12 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.provider.Downloads; import android.text.format.Formatter; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; -import android.widget.ResourceCursorAdapter; +import android.widget.RelativeLayout; import android.widget.TextView; import java.io.File; @@ -46,7 +48,7 @@ import java.util.List; * real work done by this class is to construct a custom view for the line * items. */ -public class BrowserDownloadAdapter extends ResourceCursorAdapter { +public class BrowserDownloadAdapter extends DateSortedExpandableListAdapter { private int mFilenameColumnId; private int mTitleColumnId; @@ -57,8 +59,8 @@ public class BrowserDownloadAdapter extends ResourceCursorAdapter { private int mMimetypeColumnId; private int mDateColumnId; - public BrowserDownloadAdapter(Context context, int layout, Cursor c) { - super(context, layout, c); + public BrowserDownloadAdapter(Context context, Cursor c, int index) { + super(context, c, index); mFilenameColumnId = c.getColumnIndexOrThrow(Downloads._DATA); mTitleColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_TITLE); mDescColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_DESCRIPTION); @@ -71,12 +73,26 @@ public class BrowserDownloadAdapter extends ResourceCursorAdapter { } @Override - public void bindView(View view, Context context, Cursor cursor) { + public View getChildView(int groupPosition, int childPosition, + boolean isLastChild, View convertView, ViewGroup parent) { + Context context = getContext(); + // The layout file uses a RelativeLayout, whereas the GroupViews use + // TextView. + if (null == convertView || !(convertView instanceof RelativeLayout)) { + convertView = LayoutInflater.from(context).inflate( + R.layout.browser_download_item, null); + } + + // Bail early if the Cursor is closed. + if (!moveCursorToChildPosition(groupPosition, childPosition)) { + return convertView; + } + Resources r = context.getResources(); // Retrieve the icon for this download - String mimeType = cursor.getString(mMimetypeColumnId); - ImageView iv = (ImageView) view.findViewById(R.id.download_icon); + String mimeType = getString(mMimetypeColumnId); + ImageView iv = (ImageView) convertView.findViewById(R.id.download_icon); if (DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mimeType)) { iv.setImageResource(R.drawable.ic_launcher_drm_file); } else if (mimeType == null) { @@ -96,10 +112,10 @@ public class BrowserDownloadAdapter extends ResourceCursorAdapter { } } - TextView tv = (TextView) view.findViewById(R.id.download_title); - String title = cursor.getString(mTitleColumnId); + TextView tv = (TextView) convertView.findViewById(R.id.download_title); + String title = getString(mTitleColumnId); if (title == null) { - String fullFilename = cursor.getString(mFilenameColumnId); + String fullFilename = getString(mFilenameColumnId); if (fullFilename == null) { title = r.getString(R.string.download_unknown_filename); } else { @@ -110,51 +126,51 @@ public class BrowserDownloadAdapter extends ResourceCursorAdapter { // assume "_id" is the first column for the cursor context.getContentResolver().update( ContentUris.withAppendedId(Downloads.CONTENT_URI, - cursor.getLong(0)), values, null, null); + getLong(0)), values, null, null); } } tv.setText(title); - tv = (TextView) view.findViewById(R.id.domain); - tv.setText(cursor.getString(mDescColumnId)); + tv = (TextView) convertView.findViewById(R.id.domain); + tv.setText(getString(mDescColumnId)); - long totalBytes = cursor.getLong(mTotalBytesColumnId); + long totalBytes = getLong(mTotalBytesColumnId); - int status = cursor.getInt(mStatusColumnId); + int status = getInt(mStatusColumnId); if (Downloads.isStatusCompleted(status)) { // Download stopped - View v = view.findViewById(R.id.progress_text); + View v = convertView.findViewById(R.id.progress_text); v.setVisibility(View.GONE); - v = view.findViewById(R.id.download_progress); + v = convertView.findViewById(R.id.download_progress); v.setVisibility(View.GONE); - tv = (TextView) view.findViewById(R.id.complete_text); + tv = (TextView) convertView.findViewById(R.id.complete_text); tv.setVisibility(View.VISIBLE); if (Downloads.isStatusError(status)) { tv.setText(getErrorText(status)); } else { tv.setText(r.getString(R.string.download_success, - Formatter.formatFileSize(mContext, totalBytes))); + Formatter.formatFileSize(context, totalBytes))); } - long time = cursor.getLong(mDateColumnId); + long time = getLong(mDateColumnId); Date d = new Date(time); DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); - tv = (TextView) view.findViewById(R.id.complete_date); + tv = (TextView) convertView.findViewById(R.id.complete_date); tv.setVisibility(View.VISIBLE); tv.setText(df.format(d)); } else { // Download is still running - tv = (TextView) view.findViewById(R.id.progress_text); + tv = (TextView) convertView.findViewById(R.id.progress_text); tv.setVisibility(View.VISIBLE); - View progress = view.findViewById(R.id.download_progress); + View progress = convertView.findViewById(R.id.download_progress); progress.setVisibility(View.VISIBLE); - View v = view.findViewById(R.id.complete_date); + View v = convertView.findViewById(R.id.complete_date); v.setVisibility(View.GONE); - v = view.findViewById(R.id.complete_text); + v = convertView.findViewById(R.id.complete_text); v.setVisibility(View.GONE); if (status == Downloads.STATUS_PENDING) { @@ -171,14 +187,14 @@ public class BrowserDownloadAdapter extends ResourceCursorAdapter { sb.append(r.getText(R.string.download_running_paused)); } if (totalBytes > 0) { - long currentBytes = cursor.getLong(mCurrentBytesColumnId); + long currentBytes = getLong(mCurrentBytesColumnId); int progressAmount = (int)(currentBytes * 100 / totalBytes); sb.append(' '); sb.append(progressAmount); sb.append("% ("); - sb.append(Formatter.formatFileSize(mContext, currentBytes)); + sb.append(Formatter.formatFileSize(context, currentBytes)); sb.append("/"); - sb.append(Formatter.formatFileSize(mContext, totalBytes)); + sb.append(Formatter.formatFileSize(context, totalBytes)); sb.append(")"); pb.setIndeterminate(false); pb.setProgress(progressAmount); @@ -188,7 +204,7 @@ public class BrowserDownloadAdapter extends ResourceCursorAdapter { tv.setText(sb.toString()); } } - + return convertView; } /** diff --git a/src/com/android/browser/BrowserDownloadPage.java b/src/com/android/browser/BrowserDownloadPage.java index 22e0e65..3a41afd 100644 --- a/src/com/android/browser/BrowserDownloadPage.java +++ b/src/com/android/browser/BrowserDownloadPage.java @@ -16,8 +16,8 @@ package com.android.browser; -import android.app.Activity; import android.app.AlertDialog; +import android.app.ExpandableListActivity; import android.content.ActivityNotFoundException; import android.content.ContentValues; import android.content.DialogInterface; @@ -38,8 +38,7 @@ import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.AdapterView; -import android.widget.ListView; -import android.widget.AdapterView.OnItemClickListener; +import android.widget.ExpandableListView; import java.io.File; import java.util.List; @@ -47,16 +46,15 @@ import java.util.List; /** * View showing the user's current browser downloads */ -public class BrowserDownloadPage extends Activity - implements View.OnCreateContextMenuListener, OnItemClickListener { +public class BrowserDownloadPage extends ExpandableListActivity { - private ListView mListView; + private ExpandableListView mListView; private Cursor mDownloadCursor; private BrowserDownloadAdapter mDownloadAdapter; private int mStatusColumnId; private int mIdColumnId; private int mTitleColumnId; - private int mContextMenuPosition; + private long mContextMenuPosition; @Override public void onCreate(Bundle icicle) { @@ -65,16 +63,15 @@ public class BrowserDownloadPage extends Activity setTitle(getText(R.string.download_title)); - mListView = (ListView) findViewById(R.id.list); + mListView = (ExpandableListView) findViewById(android.R.id.list); mListView.setEmptyView(findViewById(R.id.empty)); - mDownloadCursor = managedQuery(Downloads.CONTENT_URI, new String [] {"_id", Downloads.COLUMN_TITLE, Downloads.COLUMN_STATUS, Downloads.COLUMN_TOTAL_BYTES, Downloads.COLUMN_CURRENT_BYTES, Downloads._DATA, Downloads.COLUMN_DESCRIPTION, Downloads.COLUMN_MIME_TYPE, Downloads.COLUMN_LAST_MODIFICATION, Downloads.COLUMN_VISIBILITY}, - null, null); + null, Downloads.COLUMN_LAST_MODIFICATION + " DESC"); // only attach everything to the listbox if we can access // the download database. Otherwise, just show it empty @@ -88,20 +85,24 @@ public class BrowserDownloadPage extends Activity // Create a list "controller" for the data mDownloadAdapter = new BrowserDownloadAdapter(this, - R.layout.browser_download_item, mDownloadCursor); + mDownloadCursor, mDownloadCursor.getColumnIndexOrThrow( + Downloads.COLUMN_LAST_MODIFICATION)); - mListView.setAdapter(mDownloadAdapter); + setListAdapter(mDownloadAdapter); mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET); mListView.setOnCreateContextMenuListener(this); - mListView.setOnItemClickListener(this); - + Intent intent = getIntent(); - if (intent != null && intent.getData() != null) { - int position = checkStatus( - ContentUris.parseId(intent.getData())); - if (position >= 0) { - mListView.setSelection(position); - } + final int groupToShow = intent == null || intent.getData() == null + ? 0 : checkStatus(ContentUris.parseId(intent.getData())); + if (mDownloadAdapter.getGroupCount() > groupToShow) { + mListView.post(new Runnable() { + public void run() { + if (mDownloadAdapter.getGroupCount() > groupToShow) { + mListView.expandGroup(groupToShow); + } + } + }); } } } @@ -141,7 +142,10 @@ public class BrowserDownloadPage extends Activity @Override public boolean onContextItemSelected(MenuItem item) { - mDownloadCursor.moveToPosition(mContextMenuPosition); + if (!mDownloadAdapter.moveCursorToPackedChildPosition( + mContextMenuPosition)) { + return false; + } switch (item.getItemId()) { case R.id.download_menu_open: hideCompletedDownload(); @@ -162,10 +166,15 @@ public class BrowserDownloadPage extends Activity public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { if (mDownloadCursor != null) { - AdapterView.AdapterContextMenuInfo info = - (AdapterView.AdapterContextMenuInfo) menuInfo; - mDownloadCursor.moveToPosition(info.position); - mContextMenuPosition = info.position; + ExpandableListView.ExpandableListContextMenuInfo info + = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo; + long packedPosition = info.packedPosition; + // Only show a context menu for the child views + if (!mDownloadAdapter.moveCursorToPackedChildPosition( + packedPosition)) { + return; + } + mContextMenuPosition = packedPosition; menu.setHeaderTitle(mDownloadCursor.getString(mTitleColumnId)); MenuInflater inflater = getMenuInflater(); @@ -178,60 +187,50 @@ public class BrowserDownloadPage extends Activity inflater.inflate(R.menu.downloadhistorycontextrunning, menu); } } + super.onCreateContextMenu(menu, v, menuInfo); } /** * This function is called to check the status of the download and if it * has an error show an error dialog. * @param id Row id of the download to check - * @return position of item + * @return Group which contains the download */ - int checkStatus(final long id) { - int position = -1; - for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast(); - mDownloadCursor.moveToNext()) { - if (id == mDownloadCursor.getLong(mIdColumnId)) { - position = mDownloadCursor.getPosition(); - break; - } - + private int checkStatus(final long id) { + int groupToShow = mDownloadAdapter.groupFromChildId(id); + if (-1 == groupToShow) return 0; + int status = mDownloadCursor.getInt(mStatusColumnId); + if (!Downloads.isStatusError(status)) { + return groupToShow; } - if (!mDownloadCursor.isAfterLast()) { - int status = mDownloadCursor.getInt(mStatusColumnId); - if (!Downloads.isStatusError(status)) { - return position; - } - - if (status == Downloads.STATUS_FILE_ERROR) { - String title = mDownloadCursor.getString(mTitleColumnId); - if (title == null || title.length() == 0) { - title = getString(R.string.download_unknown_filename); - } - String msg = getString(R.string.download_file_error_dlg_msg, - title); - new AlertDialog.Builder(this) - .setTitle(R.string.download_file_error_dlg_title) - .setIcon(android.R.drawable.ic_popup_disk_full) - .setMessage(msg) - .setPositiveButton(R.string.ok, null) - .setNegativeButton(R.string.retry, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int whichButton) { - resumeDownload(id); - } - }) - .show(); - } else { - new AlertDialog.Builder(this) - .setTitle(R.string.download_failed_generic_dlg_title) - .setIcon(R.drawable.ssl_icon) - .setMessage(BrowserDownloadAdapter.getErrorText(status)) - .setPositiveButton(R.string.ok, null) - .show(); + if (status == Downloads.STATUS_FILE_ERROR) { + String title = mDownloadCursor.getString(mTitleColumnId); + if (title == null || title.length() == 0) { + title = getString(R.string.download_unknown_filename); } + String msg = getString(R.string.download_file_error_dlg_msg, title); + new AlertDialog.Builder(this) + .setTitle(R.string.download_file_error_dlg_title) + .setIcon(android.R.drawable.ic_popup_disk_full) + .setMessage(msg) + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.retry, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + resumeDownload(id); + } + }) + .show(); + } else { + new AlertDialog.Builder(this) + .setTitle(R.string.download_failed_generic_dlg_title) + .setIcon(R.drawable.ssl_icon) + .setMessage(BrowserDownloadAdapter.getErrorText(status)) + .setPositiveButton(R.string.ok, null) + .show(); } - return position; + return groupToShow; } /** @@ -421,14 +420,12 @@ public class BrowserDownloadPage extends Activity } } - /* - * (non-Javadoc) - * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long) - */ - public void onItemClick(AdapterView parent, View view, int position, - long id) { + @Override + public boolean onChildClick(ExpandableListView parent, View v, + int groupPosition, int childPosition, long id) { // Open the selected item - mDownloadCursor.moveToPosition(position); + mDownloadAdapter.moveCursorToChildPosition(groupPosition, + childPosition); hideCompletedDownload(); @@ -440,6 +437,7 @@ public class BrowserDownloadPage extends Activity // Check to see if there is an error. checkStatus(id); } + return true; } /** diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java index 5818e1d..d23137e 100644 --- a/src/com/android/browser/BrowserHistoryPage.java +++ b/src/com/android/browser/BrowserHistoryPage.java @@ -32,7 +32,6 @@ import android.text.IClipboard; import android.util.Log; import android.view.ContextMenu; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -46,7 +45,6 @@ import android.widget.AdapterView; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.ExpandableListView.ExpandableListContextMenuInfo; -import android.widget.TextView; import android.widget.Toast; /** @@ -336,20 +334,5 @@ public class BrowserHistoryPage extends ExpandableListActivity { getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX)); return item; } - - public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { - TextView item; - if (null == convertView || !(convertView instanceof TextView)) { - LayoutInflater factory = - LayoutInflater.from(BrowserHistoryPage.this); - item = (TextView) - factory.inflate(R.layout.history_header, null); - } else { - item = (TextView) convertView; - } - item.setText(getGroupLabel(groupPosition)); - return item; - } - } } diff --git a/src/com/android/browser/DateSortedExpandableListAdapter.java b/src/com/android/browser/DateSortedExpandableListAdapter.java index 05d16bd..1d04493 100644 --- a/src/com/android/browser/DateSortedExpandableListAdapter.java +++ b/src/com/android/browser/DateSortedExpandableListAdapter.java @@ -21,10 +21,14 @@ import android.database.ContentObserver; import android.database.Cursor; import android.database.DataSetObserver; import android.os.Handler; +import android.provider.BaseColumns; +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; @@ -43,6 +47,8 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { private Cursor mCursor; private DateSorter mDateSorter; private int mDateIndex; + private int mIdIndex; + private Context mContext; private class ChangeObserver extends ContentObserver { public ChangeObserver() { @@ -62,9 +68,11 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { public DateSortedExpandableListAdapter(Context context, Cursor cursor, int dateIndex) { + mContext = context; mDateSorter = new DateSorter(context); mObservers = new Vector<DataSetObserver>(); mCursor = cursor; + mIdIndex = cursor.getColumnIndexOrThrow(BaseColumns._ID); cursor.registerContentObserver(new ChangeObserver()); mDateIndex = dateIndex; buildMap(); @@ -85,7 +93,7 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { int dateIndex = -1; if (mCursor.moveToFirst() && mCursor.getCount() > 0) { while (!mCursor.isAfterLast()) { - long date = mCursor.getLong(mDateIndex); + long date = getLong(mDateIndex); int index = mDateSorter.getIndex(date); if (index > dateIndex) { mNumberOfBins++; @@ -117,6 +125,10 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { 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 @@ -130,12 +142,11 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { } /** - * Get the label for a group, as specified by the ExpandableList - * @param groupPosition Position in the ExpandableList's set of groups - * @return String label for the corresponding bin. + * Get the long at cursorIndex from the Cursor. Assumes the Cursor has + * already been moved to the correct position. */ - /* package */ String getGroupLabel(int groupPosition) { - return mDateSorter.getLabel(groupPositionToBin(groupPosition)); + /* package */ long getLong(int cursorIndex) { + return mCursor.getLong(cursorIndex); } /** @@ -151,6 +162,30 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { } /** + * 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 (mDateSorter.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. @@ -181,6 +216,23 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { } /** + * 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. @@ -195,8 +247,7 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { for (int i = 0; i < groupPosition; i++) { index += mItemMap[i]; } - mCursor.moveToPosition(index); - return true; + return mCursor.moveToPosition(index); } /* package */ void refreshData() { @@ -212,7 +263,16 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { - return null; + TextView item; + if (null == convertView || !(convertView instanceof TextView)) { + LayoutInflater factory = LayoutInflater.from(mContext); + item = (TextView) factory.inflate(R.layout.history_header, null); + } else { + item = (TextView) convertView; + } + String label = mDateSorter.getLabel(groupPositionToBin(groupPosition)); + item.setText(label); + return item; } public View getChildView(int groupPosition, int childPosition, @@ -249,7 +309,10 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { } public long getChildId(int groupPosition, int childPosition) { - return (childPosition << 3) + groupPosition; + if (moveCursorToChildPosition(groupPosition, childPosition)) { + return getLong(mIdIndex); + } + return 0; } public boolean hasStableIds() { |