From 4cb25d93a23d086b954c03cb72afb2cf0248f1c4 Mon Sep 17 00:00:00 2001 From: satok Date: Mon, 16 Nov 2009 18:33:13 +0900 Subject: [PATCH] Save the state of checkboxes when orientation is changed BUG: 2239516 * Add tests for save/restore Instance State --- .../android/email/activity/MessageList.java | 20 +- .../email/activity/MessageListUnitTests.java | 252 ++++++++++++++++++ 2 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 tests/src/com/android/email/activity/MessageListUnitTests.java diff --git a/src/com/android/email/activity/MessageList.java b/src/com/android/email/activity/MessageList.java index d1103e6b7..0919fb7a3 100644 --- a/src/com/android/email/activity/MessageList.java +++ b/src/com/android/email/activity/MessageList.java @@ -86,6 +86,8 @@ public class MessageList extends ListActivity implements OnItemClickListener, On "com.android.email.activity.MessageList.selectedItemTop"; private static final String STATE_SELECTED_POSITION = "com.android.email.activity.MessageList.selectedPosition"; + private static final String STATE_CHECKED_ITEMS = + "com.android.email.activity.MessageList.checkedItems"; // UI support private ListView mListView; @@ -354,6 +356,14 @@ public class MessageList extends ListActivity implements OnItemClickListener, On saveListPosition(); outState.putInt(STATE_SELECTED_POSITION, mSavedItemPosition); outState.putInt(STATE_SELECTED_ITEM_TOP, mSavedItemTop); + Set checkedset = mListAdapter.getSelectedSet(); + long[] checkedarray = new long[checkedset.size()]; + int i = 0; + for (Long l : checkedset) { + checkedarray[i] = l; + i++; + } + outState.putLongArray(STATE_CHECKED_ITEMS, checkedarray); } @Override @@ -361,11 +371,15 @@ public class MessageList extends ListActivity implements OnItemClickListener, On super.onRestoreInstanceState(savedInstanceState); mSavedItemTop = savedInstanceState.getInt(STATE_SELECTED_ITEM_TOP, 0); mSavedItemPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION, -1); + Set checkedset = mListAdapter.getSelectedSet(); + for (long l: savedInstanceState.getLongArray(STATE_CHECKED_ITEMS)) { + checkedset.add(l); + } } private void saveListPosition() { mSavedItemPosition = getListView().getSelectedItemPosition(); - if (mSavedItemPosition >= 0) { + if (mSavedItemPosition >= 0 && getListView().isSelected()) { mSavedItemTop = getListView().getSelectedView().getTop(); } else { mSavedItemPosition = getListView().getFirstVisiblePosition(); @@ -1411,7 +1425,7 @@ public class MessageList extends ListActivity implements OnItemClickListener, On /** * This class implements the adapter for displaying messages based on cursors. */ - /* package */ class MessageListAdapter extends CursorAdapter { + /* package */ public class MessageListAdapter extends CursorAdapter { public static final int COLUMN_ID = 0; public static final int COLUMN_MAILBOX_KEY = 1; @@ -1449,7 +1463,6 @@ public class MessageList extends ListActivity implements OnItemClickListener, On private static final long REFRESH_INTERVAL_MS = 2500; private java.text.DateFormat mDateFormat; - private java.text.DateFormat mDayFormat; private java.text.DateFormat mTimeFormat; private HashSet mChecked = new HashSet(); @@ -1474,7 +1487,6 @@ public class MessageList extends ListActivity implements OnItemClickListener, On mTextColorSecondary = resources.getColorStateList(array.getResourceId(0, 0)); mDateFormat = android.text.format.DateFormat.getDateFormat(context); // short date - mDayFormat = android.text.format.DateFormat.getDateFormat(context); // TODO: day mTimeFormat = android.text.format.DateFormat.getTimeFormat(context); // 12/24 time } diff --git a/tests/src/com/android/email/activity/MessageListUnitTests.java b/tests/src/com/android/email/activity/MessageListUnitTests.java new file mode 100644 index 000000000..c50deb5da --- /dev/null +++ b/tests/src/com/android/email/activity/MessageListUnitTests.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2009 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.email.activity; + +import com.android.email.Email; +import com.android.email.R; +import com.android.email.provider.EmailContent; +import com.android.email.provider.EmailContent.MessageColumns; + +import android.app.Application; +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.database.CursorIndexOutOfBoundsException; +import android.database.AbstractCursor; +import android.database.SQLException; +import android.os.Bundle; +import android.test.ActivityInstrumentationTestCase2; +import android.test.UiThreadTest; +import android.test.suitebuilder.annotation.LargeTest; +import android.widget.ListAdapter; +import android.widget.CursorAdapter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Various instrumentation tests for MessageList. + * + * It might be possible to convert these to ActivityUnitTest, which would be faster. + */ +@LargeTest +public class MessageListUnitTests + extends ActivityInstrumentationTestCase2 { + + private static final String EXTRA_ACCOUNT_ID = "com.android.email.activity._ACCOUNT_ID"; + private static final String EXTRA_MAILBOX_TYPE = "com.android.email.activity.MAILBOX_TYPE"; + private static final String EXTRA_MAILBOX_ID = "com.android.email.activity.MAILBOX_ID"; + private static final String STATE_SELECTED_ITEM_TOP = + "com.android.email.activity.MessageList.selectedItemTop"; + private static final String STATE_SELECTED_POSITION = + "com.android.email.activity.MessageList.selectedPosition"; + private static final String STATE_CHECKED_ITEMS = + "com.android.email.activity.MessageList.checkedItems"; + public final String[] PROJECTION = new String[] { + EmailContent.RECORD_ID, MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY, + MessageColumns.DISPLAY_NAME, MessageColumns.SUBJECT, MessageColumns.TIMESTAMP, + MessageColumns.FLAG_READ, MessageColumns.FLAG_FAVORITE, MessageColumns.FLAG_ATTACHMENT, + }; + private Context mContext; + private MessageList mMessageList; + private CursorAdapter mListAdapter; + private HashMap> mRowsMap; + private ArrayList mIDarray; + + public MessageListUnitTests() { + super("com.android.email", MessageList.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mContext = getInstrumentation().getTargetContext(); + Email.setServicesEnabled(mContext); + + Intent i = new Intent() + .putExtra(EXTRA_ACCOUNT_ID, Long.MIN_VALUE) + .putExtra(EXTRA_MAILBOX_TYPE, Long.MIN_VALUE) + .putExtra(EXTRA_MAILBOX_ID, Long.MIN_VALUE); + this.setActivityIntent(i); + mMessageList = getActivity(); + } + + /** + * Add a dummy message to the data map + */ + private void addElement(long id, long mailboxKey, long accountKey, String displayName, + String subject, long timestamp, int flagRead, int flagFavorite, int flagAttachment) { + HashMap emap = new HashMap(); + emap.put(EmailContent.RECORD_ID, id); + emap.put(MessageColumns.MAILBOX_KEY, mailboxKey); + emap.put(MessageColumns.ACCOUNT_KEY, accountKey); + emap.put(MessageColumns.DISPLAY_NAME, displayName); + emap.put(MessageColumns.SUBJECT, subject); + emap.put(MessageColumns.TIMESTAMP, timestamp); + emap.put(MessageColumns.FLAG_READ, flagRead); + emap.put(MessageColumns.FLAG_FAVORITE, flagFavorite); + emap.put(MessageColumns.FLAG_ATTACHMENT, flagAttachment); + mRowsMap.put(id, emap); + mIDarray.add(id); + } + + /** + * Create dummy messages + */ + private void setUpCustomCursor() throws Throwable { + runTestOnUiThread(new Runnable() { + public void run() { + mListAdapter = (CursorAdapter)mMessageList.getListAdapter(); + mRowsMap = new HashMap>(0); + mIDarray = new ArrayList(0); + addElement(0, Long.MIN_VALUE, Long.MIN_VALUE, "a", "A", 0, 0, 0, 0); + addElement(1, Long.MIN_VALUE, Long.MIN_VALUE, "b", "B", 0, 0, 0, 0); + addElement(2, Long.MIN_VALUE, Long.MIN_VALUE, "c", "C", 0, 0, 0, 0); + addElement(3, Long.MIN_VALUE, Long.MIN_VALUE, "d", "D", 0, 0, 0, 0); + addElement(4, Long.MIN_VALUE, Long.MIN_VALUE, "e", "E", 0, 0, 0, 0); + addElement(5, Long.MIN_VALUE, Long.MIN_VALUE, "f", "F", 0, 0, 0, 0); + addElement(6, Long.MIN_VALUE, Long.MIN_VALUE, "g", "G", 0, 0, 0, 0); + addElement(7, Long.MIN_VALUE, Long.MIN_VALUE, "h", "H", 0, 0, 0, 0); + addElement(8, Long.MIN_VALUE, Long.MIN_VALUE, "i", "I", 0, 0, 0, 0); + addElement(9, Long.MIN_VALUE, Long.MIN_VALUE, "j", "J", 0, 0, 0, 0); + CustomCursor cc = new CustomCursor(mIDarray, PROJECTION, mRowsMap); + mListAdapter.changeCursor(cc); + } + }); + } + + public void testRestoreAndSaveInstanceState() throws Throwable { + setUpCustomCursor(); + Bundle bundle = new Bundle(); + mMessageList.onSaveInstanceState(bundle); + long[] checkedarray = bundle.getLongArray(STATE_CHECKED_ITEMS); + assertEquals(0, checkedarray.length); + Set checkedset = ((MessageList.MessageListAdapter)mListAdapter).getSelectedSet(); + checkedset.add(1L); + checkedset.add(3L); + checkedset.add(5L); + mMessageList.onSaveInstanceState(bundle); + checkedarray = bundle.getLongArray(STATE_CHECKED_ITEMS); + java.util.Arrays.sort(checkedarray); + assertEquals(3, checkedarray.length); + assertEquals(1, checkedarray[0]); + assertEquals(3, checkedarray[1]); + assertEquals(5, checkedarray[2]); + } + + public void testRestoreInstanceState() throws Throwable { + setUpCustomCursor(); + Bundle bundle = new Bundle(); + long[] checkedarray = new long[3]; + checkedarray[0] = 1; + checkedarray[1] = 3; + checkedarray[2] = 5; + Set checkedset = ((MessageList.MessageListAdapter)mListAdapter).getSelectedSet(); + assertEquals(0, checkedset.size()); + bundle.putLongArray(STATE_CHECKED_ITEMS, checkedarray); + mMessageList.onRestoreInstanceState(bundle); + checkedset = ((MessageList.MessageListAdapter)mListAdapter).getSelectedSet(); + assertEquals(3, checkedset.size()); + assertTrue(checkedset.contains(1L)); + assertTrue(checkedset.contains(3L)); + assertTrue(checkedset.contains(5L)); + } +} + +/** + * Mock Cursor for MessageList + */ +class CustomCursor extends AbstractCursor { + private final ArrayList mSortedIdList; + private final String[] mColumnNames; + + public CustomCursor(ArrayList sortedIdList, + String[] columnNames, + HashMap> rows) { + mSortedIdList = sortedIdList; + mColumnNames = columnNames; + mUpdatedRows = rows; + } + + @Override + public void close() { + super.close(); + } + + @Override + public String[] getColumnNames() { + return mColumnNames; + } + + private Object getObject(int columnIndex) { + if (isClosed()) { + throw new SQLException("Already closed."); + } + int size = mSortedIdList.size(); + if (mPos < 0 || mPos >= size) { + throw new CursorIndexOutOfBoundsException(mPos, size); + } + if (columnIndex < 0 || columnIndex >= getColumnCount()) { + return null; + } + return mUpdatedRows.get(mSortedIdList.get(mPos)).get(mColumnNames[columnIndex]); + } + + @Override + public float getFloat(int columnIndex) { + return Float.valueOf(getObject(columnIndex).toString()); + } + + @Override + public double getDouble(int columnIndex) { + return Double.valueOf(getObject(columnIndex).toString()); + } + + @Override + public int getInt(int columnIndex) { + return Integer.valueOf(getObject(columnIndex).toString()); + } + + @Override + public long getLong(int columnIndex) { + return Long.valueOf(getObject(columnIndex).toString()); + } + + @Override + public short getShort(int columnIndex) { + return Short.valueOf(getObject(columnIndex).toString()); + } + + @Override + public String getString(int columnIndex) { + return String.valueOf(getObject(columnIndex)); + } + + @Override + public boolean isNull(int columnIndex) { + return getObject(columnIndex) == null; + } + + @Override + public int getCount() { + return mSortedIdList.size(); + } +} \ No newline at end of file