/* * 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.MailboxColumns; import com.android.emailcommon.provider.Mailbox; import android.content.Context; import android.database.Cursor; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; 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 * 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; /** * Note here we have two ID columns. The first one is for ListView, which doesn't like ID * values to be negative. The second one is the actual mailbox ID, which we use in the rest * of code. * ListView uses row IDs for some operations, including onSave/RestoreInstanceState, * and if we use negative IDs they don't work as expected. * Because ListView finds the ID column by name ("_id"), we rename the second column * so that ListView gets the correct column. */ /*package*/ static final String[] PROJECTION = new String[] { MailboxColumns.ID, MailboxColumns.ID + " AS org_mailbox_id", MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT, ROW_TYPE_MAILBOX + " AS row_type", MailboxColumns.FLAGS }; // 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 row_type. */ /*package*/ static final String[] SUBMAILBOX_PROJECTION = new String[] { MailboxColumns.ID, MailboxColumns.ID + " AS org_mailbox_id", MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT, ROW_TYPE_SUBMAILBOX + " AS row_type", MailboxColumns.FLAGS }; /*package*/ static final String[] CURMAILBOX_PROJECTION = new String[] { MailboxColumns.ID, MailboxColumns.ID + " AS org_mailbox_id", MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE, MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT, ROW_TYPE_CURMAILBOX + " AS row_type", MailboxColumns.FLAGS }; // Column 0 is only for ListView; we don't use it in our code. /*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; /** 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 mCallback. */ private static final class EmptyCallback implements Callback { public static final Callback INSTANCE = new EmptyCallback(); @Override public void onBind(MailboxListItem listItem) { } } /*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); /** * @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); } /** * 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); } }