Collapse MailboxAdapter & MailboxFragmentAdapter

MFA was the only subclass of MA and the abstraction no longer made sense.
Collapse the two into a single adapter class.

Change-Id: I32c6f027bc37f3da08626f743c3f494e5f48c3f0
This commit is contained in:
Todd Kennedy 2011-06-07 14:22:56 -07:00
parent 96e44859cf
commit 1b404f4fff
6 changed files with 309 additions and 372 deletions

View File

@ -16,9 +16,12 @@
package com.android.email.activity; package com.android.email.activity;
import com.google.common.annotations.VisibleForTesting;
import com.android.email.Email; import com.android.email.Email;
import com.android.email.FolderProperties; import com.android.email.FolderProperties;
import com.android.email.R; import com.android.email.R;
import com.android.email.ResourceHelper;
import com.android.email.data.ClosingMatrixCursor; import com.android.email.data.ClosingMatrixCursor;
import com.android.email.data.ThrottlingCursorLoader; import com.android.email.data.ThrottlingCursorLoader;
import com.android.emailcommon.Logging; import com.android.emailcommon.Logging;
@ -38,8 +41,11 @@ import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder; import android.database.MatrixCursor.RowBuilder;
import android.database.MergeCursor; import android.database.MergeCursor;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@ -57,15 +63,284 @@ import android.widget.TextView;
* TODO Determine if we actually need a separate adapter / view / loader for nested folder * TODO Determine if we actually need a separate adapter / view / loader for nested folder
* navigation. It's a little convoluted at the moment, but, still manageable. * navigation. It's a little convoluted at the moment, but, still manageable.
*/ */
/*package*/ class MailboxFragmentAdapter extends MailboxesAdapter { class MailboxFragmentAdapter extends CursorAdapter {
/**
* Callback interface used to report clicks other than the basic list item click or long press.
*/
interface Callback {
/** Callback for setting background of mailbox list items during a drag */
public void onBind(MailboxListItem listItem);
}
/** Do-nothing callback to avoid null tests for <code>mCallback</code>. */
private static final class EmptyCallback implements Callback {
public static final Callback INSTANCE = new EmptyCallback();
@Override public void onBind(MailboxListItem listItem) { }
}
/*
* The type of the row to present to the user. There are 4 defined rows that each
* have a slightly different look. These are typically used in the constant column
* {@link #ROW_TYPE} specified in {@link #PROJECTION} and {@link #SUBMAILBOX_PROJECTION}.
*/
/** Both regular and combined mailboxes */
private static final int ROW_TYPE_MAILBOX = 0;
/** Account "mailboxes" in the combined view */
private static final int ROW_TYPE_ACCOUNT = 1;
// STOPSHIP Need to determine if these types are sufficient for nested folders
// The following types are used when drilling into a mailbox
/** The current mailbox */
private static final int ROW_TYPE_CURMAILBOX = 2;
/** Sub mailboxes */
private static final int ROW_TYPE_SUBMAILBOX = 3;
/** Header */
private static final int ROW_TYPE_HEADER = 4;
/** The type of count to use. Different mailboxes have different counts. */
private static final int COUNT_TYPE_UNREAD = 0;
private static final int COUNT_TYPE_TOTAL = 1;
private static final int COUNT_TYPE_NO_COUNT = 2;
/** The type of data contained in the cursor row. */
private static final String ROW_TYPE = "rowType";
/** The original ID of the cursor row. May be negative. */
private static final String ORIGINAL_ID = "orgMailboxId";
/**
* Projection for a typical mailbox or account row.
* <p><em>NOTE</em> This projection contains two ID columns. The first, named "_id", is used
* by the framework ListView implementation. Since ListView does not handle negative IDs in
* this column, we define a "mailbox_id" column that contains the real mailbox ID; which
* may be negative for special mailboxes.
*/
private static final String[] PROJECTION = new String[] { MailboxColumns.ID,
MailboxColumns.ID + " AS " + ORIGINAL_ID,
MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT,
MailboxColumns.MESSAGE_COUNT, ROW_TYPE_MAILBOX + " AS " + ROW_TYPE,
MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY };
// STOPSHIP May need to adjust sub-folder projection depending upon final UX
/**
* Projection used to retrieve immediate children for a mailbox. The columns need to
* be identical to those in {@link #PROJECTION}. We are only changing the constant
* column {@link #ROW_TYPE}.
*/
private static final String[] SUBMAILBOX_PROJECTION = new String[] { MailboxColumns.ID,
MailboxColumns.ID + " AS " + ORIGINAL_ID,
MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT,
MailboxColumns.MESSAGE_COUNT, ROW_TYPE_SUBMAILBOX + " AS " + ROW_TYPE,
MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY };
private static final String[] CURMAILBOX_PROJECTION = new String[] { MailboxColumns.ID,
MailboxColumns.ID + " AS " + ORIGINAL_ID,
MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT,
MailboxColumns.MESSAGE_COUNT, ROW_TYPE_CURMAILBOX + " AS " + ROW_TYPE,
MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY };
/** Project to use for matrix cursors; rows MUST be identical to {@link #PROJECTION} */
private static final String[] MATRIX_PROJECTION = new String[] {
MailboxColumns.ID, ORIGINAL_ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE,
MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT, ROW_TYPE, MailboxColumns.FLAGS,
MailboxColumns.ACCOUNT_KEY };
/** All mailboxes for the account */
private static final String ALL_MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" +
" AND " + Mailbox.USER_VISIBLE_MAILBOX_SELECTION;
/** All mailboxes with the given parent */
private static final String MAILBOX_SELECTION_WITH_PARENT = ALL_MAILBOX_SELECTION +
" AND " + MailboxColumns.PARENT_KEY + "=?";
/** Selection for a specific mailbox */
private static final String MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" + private static final String MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" +
" AND " + MailboxColumns.ID + "=?"; " AND " + MailboxColumns.ID + "=?";
private static final String MAILBOX_ORDER_BY = "CASE " + MailboxColumns.TYPE +
" WHEN " + Mailbox.TYPE_INBOX + " THEN 0" +
" WHEN " + Mailbox.TYPE_DRAFTS + " THEN 1" +
" WHEN " + Mailbox.TYPE_OUTBOX + " THEN 2" +
" WHEN " + Mailbox.TYPE_SENT + " THEN 3" +
" WHEN " + Mailbox.TYPE_TRASH + " THEN 4" +
" WHEN " + Mailbox.TYPE_JUNK + " THEN 5" +
// Other mailboxes (i.e. of Mailbox.TYPE_MAIL) are shown in alphabetical order.
" ELSE 10 END" +
" ," + MailboxColumns.DISPLAY_NAME;
/** View is of a "normal" row */
private static final int ITEM_VIEW_TYPE_NORMAL = 0;
/** View is of a separator row */
private static final int ITEM_VIEW_TYPE_HEADER = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
private static boolean sEnableUpdate = true;
private final LayoutInflater mInflater;
private final ResourceHelper mResourceHelper;
private final Callback mCallback;
public MailboxFragmentAdapter(Context context, Callback callback) {
super(context, null, 0 /* flags; no content observer */);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
mResourceHelper = ResourceHelper.getInstance(context);
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_NORMAL;
}
@Override
public boolean isEnabled(int position) {
return !isHeader(position);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof MailboxListItem) {
bindListItem(view, context, cursor);
} else {
bindListHeader(view, context, cursor);
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
if (cursor.getInt(cursor.getColumnIndex(ROW_TYPE)) == ROW_TYPE_HEADER) {
return mInflater.inflate(R.layout.mailbox_list_header, parent, false);
}
return mInflater.inflate(R.layout.mailbox_list_item, parent, false);
}
private boolean isHeader(int position) {
Cursor c = getCursor();
c.moveToPosition(position);
int rowType = c.getInt(c.getColumnIndex(ROW_TYPE));
return rowType == ROW_TYPE_HEADER;
}
/** Returns {@code true} if the specified row is of an account in the combined view. */
boolean isAccountRow(int position) {
return isAccountRow((Cursor) getItem(position));
}
/** Returns {@code true} if the current row is of an account in the combined view. */
private static boolean isAccountRow(Cursor cursor) {
return getRowType(cursor) == ROW_TYPE_ACCOUNT;
}
/**
* Returns the type of count to use for the given row.
*/
private int getCountType(Cursor cursor) {
if (isAccountRow(cursor)) {
return COUNT_TYPE_UNREAD;
}
// otherwise, look at mailbox type to determine the count
switch (cursor.getInt(cursor.getColumnIndex(MailboxColumns.TYPE))) {
case Mailbox.TYPE_DRAFTS:
case Mailbox.TYPE_OUTBOX:
return COUNT_TYPE_TOTAL;
case Mailbox.TYPE_SENT:
case Mailbox.TYPE_TRASH:
return COUNT_TYPE_NO_COUNT;
}
return COUNT_TYPE_UNREAD;
}
/**
* Returns the unread count for the given row if the count type is {@link #COUNT_TYPE_UNREAD}.
* Otherwise, {@code 0}.
*/
int getUnreadCount(int position) {
Cursor c = (Cursor) getItem(position);
if (getCountType(c) != COUNT_TYPE_UNREAD) {
return 0; // Don't have a unread count.
}
return c.getInt(c.getColumnIndex(MailboxColumns.UNREAD_COUNT));
}
/**
* Returns the display name for the given row.
*/
String getDisplayName(Context context, int position) {
Cursor c = (Cursor) getItem(position);
return getDisplayName(context, c);
}
/**
* Returns the ID of the given row. It may be a mailbox or account ID depending upon the
* result of {@link #isAccountRow}.
*/
long getId(int position) {
Cursor c = (Cursor) getItem(position);
return getId(c);
}
/**
* Returns the account ID of the mailbox owner for the given row. If the given row is a
* combined mailbox, {@link Account#ACCOUNT_ID_COMBINED_VIEW} is returned. If the given
* row is an account, returns the account's ID [the same as {@link #ORIGINAL_ID}].
*/
long getAccountId(int position) {
Cursor c = (Cursor) getItem(position);
return getAccountId(c);
}
/**
* Turn on and off list updates; during a drag operation, we do NOT want to the list of
* mailboxes to update, as this would be visually jarring
* @param state whether or not the MailboxList can be updated
*/
static void enableUpdates(boolean state) {
sEnableUpdate = state;
}
private static String getDisplayName(Context context, Cursor cursor) {
String name = null;
if (getRowType(cursor) == ROW_TYPE_MAILBOX) {
// If it's a mailbox (as opposed to account row in combined view), and of certain types,
// we use the predefined names.
final int type = getType(cursor);
final long mailboxId = getId(cursor);
name = FolderProperties.getInstance(context).getDisplayName(type, mailboxId);
}
if (name == null) {
name = cursor.getString(cursor.getColumnIndex(MailboxColumns.DISPLAY_NAME));
}
return name;
}
static long getId(Cursor cursor) {
return cursor.getLong(cursor.getColumnIndex(ORIGINAL_ID));
}
static int getType(Cursor cursor) {
return cursor.getInt(cursor.getColumnIndex(MailboxColumns.TYPE));
}
static int getMessageCount(Cursor cursor) {
return cursor.getInt(cursor.getColumnIndex(MailboxColumns.MESSAGE_COUNT));
}
static int getUnreadCount(Cursor cursor) {
return cursor.getInt(cursor.getColumnIndex(MailboxColumns.UNREAD_COUNT));
}
static long getAccountId(Cursor cursor) {
return cursor.getLong(cursor.getColumnIndex(MailboxColumns.ACCOUNT_KEY));
}
private static int getRowType(Cursor cursor) {
return cursor.getInt(cursor.getColumnIndex(ROW_TYPE));
}
private static int getFlags(Cursor cursor) {
return cursor.getInt(cursor.getColumnIndex(MailboxColumns.FLAGS));
}
/** /**
* {@link Cursor} with extra information which is returned by the loader created by * {@link Cursor} with extra information which is returned by the loader created by
* {@link MailboxFragmentAdapter#createMailboxesLoader}. * {@link MailboxFragmentAdapter#createMailboxesLoader}.
*/ */
public static class CursorWithExtras extends CursorWrapper { static class CursorWithExtras extends CursorWrapper {
/** /**
* The number of mailboxes in the cursor if the cursor contains top-level mailboxes. * The number of mailboxes in the cursor if the cursor contains top-level mailboxes.
* Otherwise, the number of *child* mailboxes. * Otherwise, the number of *child* mailboxes.
@ -78,29 +353,18 @@ import android.widget.TextView;
} }
} }
public MailboxFragmentAdapter(Context context, Callback callback) {
super(context, callback);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof MailboxListItem) {
bindListItem(view, context, cursor);
} else {
bindListHeader(view, context, cursor);
}
}
private void bindListHeader(View view, Context context, Cursor cursor) { private void bindListHeader(View view, Context context, Cursor cursor) {
final TextView nameView = (TextView) view.findViewById(R.id.display_name); final TextView nameView = (TextView) view.findViewById(R.id.display_name);
nameView.setText(getDisplayName(context, cursor)); nameView.setText(getDisplayName(context, cursor));
} }
private void bindListItem(View view, Context context, Cursor cursor) { private void bindListItem(View view, Context context, Cursor cursor) {
final boolean isAccount = isAccountRow(cursor); final boolean isAccount = isAccountRow(cursor);
final int type = cursor.getInt(COLUMN_TYPE); final int type = getType(cursor);
final long id = cursor.getLong(COLUMN_ID); final long id = getId(cursor);
final long accountId = cursor.getLong(COLUMN_ACCOUNT_ID); final long accountId = getAccountId(cursor);
final int flags = cursor.getInt(COLUMN_FLAGS); final int flags = getFlags(cursor);
final int rowType = cursor.getInt(COLUMN_ROW_TYPE); final int rowType = getRowType(cursor);
final boolean hasVisibleChildren = (flags & Mailbox.FLAG_HAS_CHILDREN) != 0 final boolean hasVisibleChildren = (flags & Mailbox.FLAG_HAS_CHILDREN) != 0
&& (flags & Mailbox.FLAG_CHILDREN_VISIBLE) != 0; && (flags & Mailbox.FLAG_CHILDREN_VISIBLE) != 0;
@ -123,12 +387,12 @@ import android.widget.TextView;
nameView.setText(getDisplayName(context, cursor)); nameView.setText(getDisplayName(context, cursor));
// Set count // Set count
final int count; final int count;
switch (getCountTypeForMailboxType(cursor)) { switch (getCountType(cursor)) {
case COUNT_TYPE_UNREAD: case COUNT_TYPE_UNREAD:
count = cursor.getInt(COLUMN_UNREAD_COUNT); count = getUnreadCount(cursor);
break; break;
case COUNT_TYPE_TOTAL: case COUNT_TYPE_TOTAL:
count = cursor.getInt(COLUMN_MESSAGE_COUNT); count = getMessageCount(cursor);
break; break;
default: // no count default: // no count
count = 0; count = 0;
@ -191,15 +455,6 @@ import android.widget.TextView;
} }
} }
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
if (cursor.getInt(cursor.getColumnIndex(ROW_TYPE)) == ROW_TYPE_HEADER) {
return mInflater.inflate(R.layout.mailbox_list_header, parent, false);
}
return mInflater.inflate(R.layout.mailbox_list_item, parent, false);
}
/** /**
* Returns a cursor loader for the mailboxes of the given account. If <code>parentKey</code> * Returns a cursor loader for the mailboxes of the given account. If <code>parentKey</code>
* refers to a valid mailbox ID [e.g. non-zero], restrict the loader to only those mailboxes * refers to a valid mailbox ID [e.g. non-zero], restrict the loader to only those mailboxes
@ -207,7 +462,7 @@ import android.widget.TextView;
* *
* Note the returned loader always returns a {@link CursorWithExtras}. * Note the returned loader always returns a {@link CursorWithExtras}.
*/ */
public static Loader<Cursor> createMailboxesLoader(Context context, long accountId, static Loader<Cursor> createMailboxesLoader(Context context, long accountId,
long parentMailboxId) { long parentMailboxId) {
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#CursorWithExtras accountId=" + accountId Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#CursorWithExtras accountId=" + accountId
@ -222,7 +477,7 @@ import android.widget.TextView;
/** /**
* Returns a cursor loader for the combined view. * Returns a cursor loader for the combined view.
*/ */
public static Loader<Cursor> createCombinedViewLoader(Context context) { static Loader<Cursor> createCombinedViewLoader(Context context) {
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#createCombinedViewLoader"); Log.d(Logging.LOG_TAG, "MailboxFragmentAdapter#createCombinedViewLoader");
} }
@ -329,16 +584,17 @@ import android.widget.TextView;
/** /**
* Loader for mailboxes in "Combined view". * Loader for mailboxes in "Combined view".
*/ */
/*package*/ static class CombinedMailboxLoader extends ThrottlingCursorLoader { @VisibleForTesting
static class CombinedMailboxLoader extends ThrottlingCursorLoader {
private static final String[] ACCOUNT_PROJECTION = new String[] { private static final String[] ACCOUNT_PROJECTION = new String[] {
EmailContent.RECORD_ID, AccountColumns.DISPLAY_NAME, EmailContent.RECORD_ID, AccountColumns.DISPLAY_NAME,
}; };
private static final int COLUMN_ACCOUND_ID = 0; private static final int COLUMN_ACCOUND_ID = 0;
private static final int COLUMN_ACCOUNT_DISPLAY_NAME = 1; private static final int COLUMN_ACCOUNT_DISPLAY_NAME = 1;
private final Context mContext; private final Context mContext;
public CombinedMailboxLoader(Context context) { private CombinedMailboxLoader(Context context) {
super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null, null); super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null, null);
mContext = context; mContext = context;
} }
@ -364,9 +620,9 @@ import android.widget.TextView;
return Utility.CloseTraceCursorWrapper.get(returnCursor); return Utility.CloseTraceCursorWrapper.get(returnCursor);
} }
/*package*/ static MatrixCursor buildCombinedMailboxes(Context context, @VisibleForTesting
Cursor innerCursor) { static MatrixCursor buildCombinedMailboxes(Context context, Cursor innerCursor) {
MatrixCursor cursor = new ClosingMatrixCursor(PROJECTION, innerCursor); MatrixCursor cursor = new ClosingMatrixCursor(MATRIX_PROJECTION, innerCursor);
// Combined inbox -- show unread count // Combined inbox -- show unread count
addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_INBOXES, Mailbox.TYPE_INBOX, addCombinedMailboxRow(cursor, Mailbox.QUERY_ALL_INBOXES, Mailbox.TYPE_INBOX,
Mailbox.getUnreadCountByMailboxType(context, Mailbox.TYPE_INBOX), true); Mailbox.getUnreadCountByMailboxType(context, Mailbox.TYPE_INBOX), true);

View File

@ -191,7 +191,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
// UI Support // UI Support
private Activity mActivity; private Activity mActivity;
private MailboxesAdapter mListAdapter; private MailboxFragmentAdapter mListAdapter;
private Callback mCallback = EmptyCallback.INSTANCE; private Callback mCallback = EmptyCallback.INSTANCE;
// See the class javadoc // See the class javadoc
@ -220,8 +220,8 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
private Parcelable mSavedListState; private Parcelable mSavedListState;
private final MailboxesAdapter.Callback mMailboxesAdapterCallback = private final MailboxFragmentAdapter.Callback mMailboxesAdapterCallback =
new MailboxesAdapter.Callback() { new MailboxFragmentAdapter.Callback() {
@Override @Override
public void onBind(MailboxListItem listItem) { public void onBind(MailboxListItem listItem) {
listItem.setDropTargetBackground(mDragInProgress, mDragItemMailboxId); listItem.setDropTargetBackground(mDragInProgress, mDragItemMailboxId);
@ -870,7 +870,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
* <p> * <p>
* @param doNotUse <em>IMPORTANT</em>: Do not use this parameter. The ID in the list widget * @param doNotUse <em>IMPORTANT</em>: Do not use this parameter. The ID in the list widget
* must be a positive value. However, we rely on negative IDs for special mailboxes. Instead, * must be a positive value. However, we rely on negative IDs for special mailboxes. Instead,
* we use the ID returned by {@link MailboxesAdapter#getId(int)}. * we use the ID returned by {@link MailboxFragmentAdapter#getId(int)}.
*/ */
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long doNotUse) { public void onItemClick(AdapterView<?> parent, View view, int position, long doNotUse) {
@ -1127,7 +1127,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
if (mDragInProgress) { if (mDragInProgress) {
mDragInProgress = false; mDragInProgress = false;
// Reenable updates to the view and redraw (in case it changed) // Reenable updates to the view and redraw (in case it changed)
MailboxesAdapter.enableUpdates(true); MailboxFragmentAdapter.enableUpdates(true);
mListAdapter.notifyDataSetChanged(); mListAdapter.notifyDataSetChanged();
// Stop highlighting targets // Stop highlighting targets
updateChildViews(); updateChildViews();
@ -1160,7 +1160,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
} }
mDragInProgress = true; mDragInProgress = true;
// Stop the list from updating // Stop the list from updating
MailboxesAdapter.enableUpdates(false); MailboxFragmentAdapter.enableUpdates(false);
// Update the backgrounds of our child views to highlight drop targets // Update the backgrounds of our child views to highlight drop targets
updateChildViews(); updateChildViews();
return true; return true;

View File

@ -50,7 +50,7 @@ public class MailboxListItem extends RelativeLayout {
public boolean mIsValidDropTarget; public boolean mIsValidDropTarget;
/** If {@code true} this item can be navigated to. Otherwise, it can just be selected. */ /** If {@code true} this item can be navigated to. Otherwise, it can just be selected. */
public boolean mIsNavigable; public boolean mIsNavigable;
public MailboxesAdapter mAdapter; public MailboxFragmentAdapter mAdapter;
private Drawable mBackground; private Drawable mBackground;
private TextView mLabelName; private TextView mLabelName;

View File

@ -38,7 +38,7 @@ import android.widget.TextView;
/** /**
* Cursor adapter for the "move to mailbox" dialog. * Cursor adapter for the "move to mailbox" dialog.
* TODO We've detached this class from {@link MailboxesAdapter} and {@link MailboxFragmentAdapter}. * TODO We've detached this class from {@link MailboxFragmentAdapter} and {@link MailboxFragmentAdapter}.
* Depending upon the UX for the dialog and nested folders, we may want to bring these three * Depending upon the UX for the dialog and nested folders, we may want to bring these three
* adapter classes back into alignment. * adapter classes back into alignment.
*/ */

View File

@ -1,319 +0,0 @@
/*
* 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.email.activity;
import com.android.email.FolderProperties;
import com.android.email.ResourceHelper;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.Mailbox;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
/**
* Super class adapter for displaying a mailbox list.
* NOTE: Do not use {@link #getItemId(int)}; it's only for ListView. Instead, use {@link #getId}.
*
* TODO Show "Starred" per account
* TODO Unit test, when UI is settled.
*/
/*package*/ abstract class MailboxesAdapter extends CursorAdapter {
/**
* Return value from {@link #getCountType}.
*/
/*package*/ static final int COUNT_TYPE_UNREAD = 0;
/*package*/ static final int COUNT_TYPE_TOTAL = 1;
/*package*/ static final int COUNT_TYPE_NO_COUNT = 2;
/**
* Callback interface used to report clicks other than the basic list item click or long press.
*/
public interface Callback {
/** Callback for setting background of mailbox list items during a drag */
public void onBind(MailboxListItem listItem);
}
/**
* The type of the row to present to the user. There are 4 defined rows that each
* have a slightly different look. These are typically used in the constant column
* {@link #ROW_TYPE} specified in {@link #PROJECTION} and {@link #SUBMAILBOX_PROJECTION}.
*/
/** Both regular and combined mailboxes */
static final int ROW_TYPE_MAILBOX = 0;
/** Account "mailboxes" in the combined view */
static final int ROW_TYPE_ACCOUNT = 1;
// STOPSHIP Need to determine if these types are sufficient for nested folders
// The following types are used when drilling into a mailbox
/** The current mailbox */
static final int ROW_TYPE_CURMAILBOX = 2;
/** Sub mailboxes */
static final int ROW_TYPE_SUBMAILBOX = 3;
/** Header */
static final int ROW_TYPE_HEADER = 4;
/**
* Projection for a typical mailbox or account row.
* <p><em>NOTE</em> This projection contains two ID columns. The first, named "_id", is used
* by the framework ListView implementation. Since ListView does not handle negative IDs in
* this column, we define a "mailbox_id" column that contains the real mailbox ID; which
* may be negative for special mailboxes.
*/
final static String ROW_TYPE = "rowType";
final static String ORIGINAL_ID = "orgMailboxId";
static final String[] PROJECTION = new String[] { MailboxColumns.ID,
MailboxColumns.ID + " AS " + ORIGINAL_ID,
MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT,
MailboxColumns.MESSAGE_COUNT, ROW_TYPE_MAILBOX + " AS " + ROW_TYPE,
MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY };
// STOPSHIP May need to adjust sub-folder projection depending upon final UX
/**
* Projection used to retrieve immediate children for a mailbox. The columns need to
* be identical to those in {@link #PROJECTION}. We are only changing the constant
* column {@link #ROW_TYPE}.
*/
static final String[] SUBMAILBOX_PROJECTION = new String[] { MailboxColumns.ID,
MailboxColumns.ID + " AS " + ORIGINAL_ID,
MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT,
MailboxColumns.MESSAGE_COUNT, ROW_TYPE_SUBMAILBOX + " AS " + ROW_TYPE,
MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY };
static final String[] CURMAILBOX_PROJECTION = new String[] { MailboxColumns.ID,
MailboxColumns.ID + " AS " + ORIGINAL_ID,
MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT,
MailboxColumns.MESSAGE_COUNT, ROW_TYPE_CURMAILBOX + " AS " + ROW_TYPE,
MailboxColumns.FLAGS, MailboxColumns.ACCOUNT_KEY };
/** Project to use for matrix cursors; rows MUST be identical to {@link #PROJECTION} */
static final String[] MATRIX_PROJECTION = new String[] {
MailboxColumns.ID, ORIGINAL_ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE,
MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT, ROW_TYPE, MailboxColumns.FLAGS,
MailboxColumns.ACCOUNT_KEY };
// Column 0 is only for ListView; we don't use it in our code.
/**
* ID for the current row. Normally it's the ID for the current mailbox, but if it's an account
* row on the combined view, it's the ID for the account.
*/
/*package*/ static final int COLUMN_ID = 1;
/*package*/ static final int COLUMN_DISPLAY_NAME = 2;
/*package*/ static final int COLUMN_TYPE = 3;
/*package*/ static final int COLUMN_UNREAD_COUNT = 4;
/*package*/ static final int COLUMN_MESSAGE_COUNT = 5;
/*package*/ static final int COLUMN_ROW_TYPE = 6;
/*package*/ static final int COLUMN_FLAGS = 7;
/**
* ID for the owner account of the mailbox. If it's a combined mailbox, it's
* {@link Account#ACCOUNT_ID_COMBINED_VIEW}. If it's an account row on the combined view,
* it's the ID for the account.
*/
/*package*/ static final int COLUMN_ACCOUNT_ID = 8;
/** All mailboxes for the account */
/*package*/ static final String ALL_MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" +
" AND " + Mailbox.USER_VISIBLE_MAILBOX_SELECTION;
/** All mailboxes with the given parent */
/*package*/ static final String MAILBOX_SELECTION_WITH_PARENT = ALL_MAILBOX_SELECTION +
" AND " + MailboxColumns.PARENT_KEY + "=?";
/*package*/ static final String MAILBOX_ORDER_BY = "CASE " + MailboxColumns.TYPE +
" WHEN " + Mailbox.TYPE_INBOX + " THEN 0" +
" WHEN " + Mailbox.TYPE_DRAFTS + " THEN 1" +
" WHEN " + Mailbox.TYPE_OUTBOX + " THEN 2" +
" WHEN " + Mailbox.TYPE_SENT + " THEN 3" +
" WHEN " + Mailbox.TYPE_TRASH + " THEN 4" +
" WHEN " + Mailbox.TYPE_JUNK + " THEN 5" +
// Other mailboxes (i.e. of Mailbox.TYPE_MAIL) are shown in alphabetical order.
" ELSE 10 END" +
" ," + MailboxColumns.DISPLAY_NAME;
/** Do-nothing callback to avoid null tests for <code>mCallback</code>. */
private static final class EmptyCallback implements Callback {
public static final Callback INSTANCE = new EmptyCallback();
@Override
public void onBind(MailboxListItem listItem) {
}
}
private final static int ITEM_VIEW_TYPE_NORMAL = 0;
private final static int ITEM_VIEW_TYPE_HEADER = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
/*package*/ static boolean sEnableUpdate = true;
/*package*/ final LayoutInflater mInflater;
/*package*/ final ResourceHelper mResourceHelper;
/*package*/ final Callback mCallback;
/*package*/ MailboxesAdapter(Context context, Callback callback) {
super(context, null, 0 /* flags; no content observer */);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
mResourceHelper = ResourceHelper.getInstance(context);
}
@Override
public abstract void bindView(View view, Context context, Cursor cursor);
@Override
public abstract View newView(Context context, Cursor cursor, ViewGroup parent);
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_NORMAL;
}
@Override
public boolean isEnabled(int position) {
return !isHeader(position);
}
private boolean isHeader(int position) {
Cursor c = getCursor();
c.moveToPosition(position);
int rowType = c.getInt(c.getColumnIndex(ROW_TYPE));
return rowType == ROW_TYPE_HEADER;
}
/**
* @return true if the current row is of an account in the combined view.
*/
/*package*/ static boolean isAccountRow(Cursor c) {
return c.getInt(COLUMN_ROW_TYPE) == ROW_TYPE_ACCOUNT;
}
/**
* @return true if the specified row is of an account in the combined view.
*/
public boolean isAccountRow(int position) {
return isAccountRow((Cursor) getItem(position));
}
/**
* @return which type of count should be used for the current row.
* Possible return values are COUNT_TYPE_*.
*/
/*package*/ static int getCountTypeForMailboxType(Cursor c) {
if (isAccountRow(c)) {
return COUNT_TYPE_UNREAD; // Use the unread count for account rows.
}
switch (c.getInt(COLUMN_TYPE)) {
case Mailbox.TYPE_DRAFTS:
case Mailbox.TYPE_OUTBOX:
return COUNT_TYPE_TOTAL;
case Mailbox.TYPE_SENT:
case Mailbox.TYPE_TRASH:
return COUNT_TYPE_NO_COUNT;
}
return COUNT_TYPE_UNREAD;
}
/**
* @return count type for the specified row. See {@link #getCountTypeForMailboxType}
*/
private int getCountType(int position) {
if (isAccountRow(position)) {
return COUNT_TYPE_UNREAD;
}
return getCountTypeForMailboxType((Cursor) getItem(position));
}
/**
* @return count type for the specified row.
*/
public int getUnreadCount(int position) {
if (getCountType(position) != COUNT_TYPE_UNREAD) {
return 0; // Don't have a unread count.
}
Cursor c = (Cursor) getItem(position);
return c.getInt(COLUMN_UNREAD_COUNT);
}
/**
* @return display name for the specified row.
*/
public String getDisplayName(Context context, int position) {
Cursor c = (Cursor) getItem(position);
return getDisplayName(context, c);
}
/**
* Returns the ID of the mailbox (or account, if {@link #isAccountRow} is {@code true})
* of the given row.
*/
public long getId(int position) {
Cursor c = (Cursor) getItem(position);
return c.getLong(COLUMN_ID);
}
/** @see #COLUMN_ACCOUNT_ID */
public long getAccountId(int position) {
Cursor c = (Cursor) getItem(position);
return c.getLong(COLUMN_ACCOUNT_ID);
}
/**
* Turn on and off list updates; during a drag operation, we do NOT want to the list of
* mailboxes to update, as this would be visually jarring
* @param state whether or not the MailboxList can be updated
*/
public static void enableUpdates(boolean state) {
sEnableUpdate = state;
}
/*package*/ static String getDisplayName(Context context, Cursor cursor) {
String name = null;
if (cursor.getInt(COLUMN_ROW_TYPE) == ROW_TYPE_MAILBOX) {
// If it's a mailbox (as opposed to account row in combined view), and of certain types,
// we use the predefined names.
final int type = cursor.getInt(COLUMN_TYPE);
final long mailboxId = cursor.getLong(COLUMN_ID);
name = FolderProperties.getInstance(context).getDisplayName(type, mailboxId);
}
if (name == null) {
name = cursor.getString(COLUMN_DISPLAY_NAME);
}
return name;
}
/*package*/ static long getIdForTest(Cursor cursor) {
return cursor.getLong(COLUMN_ID);
}
/*package*/ static int getTypeForTest(Cursor cursor) {
return cursor.getInt(COLUMN_TYPE);
}
/*package*/ static int getMessageCountForTest(Cursor cursor) {
return cursor.getInt(COLUMN_MESSAGE_COUNT);
}
/*package*/ static int getUnreadCountForTest(Cursor cursor) {
return cursor.getInt(COLUMN_UNREAD_COUNT);
}
@VisibleForTesting
static long getAccountId(Cursor cursor) {
return cursor.getLong(COLUMN_ACCOUNT_ID);
}
}

View File

@ -115,11 +115,11 @@ public class MailboxFragmentAdapterTest extends ProviderTestCase2<EmailProvider>
int count) { int count) {
// _id must always be >= 0; otherwise ListView gets confused. // _id must always be >= 0; otherwise ListView gets confused.
Assert.assertTrue(cursor.getLong(cursor.getColumnIndex("_id")) >= 0); Assert.assertTrue(cursor.getLong(cursor.getColumnIndex("_id")) >= 0);
Assert.assertEquals(id, MailboxesAdapter.getIdForTest(cursor)); Assert.assertEquals(id, MailboxFragmentAdapter.getId(cursor));
Assert.assertEquals(type, MailboxesAdapter.getTypeForTest(cursor)); Assert.assertEquals(type, MailboxFragmentAdapter.getType(cursor));
Assert.assertEquals(count, MailboxesAdapter.getMessageCountForTest(cursor)); Assert.assertEquals(count, MailboxFragmentAdapter.getMessageCount(cursor));
Assert.assertEquals(count, MailboxesAdapter.getUnreadCountForTest(cursor)); Assert.assertEquals(count, MailboxFragmentAdapter.getUnreadCount(cursor));
Assert.assertEquals(Account.ACCOUNT_ID_COMBINED_VIEW, Assert.assertEquals(Account.ACCOUNT_ID_COMBINED_VIEW,
MailboxesAdapter.getAccountId(cursor)); MailboxFragmentAdapter.getAccountId(cursor));
} }
} }