From e53ec3e3a620223723cf7a47b1b7917009177931 Mon Sep 17 00:00:00 2001 From: Ben Komalo Date: Thu, 9 Jun 2011 11:38:11 -0700 Subject: [PATCH 1/8] Guard against invalid selections in spinner This is a stop gap fix for b/4584196 There was a race condition in which the actionbar callback happened prior to the fragments being ready, and so any callbacks on a mailbox would explode (and the spinner mistakenly thought the header was a mailbox) Change-Id: Id0a24d252472faf97088175b5c413ef554dc3b76 --- src/com/android/email/activity/AccountSelectorAdapter.java | 6 ++++++ src/com/android/email/activity/ActionBarController.java | 7 ++++++- src/com/android/email/activity/UIControllerOnePane.java | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/com/android/email/activity/AccountSelectorAdapter.java b/src/com/android/email/activity/AccountSelectorAdapter.java index 3c4a2aaed..e2d88bc94 100644 --- a/src/com/android/email/activity/AccountSelectorAdapter.java +++ b/src/com/android/email/activity/AccountSelectorAdapter.java @@ -209,6 +209,12 @@ public class AccountSelectorAdapter extends CursorAdapter { return (c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_ACCOUNT); } + public boolean isMailboxItem(int position) { + Cursor c = getCursor(); + c.moveToPosition(position); + return (c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_MAILBOX); + } + private String getAccountDisplayName(int position) { final Cursor c = getCursor(); return c.moveToPosition(position) ? getAccountDisplayName(c) : null; diff --git a/src/com/android/email/activity/ActionBarController.java b/src/com/android/email/activity/ActionBarController.java index 2d95ea444..13e4efc8f 100644 --- a/src/com/android/email/activity/ActionBarController.java +++ b/src/com/android/email/activity/ActionBarController.java @@ -17,6 +17,7 @@ package com.android.email.activity; import com.android.email.R; +import com.android.emailcommon.Logging; import com.android.emailcommon.provider.EmailContent.Account; import com.android.emailcommon.provider.Mailbox; @@ -27,6 +28,7 @@ import android.content.Context; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; @@ -236,7 +238,7 @@ public class ActionBarController { if (mAccountsSelectorAdapter.isAccountItem(itemPosition) && itemId != mCallback.getUIAccountId()) { mCallback.onAccountSelected(itemId); - } else if (!mAccountsSelectorAdapter.isAccountItem(itemPosition)) { + } else if (mAccountsSelectorAdapter.isMailboxItem(itemPosition)) { mCallback.onMailboxSelected(itemId); // We need to update the selection, otherwise the user is unable to select the // recent folder a second time w/o first selecting another item in the spinner @@ -244,6 +246,9 @@ public class ActionBarController { if (selectedPosition != AccountSelectorAdapter.UNKNOWN_POSITION) { mActionBar.setSelectedNavigationItem(selectedPosition); } + } else { + Log.i(Logging.LOG_TAG, + "Invalid type selected in ActionBarController at index " + itemPosition); } return true; } diff --git a/src/com/android/email/activity/UIControllerOnePane.java b/src/com/android/email/activity/UIControllerOnePane.java index 3538a0cf8..8d481aa04 100644 --- a/src/com/android/email/activity/UIControllerOnePane.java +++ b/src/com/android/email/activity/UIControllerOnePane.java @@ -239,7 +239,7 @@ class UIControllerOnePane extends UIControllerBase { if (mailboxId == Mailbox.NO_MAILBOX) { showAllMailboxes(); } else { - UIControllerOnePane.this.openMailbox(getUIAccountId(), mailboxId); + openMailbox(getUIAccountId(), mailboxId); } } From a7b4efd2a7080931bfde35680a1adf81f8f8e4a7 Mon Sep 17 00:00:00 2001 From: Ben Komalo Date: Tue, 14 Jun 2011 10:25:27 -0700 Subject: [PATCH 2/8] Fix proguard flags after Account move. Bug: 4603857 Change-Id: I8bd91ebe4b260b9ff1f53491e4cc346b7668480b --- proguard.flags | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proguard.flags b/proguard.flags index 23878e985..c14eea3f1 100644 --- a/proguard.flags +++ b/proguard.flags @@ -1,20 +1,20 @@ # keep names that are used by reflection. --keep class com.android.emailcommon.provider.EmailContent$Account +-keep class com.android.emailcommon.provider.Account -keepclasseswithmembers class * { - public static void actionEditIncomingSettings(android.app.Activity, int, com.android.emailcommon.provider.EmailContent$Account); + public static void actionEditIncomingSettings(android.app.Activity, int, com.android.emailcommon.provider.Account); } -keepclasseswithmembers class * { - public static void actionEditOutgoingSettings(android.app.Activity, int, com.android.emailcommon.provider.EmailContent$Account); + public static void actionEditOutgoingSettings(android.app.Activity, int, com.android.emailcommon.provider.Account); } -keepclasseswithmembers class * { - public *** newInstance(com.android.emailcommon.provider.EmailContent$Account, android.content.Context); + public *** newInstance(com.android.emailcommon.provider.Account, android.content.Context); } -keepclasseswithmembers class * { - public *** newInstance(com.android.emailcommon.provider.EmailContent$Account, android.content.Context, com.android.email.mail.Store$PersistentDataCallbacks); + public *** newInstance(com.android.emailcommon.provider.Account, android.content.Context, com.android.email.mail.Store$PersistentDataCallbacks); } -keepclasseswithmembers class android.content.SharedPreferences$Editor { @@ -180,17 +180,17 @@ *** size(); } --keepclasseswithmembers class com.android.emailcommon.provider.EmailContent$Account { +-keepclasseswithmembers class com.android.emailcommon.provider.Account { *** getShortcutSafeUri(); *** refresh(android.content.Context); } -keepclasseswithmembers class com.android.emailcommon.provider.Policy { *** setAttachmentFlagsForNewPolicy(android.content.Context, - com.android.emailcommon.provider.EmailContent$Account, + com.android.emailcommon.provider.Account, com.android.emailcommon.provider.Policy); *** clearAccountPolicy(android.content.Context, - com.android.emailcommon.provider.EmailContent$Account); + com.android.emailcommon.provider.Account); } -keep class org.apache.james.mime4j.field.Field { From 6a7eb791976dee105290817b70b409b7beafc2b9 Mon Sep 17 00:00:00 2001 From: Marc Blank Date: Wed, 22 Jun 2011 10:59:06 -0700 Subject: [PATCH 3/8] Fix cursor-related errors: 1) Have CachedCursor implement CrossProcessCursor; still need to figure out how this ever worked 2) Close cursor used internally in findMailboxOfType Bug: 4869024 Change-Id: Id20d37b7b83e133aa4d5fe9293a42ae217024f01 --- .../android/emailcommon/provider/Mailbox.java | 19 +++++++++++++------ .../android/email/provider/ContentCache.java | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java index a3d8daae9..946c0d05d 100644 --- a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java +++ b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java @@ -315,12 +315,19 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns Uri uri = FROM_ACCOUNT_AND_TYPE_URI.buildUpon().appendPath(Long.toString(accountId)) .appendPath(Integer.toString(type)).build(); Cursor c = context.getContentResolver().query(uri, ID_PROJECTION, null, null, null); - c.moveToFirst(); - Long mailboxId = c.getLong(ID_PROJECTION_COLUMN); - if (mailboxId != null && mailboxId.intValue() != 0) { - return mailboxId; - } else { - Log.w(Logging.LOG_TAG, "========== Mailbox of type " + type + " not found in cache??"); + if (c != null) { + try { + c.moveToFirst(); + Long mailboxId = c.getLong(ID_PROJECTION_COLUMN); + if (mailboxId != null && mailboxId.intValue() != 0) { + return mailboxId; + } else { + Log.w(Logging.LOG_TAG, "========== Mailbox of type " + type + + " not found in cache"); + } + } finally { + c.close(); + } } String[] bindArguments = new String[] {Long.toString(type), Long.toString(accountId)}; return Utility.getFirstRowLong(context, Mailbox.CONTENT_URI, diff --git a/src/com/android/email/provider/ContentCache.java b/src/com/android/email/provider/ContentCache.java index 492fc8e98..11ad179a5 100644 --- a/src/com/android/email/provider/ContentCache.java +++ b/src/com/android/email/provider/ContentCache.java @@ -17,7 +17,9 @@ package com.android.email.provider; import android.content.ContentValues; +import android.database.CrossProcessCursor; import android.database.Cursor; +import android.database.CursorWindow; import android.database.CursorWrapper; import android.database.MatrixCursor; import android.net.Uri; @@ -275,7 +277,7 @@ public final class ContentCache { * Multiple CachedCursor's can use the same underlying cursor, so we override the various * moveX methods such that each CachedCursor can have its own position information */ - public static final class CachedCursor extends CursorWrapper { + public static final class CachedCursor extends CursorWrapper implements CrossProcessCursor { // The cursor we're wrapping private final Cursor mCursor; // The cache which generated this cursor @@ -381,6 +383,21 @@ public final class ContentCache { public final boolean isAfterLast() { return mPosition == 1; } + + @Override + public CursorWindow getWindow() { + return ((CrossProcessCursor)mCursor).getWindow(); + } + + @Override + public void fillWindow(int pos, CursorWindow window) { + ((CrossProcessCursor)mCursor).fillWindow(pos, window); + } + + @Override + public boolean onMove(int oldPosition, int newPosition) { + return ((CrossProcessCursor)mCursor).onMove(oldPosition, newPosition); + } } /** From 00691d6a0d3f1d562905fa16753b63951399f7c9 Mon Sep 17 00:00:00 2001 From: Ben Komalo Date: Thu, 23 Jun 2011 12:36:58 -0700 Subject: [PATCH 4/8] Save/restore message list context This fixes opening messages after rotations. Oops. Bug: 4905495 Change-Id: Ibb9984245f7a040b65d472ad4103da587c28c83b --- .../emailcommon/service/SearchParams.java | 28 +++++++++ src/com/android/email/MessageListContext.java | 62 ++++++++++++++++++- .../email/activity/UIControllerBase.java | 6 +- .../email/MessageListContextTests.java | 54 ++++++++++++++++ 4 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 tests/src/com/android/email/MessageListContextTests.java diff --git a/emailcommon/src/com/android/emailcommon/service/SearchParams.java b/emailcommon/src/com/android/emailcommon/service/SearchParams.java index 9c4ab2e61..367cc89ab 100644 --- a/emailcommon/src/com/android/emailcommon/service/SearchParams.java +++ b/emailcommon/src/com/android/emailcommon/service/SearchParams.java @@ -20,6 +20,7 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.emailcommon.provider.Mailbox; +import com.google.common.base.Objects; public class SearchParams implements Parcelable { public static final long ALL_MAILBOXES = Mailbox.NO_MAILBOX; @@ -53,6 +54,33 @@ public class SearchParams implements Parcelable { mFilter = filter; } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if ((o == null) || !(o instanceof SearchParams)) { + return false; + } + + SearchParams os = (SearchParams) o; + return mMailboxId == os.mMailboxId + && mIncludeChildren == os.mIncludeChildren + && mFilter.equals(os.mFilter) + && mLimit == os.mLimit + && mOffset == os.mOffset; + } + + @Override + public int hashCode() { + return Objects.hashCode(mMailboxId, mFilter, mOffset); + } + + @Override + public String toString() { + return "[SearchParams " + mMailboxId + ":" + mFilter + " (" + mOffset + ", " + mLimit + "]"; + } + @Override public int describeContents() { return 0; diff --git a/src/com/android/email/MessageListContext.java b/src/com/android/email/MessageListContext.java index 762016551..448bdcf90 100644 --- a/src/com/android/email/MessageListContext.java +++ b/src/com/android/email/MessageListContext.java @@ -18,11 +18,14 @@ package com.android.email; import android.content.Context; import android.content.Intent; +import android.os.Parcel; +import android.os.Parcelable; import com.android.email.activity.EmailActivity; import com.android.emailcommon.provider.Account; import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.service.SearchParams; +import com.google.common.base.Objects; import com.google.common.base.Preconditions; /** @@ -30,7 +33,7 @@ import com.google.common.base.Preconditions; * This encapsulates the meta-data about the list of messages, which can either be the * {@link Mailbox} ID, or {@link SearchParams}. */ -public class MessageListContext { +public class MessageListContext implements Parcelable { /** * The active account. Changing an account is a destructive enough operation that it warrants @@ -121,4 +124,61 @@ public class MessageListContext { public long getMailboxId() { return mMailboxId; } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if ((o == null) || !(o instanceof MessageListContext)) { + return false; + } + + MessageListContext om = (MessageListContext) o; + return mAccountId == om.mAccountId + && mMailboxId == om.mMailboxId + && Objects.equal(mSearchParams, om.mSearchParams); + } + + @Override + public int hashCode() { + return Objects.hashCode(mAccountId, mMailboxId, mSearchParams); + } + + @Override + public String toString() { + return "[MessageListContext " + mAccountId + ":" + mMailboxId + ":" + mSearchParams + "]"; + } + + + private MessageListContext(Parcel in) { + mAccountId = in.readLong(); + mMailboxId = in.readLong(); + mSearchParams = in.readParcelable(SearchParams.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mAccountId); + dest.writeLong(mMailboxId); + dest.writeParcelable(mSearchParams, flags); + } + + public static Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public MessageListContext createFromParcel(Parcel source) { + return new MessageListContext(source); + } + + @Override + public MessageListContext[] newArray(int size) { + return new MessageListContext[size]; + } + }; } diff --git a/src/com/android/email/activity/UIControllerBase.java b/src/com/android/email/activity/UIControllerBase.java index 67375911d..f9633ac88 100644 --- a/src/com/android/email/activity/UIControllerBase.java +++ b/src/com/android/email/activity/UIControllerBase.java @@ -43,13 +43,13 @@ import java.util.List; /** * Base class for the UI controller. - * - * TODO Remove all the {@link MailboxFinder} stuff. It's now done in {@link Welcome}. */ abstract class UIControllerBase implements MailboxListFragment.Callback, MessageListFragment.Callback, MessageViewFragment.Callback { static final boolean DEBUG_FRAGMENTS = false; // DO NOT SUBMIT WITH TRUE + static final String KEY_LIST_CONTEXT = "UIControllerBase.listContext"; + /** The owner activity */ final EmailActivity mActivity; final FragmentManager mFragmentManager; @@ -201,6 +201,7 @@ abstract class UIControllerBase implements MailboxListFragment.Callback, Log.d(Logging.LOG_TAG, this + " onSaveInstanceState"); } mActionBarController.onSaveInstanceState(outState); + outState.putParcelable(KEY_LIST_CONTEXT, mListContext); } /** @@ -211,6 +212,7 @@ abstract class UIControllerBase implements MailboxListFragment.Callback, Log.d(Logging.LOG_TAG, this + " restoreInstanceState"); } mActionBarController.onRestoreInstanceState(savedInstanceState); + mListContext = savedInstanceState.getParcelable(KEY_LIST_CONTEXT); } /** diff --git a/tests/src/com/android/email/MessageListContextTests.java b/tests/src/com/android/email/MessageListContextTests.java new file mode 100644 index 000000000..939762424 --- /dev/null +++ b/tests/src/com/android/email/MessageListContextTests.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 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; + +import android.os.Parcel; +import android.test.AndroidTestCase; + +import com.android.emailcommon.service.SearchParams; + +public class MessageListContextTests extends AndroidTestCase { + + public void testParcellingMailboxes() { + long accountId = 123; + long mailboxId = 456; + MessageListContext original = MessageListContext.forMailbox(accountId, mailboxId); + Parcel parcel = Parcel.obtain(); + + original.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + MessageListContext read = MessageListContext.CREATOR.createFromParcel(parcel); + assertEquals(original, read); + parcel.recycle(); + } + + public void testParcellingSearches() { + long accountId = 123; + long mailboxId = 456; + SearchParams params = new SearchParams(mailboxId, "search terms"); + MessageListContext original = MessageListContext.forSearch(accountId, mailboxId, params); + Parcel parcel = Parcel.obtain(); + + original.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + MessageListContext read = MessageListContext.CREATOR.createFromParcel(parcel); + assertEquals(original, read); + parcel.recycle(); + } +} From b780a2216bc19b74fc4247694b969cc7ebef6cb9 Mon Sep 17 00:00:00 2001 From: Ben Komalo Date: Thu, 23 Jun 2011 14:56:05 -0700 Subject: [PATCH 5/8] Disable hw acceleration temporarily Bug: 4886133 Change-Id: I0f9efc200712815610ddc9013acc7eec25631180 --- src/com/android/email/activity/ActivityHelper.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/email/activity/ActivityHelper.java b/src/com/android/email/activity/ActivityHelper.java index cbd1d5b27..05af85e88 100644 --- a/src/com/android/email/activity/ActivityHelper.java +++ b/src/com/android/email/activity/ActivityHelper.java @@ -122,6 +122,8 @@ public final class ActivityHelper { * NOTE: Currently, this only works if HW accel is *not* enabled via the manifest. */ public static void debugSetWindowFlags(Activity activity) { + // STOPSHIP - re-enable hw acceleration when b/4886133 is fixed. + /* if (Email.sDebugInhibitGraphicsAcceleration) { // Clear the flag in the activity's window activity.getWindow().setFlags(0, @@ -131,5 +133,6 @@ public final class ActivityHelper { activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); } + */ } } From 00e53ede394142bc955702628d0bf498c9730339 Mon Sep 17 00:00:00 2001 From: Marc Blank Date: Tue, 28 Jun 2011 10:58:22 -0700 Subject: [PATCH 6/8] Fix NPE Bug: 4969186 Change-Id: I27fbdf7a496ee73caf36a90bf56b91c90133b3b7 --- src/com/android/email/provider/EmailProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/email/provider/EmailProvider.java b/src/com/android/email/provider/EmailProvider.java index 8e21fa1e7..eb148961c 100644 --- a/src/com/android/email/provider/EmailProvider.java +++ b/src/com/android/email/provider/EmailProvider.java @@ -877,10 +877,11 @@ public class EmailProvider extends ContentProvider { private long getMailboxIdFromMailboxTypeMap(long accountId, int type) { synchronized(mMailboxTypeMap) { HashMap accountMap = mMailboxTypeMap.get(accountId); - long mailboxId = -1; + Long mailboxId = null; if (accountMap != null) { mailboxId = accountMap.get(type); } + if (mailboxId == null) return Mailbox.NO_MAILBOX; return mailboxId; } } From 24b56a412b24924b156a52569be9446f6292b6ef Mon Sep 17 00:00:00 2001 From: Ben Komalo Date: Thu, 30 Jun 2011 11:47:39 -0700 Subject: [PATCH 7/8] Fix b/4905749 using workaround We suspect the underlying bug is b/4981556 but it is a framework issue which needs time to fix. Some fragment state is not restored properly when going through orientation change, so navigation shortly after that will always crash. Change-Id: Id6b8714c2aeac5f6bf09e82aea5459bb19d0054d --- src/com/android/email/activity/UIControllerBase.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/com/android/email/activity/UIControllerBase.java b/src/com/android/email/activity/UIControllerBase.java index f883278ab..e7f043903 100644 --- a/src/com/android/email/activity/UIControllerBase.java +++ b/src/com/android/email/activity/UIControllerBase.java @@ -348,15 +348,14 @@ abstract class UIControllerBase implements MailboxListFragment.Callback, return; } if (!mRemovedFragments.contains(fragment)) { - // STOPSHIP Remove log/catch. b/4905749. + // STOPSHIP Remove log/catch. b/4905749 - b/4981556 Log.d(Logging.LOG_TAG, "Removing " + fragment); try { ft.remove(fragment); - } catch (RuntimeException ex) { - Log.e(Logging.LOG_TAG, "Got RuntimeException trying to remove fragment: " - + fragment, ex); + } catch (IllegalStateException ex) { + Log.e(Logging.LOG_TAG, "Swalling IllegalStateException due to known bug for " + + " fragment: " + fragment, ex); Log.e(Logging.LOG_TAG, Utility.dumpFragment(fragment)); - throw ex; } addFragmentToRemovalList(fragment); } From 235066297c897fc79b1d4b2630080e2094ef26cf Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Fri, 8 Jul 2011 16:59:49 -0700 Subject: [PATCH 8/8] Make nested folders selectable again Bug 5009065 Change-Id: I58bcb748848592414ec2334951e57693ac24cd39 --- .../email/activity/MailboxFragmentAdapter.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/com/android/email/activity/MailboxFragmentAdapter.java b/src/com/android/email/activity/MailboxFragmentAdapter.java index af7bf3a9e..8a0f75c75 100644 --- a/src/com/android/email/activity/MailboxFragmentAdapter.java +++ b/src/com/android/email/activity/MailboxFragmentAdapter.java @@ -220,9 +220,12 @@ class MailboxFragmentAdapter extends CursorAdapter { return isAccountRow((Cursor) getItem(position)); } - /** Returns {@code true} if the specified row is a mailbox. */ + /** + * Returns {@code true} if the specified row is a mailbox. + * ({@link #ROW_TYPE_MAILBOX}, {@link #ROW_TYPE_CURMAILBOX} and {@link #ROW_TYPE_SUBMAILBOX}) + */ boolean isMailboxRow(int position) { - return isMailboxrRow((Cursor) getItem(position)); + return isMailboxRow((Cursor) getItem(position)); } /** Returns {@code true} if the current row is of an account in the combined view. */ @@ -235,9 +238,12 @@ class MailboxFragmentAdapter extends CursorAdapter { return getRowType(cursor) == ROW_TYPE_HEADER; } - /** Returns {@code true} if the current row is a mailbox */ - private static boolean isMailboxrRow(Cursor cursor) { - return getRowType(cursor) == ROW_TYPE_MAILBOX; + /** + * Returns {@code true} if the current row is a mailbox. + * ({@link #ROW_TYPE_MAILBOX}, {@link #ROW_TYPE_CURMAILBOX} and {@link #ROW_TYPE_SUBMAILBOX}) + */ + private static boolean isMailboxRow(Cursor cursor) { + return !(isAccountRow(cursor) || isHeaderRow(cursor)); } /**