diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 72ddfb3d7..cb4f074d8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -294,7 +294,7 @@
@@ -339,7 +339,6 @@
-
@@ -661,6 +660,30 @@
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/emailcommon/Android.mk b/emailcommon/Android.mk
index 7f1428043..872e6c227 100644
--- a/emailcommon/Android.mk
+++ b/emailcommon/Android.mk
@@ -26,6 +26,8 @@ apache_src_dir := ../../UnifiedEmail/src/org
imported_unified_email_files := \
$(unified_email_unified_src_dir)/com/android/mail/utils/LogTag.java \
+ $(unified_email_src_dir)/com/android/mail/preferences/BasePreferenceMigrator.java \
+ $(unified_email_unified_src_dir)/com/android/mail/preferences/PreferenceMigrator.java \
$(unified_email_src_dir)/com/android/mail/utils/LogUtils.java \
$(unified_email_src_dir)/com/android/mail/providers/UIProvider.java
diff --git a/emailcommon/src/com/android/emailcommon/provider/Account.java b/emailcommon/src/com/android/emailcommon/provider/Account.java
index 06f8b70a2..15be6e8fd 100755
--- a/emailcommon/src/com/android/emailcommon/provider/Account.java
+++ b/emailcommon/src/com/android/emailcommon/provider/Account.java
@@ -57,10 +57,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
*/
public static final long NO_ACCOUNT = -1L;
- // Whether or not the user has asked for notifications of new mail in this account
- public final static int FLAGS_NOTIFY_NEW_MAIL = 1<<0;
- // Whether or not the user has asked for vibration notifications with all new mail
- public final static int FLAGS_VIBRATE = 1<<1;
// Bit mask for the account's deletion policy (see DELETE_POLICY_x below)
public static final int FLAGS_DELETE_POLICY_MASK = 1<<2 | 1<<3;
public static final int FLAGS_DELETE_POLICY_SHIFT = 2;
@@ -125,7 +121,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
public boolean mIsDefault; // note: callers should use getDefaultAccountId()
public String mCompatibilityUuid;
public String mSenderName;
- public String mRingtoneUri;
public String mProtocolVersion;
public int mNewMessageCount;
public String mSecuritySyncKey;
@@ -151,12 +146,11 @@ public final class Account extends EmailContent implements AccountColumns, Parce
public static final int CONTENT_IS_DEFAULT_COLUMN = 9;
public static final int CONTENT_COMPATIBILITY_UUID_COLUMN = 10;
public static final int CONTENT_SENDER_NAME_COLUMN = 11;
- public static final int CONTENT_RINGTONE_URI_COLUMN = 12;
- public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 13;
- public static final int CONTENT_NEW_MESSAGE_COUNT_COLUMN = 14;
- public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 15;
- public static final int CONTENT_SIGNATURE_COLUMN = 16;
- public static final int CONTENT_POLICY_KEY = 17;
+ public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 12;
+ public static final int CONTENT_NEW_MESSAGE_COUNT_COLUMN = 13;
+ public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 14;
+ public static final int CONTENT_SIGNATURE_COLUMN = 15;
+ public static final int CONTENT_POLICY_KEY = 16;
public static final String[] CONTENT_PROJECTION = new String[] {
RECORD_ID, AccountColumns.DISPLAY_NAME,
@@ -164,7 +158,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
AccountColumns.SYNC_INTERVAL, AccountColumns.HOST_AUTH_KEY_RECV,
AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS, AccountColumns.IS_DEFAULT,
AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME,
- AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION,
+ AccountColumns.PROTOCOL_VERSION,
AccountColumns.NEW_MESSAGE_COUNT, AccountColumns.SECURITY_SYNC_KEY,
AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY
};
@@ -205,10 +199,9 @@ public final class Account extends EmailContent implements AccountColumns, Parce
mBaseUri = CONTENT_URI;
// other defaults (policy)
- mRingtoneUri = "content://settings/system/notification_sound";
mSyncInterval = -1;
mSyncLookback = -1;
- mFlags = FLAGS_NOTIFY_NEW_MAIL;
+ mFlags = 0;
mCompatibilityUuid = UUID.randomUUID().toString();
}
@@ -258,7 +251,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
mIsDefault = cursor.getInt(CONTENT_IS_DEFAULT_COLUMN) == 1;
mCompatibilityUuid = cursor.getString(CONTENT_COMPATIBILITY_UUID_COLUMN);
mSenderName = cursor.getString(CONTENT_SENDER_NAME_COLUMN);
- mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN);
mProtocolVersion = cursor.getString(CONTENT_PROTOCOL_VERSION_COLUMN);
mNewMessageCount = cursor.getInt(CONTENT_NEW_MESSAGE_COUNT_COLUMN);
mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN);
@@ -360,8 +352,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
/**
* @return the flags for this account
- * @see #FLAGS_NOTIFY_NEW_MAIL
- * @see #FLAGS_VIBRATE
*/
public int getFlags() {
return mFlags;
@@ -369,29 +359,12 @@ public final class Account extends EmailContent implements AccountColumns, Parce
/**
* Set the flags for this account
- * @see #FLAGS_NOTIFY_NEW_MAIL
- * @see #FLAGS_VIBRATE
* @param newFlags the new value for the flags
*/
public void setFlags(int newFlags) {
mFlags = newFlags;
}
- /**
- * @return the ringtone Uri for this account
- */
- public String getRingtone() {
- return mRingtoneUri;
- }
-
- /**
- * Set the ringtone Uri for this account
- * @param newUri the new URI string for the ringtone for this account
- */
- public void setRingtone(String newUri) {
- mRingtoneUri = newUri;
- }
-
/**
* Set the "delete policy" as a simple 0,1,2 value set.
* @param newPolicy the new delete policy
@@ -819,7 +792,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
values.put(AccountColumns.IS_DEFAULT, mIsDefault);
values.put(AccountColumns.COMPATIBILITY_UUID, mCompatibilityUuid);
values.put(AccountColumns.SENDER_NAME, mSenderName);
- values.put(AccountColumns.RINGTONE_URI, mRingtoneUri);
values.put(AccountColumns.PROTOCOL_VERSION, mProtocolVersion);
values.put(AccountColumns.NEW_MESSAGE_COUNT, mNewMessageCount);
values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey);
@@ -870,7 +842,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
dest.writeByte(mIsDefault ? (byte)1 : (byte)0);
dest.writeString(mCompatibilityUuid);
dest.writeString(mSenderName);
- dest.writeString(mRingtoneUri);
dest.writeString(mProtocolVersion);
dest.writeInt(mNewMessageCount);
dest.writeString(mSecuritySyncKey);
@@ -909,7 +880,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
mIsDefault = in.readByte() == 1;
mCompatibilityUuid = in.readString();
mSenderName = in.readString();
- mRingtoneUri = in.readString();
mProtocolVersion = in.readString();
mNewMessageCount = in.readInt();
mSecuritySyncKey = in.readString();
diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
index 05ba4e28b..2506c695b 100755
--- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
+++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
@@ -60,11 +60,9 @@ import java.util.ArrayList;
*
*/
public abstract class EmailContent {
- public static final String[] NOTIFICATION_PROJECTION =
- new String[] {MailboxColumns.ID, MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT};
public static final int NOTIFICATION_MAILBOX_ID_COLUMN = 0;
public static final int NOTIFICATION_MAILBOX_UNREAD_COUNT_COLUMN = 1;
- public static final int NOTIFICATION_MAILBOX_MESSAGE_COUNT_COLUMN = 2;
+ public static final int NOTIFICATION_MAILBOX_UNSEEN_COUNT_COLUMN = 2;
// All classes share this
public static final String RECORD_ID = "_id";
@@ -520,11 +518,6 @@ public abstract class EmailContent {
mIntroText = cursor.getString(CONTENT_INTRO_TEXT_COLUMN);
mQuotedTextStartPos = cursor.getInt(CONTENT_QUOTED_TEXT_START_POS_COLUMN);
}
-
- public boolean update() {
- // TODO Auto-generated method stub
- return false;
- }
}
public interface MessageColumns {
@@ -578,6 +571,9 @@ public abstract class EmailContent {
public static final String THREAD_TOPIC = "threadTopic";
// For sync adapter use
public static final String SYNC_DATA = "syncData";
+
+ /** Boolean, unseen = 0, seen = 1 [INDEX] */
+ public static final String FLAG_SEEN = "flagSeen";
}
public static final class Message extends EmailContent implements SyncColumns, MessageColumns {
@@ -636,6 +632,7 @@ public abstract class EmailContent {
public static final int CONTENT_PROTOCOL_SEARCH_INFO_COLUMN = 22;
public static final int CONTENT_THREAD_TOPIC_COLUMN = 23;
public static final int CONTENT_SYNC_DATA_COLUMN = 24;
+ public static final int CONTENT_FLAG_SEEN_COLUMN = 25;
public static final String[] CONTENT_PROJECTION = new String[] {
RECORD_ID,
@@ -650,7 +647,7 @@ public abstract class EmailContent {
MessageColumns.BCC_LIST, MessageColumns.REPLY_TO_LIST,
SyncColumns.SERVER_TIMESTAMP, MessageColumns.MEETING_INFO,
MessageColumns.SNIPPET, MessageColumns.PROTOCOL_SEARCH_INFO,
- MessageColumns.THREAD_TOPIC, MessageColumns.SYNC_DATA
+ MessageColumns.THREAD_TOPIC, MessageColumns.SYNC_DATA, MessageColumns.FLAG_SEEN
};
public static final int LIST_ID_COLUMN = 0;
@@ -759,6 +756,7 @@ public abstract class EmailContent {
public long mTimeStamp;
public String mSubject;
public boolean mFlagRead = false;
+ public boolean mFlagSeen = false;
public int mFlagLoaded = FLAG_LOADED_UNLOADED;
public boolean mFlagFavorite = false;
public boolean mFlagAttachment = false;
@@ -878,6 +876,7 @@ public abstract class EmailContent {
values.put(MessageColumns.TIMESTAMP, mTimeStamp);
values.put(MessageColumns.SUBJECT, mSubject);
values.put(MessageColumns.FLAG_READ, mFlagRead);
+ values.put(MessageColumns.FLAG_SEEN, mFlagSeen);
values.put(MessageColumns.FLAG_LOADED, mFlagLoaded);
values.put(MessageColumns.FLAG_FAVORITE, mFlagFavorite);
values.put(MessageColumns.FLAG_ATTACHMENT, mFlagAttachment);
@@ -914,6 +913,7 @@ public abstract class EmailContent {
mTimeStamp = cursor.getLong(CONTENT_TIMESTAMP_COLUMN);
mSubject = cursor.getString(CONTENT_SUBJECT_COLUMN);
mFlagRead = cursor.getInt(CONTENT_FLAG_READ_COLUMN) == 1;
+ mFlagSeen = cursor.getInt(CONTENT_FLAG_SEEN_COLUMN) == 1;
mFlagLoaded = cursor.getInt(CONTENT_FLAG_LOADED_COLUMN);
mFlagFavorite = cursor.getInt(CONTENT_FLAG_FAVORITE_COLUMN) == 1;
mFlagAttachment = cursor.getInt(CONTENT_FLAG_ATTACHMENT_COLUMN) == 1;
@@ -936,11 +936,6 @@ public abstract class EmailContent {
mSyncData = cursor.getString(CONTENT_SYNC_DATA_COLUMN);
}
- public boolean update() {
- // TODO Auto-generated method stub
- return false;
- }
-
/*
* Override this so that we can store the Body first and link it to the Message
* Also, attachments when we get there...
@@ -1518,7 +1513,12 @@ public abstract class EmailContent {
public static final String COMPATIBILITY_UUID = "compatibilityUuid";
// User name (for outgoing messages)
public static final String SENDER_NAME = "senderName";
- // Ringtone
+ /**
+ * Ringtone
+ *
+ * @deprecated This is no longer used by anything except for creating the database.
+ */
+ @Deprecated
public static final String RINGTONE_URI = "ringtoneUri";
// Protocol version (arbitrary string, used by EAS currently)
public static final String PROTOCOL_VERSION = "protocolVersion";
@@ -1585,9 +1585,19 @@ public abstract class EmailContent {
public static final String UI_SYNC_STATUS = "uiSyncStatus";
// The UIProvider last sync result
public static final String UI_LAST_SYNC_RESULT = "uiLastSyncResult";
- // The UIProvider sync status
+ /**
+ * The UIProvider sync status
+ *
+ * @deprecated This is no longer used by anything except for creating the database.
+ */
+ @Deprecated
public static final String LAST_NOTIFIED_MESSAGE_KEY = "lastNotifiedMessageKey";
- // The UIProvider last sync result
+ /**
+ * The UIProvider last sync result
+ *
+ * @deprecated This is no longer used by anything except for creating the database.
+ */
+ @Deprecated
public static final String LAST_NOTIFIED_MESSAGE_COUNT = "lastNotifiedMessageCount";
// The total number of messages in the remote mailbox
public static final String TOTAL_COUNT = "totalCount";
diff --git a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
index 7307dbd47..ca8d9d6e6 100644
--- a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
+++ b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
@@ -63,8 +63,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
public long mLastTouchedTime;
public int mUiSyncStatus;
public int mUiLastSyncResult;
- public long mLastNotifiedMessageKey;
- public int mLastNotifiedMessageCount;
public int mTotalCount;
public String mHierarchicalName;
@@ -87,10 +85,8 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
public static final int CONTENT_LAST_TOUCHED_TIME_COLUMN = 16;
public static final int CONTENT_UI_SYNC_STATUS_COLUMN = 17;
public static final int CONTENT_UI_LAST_SYNC_RESULT_COLUMN = 18;
- public static final int CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN = 19;
- public static final int CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN = 20;
- public static final int CONTENT_TOTAL_COUNT_COLUMN = 21;
- public static final int CONTENT_HIERARCHICAL_NAME_COLUMN = 22;
+ public static final int CONTENT_TOTAL_COUNT_COLUMN = 19;
+ public static final int CONTENT_HIERARCHICAL_NAME_COLUMN = 20;
/**
* NOTE: If fields are added or removed, the method {@link #getHashes()}
@@ -104,7 +100,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
MailboxColumns.FLAG_VISIBLE, MailboxColumns.FLAGS, MailboxColumns.VISIBLE_LIMIT,
MailboxColumns.SYNC_STATUS, MailboxColumns.PARENT_KEY, MailboxColumns.LAST_TOUCHED_TIME,
MailboxColumns.UI_SYNC_STATUS, MailboxColumns.UI_LAST_SYNC_RESULT,
- MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT,
MailboxColumns.TOTAL_COUNT, MailboxColumns.HIERARCHICAL_NAME
};
@@ -334,8 +329,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
mLastTouchedTime = cursor.getLong(CONTENT_LAST_TOUCHED_TIME_COLUMN);
mUiSyncStatus = cursor.getInt(CONTENT_UI_SYNC_STATUS_COLUMN);
mUiLastSyncResult = cursor.getInt(CONTENT_UI_LAST_SYNC_RESULT_COLUMN);
- mLastNotifiedMessageKey = cursor.getLong(CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN);
- mLastNotifiedMessageCount = cursor.getInt(CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN);
mTotalCount = cursor.getInt(CONTENT_TOTAL_COUNT_COLUMN);
mHierarchicalName = cursor.getString(CONTENT_HIERARCHICAL_NAME_COLUMN);
}
@@ -361,8 +354,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
values.put(MailboxColumns.LAST_TOUCHED_TIME, mLastTouchedTime);
values.put(MailboxColumns.UI_SYNC_STATUS, mUiSyncStatus);
values.put(MailboxColumns.UI_LAST_SYNC_RESULT, mUiLastSyncResult);
- values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, mLastNotifiedMessageKey);
- values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT, mLastNotifiedMessageCount);
values.put(MailboxColumns.TOTAL_COUNT, mTotalCount);
values.put(MailboxColumns.HIERARCHICAL_NAME, mHierarchicalName);
return values;
@@ -554,10 +545,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
= mUiSyncStatus;
hash[CONTENT_UI_LAST_SYNC_RESULT_COLUMN]
= mUiLastSyncResult;
- hash[CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN]
- = mLastNotifiedMessageKey;
- hash[CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN]
- = mLastNotifiedMessageCount;
hash[CONTENT_TOTAL_COUNT_COLUMN]
= mTotalCount;
hash[CONTENT_HIERARCHICAL_NAME_COLUMN]
@@ -594,8 +581,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
dest.writeLong(mLastTouchedTime);
dest.writeInt(mUiSyncStatus);
dest.writeInt(mUiLastSyncResult);
- dest.writeLong(mLastNotifiedMessageKey);
- dest.writeInt(mLastNotifiedMessageCount);
dest.writeInt(mTotalCount);
dest.writeString(mHierarchicalName);
}
@@ -621,8 +606,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
mLastTouchedTime = in.readLong();
mUiSyncStatus = in.readInt();
mUiLastSyncResult = in.readInt();
- mLastNotifiedMessageKey = in.readLong();
- mLastNotifiedMessageCount = in.readInt();
mTotalCount = in.readInt();
mHierarchicalName = in.readString();
}
diff --git a/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java
index e4ac3001a..e5496de9a 100644
--- a/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java
+++ b/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Debug;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
@@ -181,6 +182,16 @@ public abstract class ServiceProxy {
}
public void waitForCompletion() {
+ /*
+ * onServiceConnected() is always called on the main thread, and we block the current thread
+ * for up to 10 seconds as a timeout. If we're currently on the main thread,
+ * onServiceConnected() is not called until our timeout elapses (and the UI is frozen for
+ * the duration).
+ */
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new IllegalStateException("This cannot be called on the main thread.");
+ }
+
synchronized (mConnection) {
long time = System.currentTimeMillis();
try {
diff --git a/emailcommon/src/com/android/emailcommon/utility/Utility.java b/emailcommon/src/com/android/emailcommon/utility/Utility.java
index 28d0b41e9..f6d3e2df7 100644
--- a/emailcommon/src/com/android/emailcommon/utility/Utility.java
+++ b/emailcommon/src/com/android/emailcommon/utility/Utility.java
@@ -50,10 +50,8 @@ import com.android.emailcommon.provider.EmailContent.AccountColumns;
import com.android.emailcommon.provider.EmailContent.Attachment;
import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
-import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.provider.ProviderUnavailableException;
import java.io.ByteArrayInputStream;
@@ -903,72 +901,6 @@ public class Utility {
}
}
- /**
- * Updates the last seen message key in the mailbox data base for the INBOX of the currently
- * shown account. If the account is {@link Account#ACCOUNT_ID_COMBINED_VIEW}, the INBOX for
- * all accounts are updated.
- * @return an {@link EmailAsyncTask} for test only.
- */
- public static EmailAsyncTask updateLastNotifiedMessageKey(
- final Context context, final long mailboxId) {
- return EmailAsyncTask.runAsyncParallel(new Runnable() {
- private void updateLastSeenMessageKeyForMailbox(long mailboxId) {
- ContentResolver resolver = context.getContentResolver();
- if (mailboxId == Mailbox.QUERY_ALL_INBOXES) {
- Cursor c = resolver.query(
- Mailbox.CONTENT_URI, EmailContent.ID_PROJECTION, Mailbox.TYPE + "=?",
- new String[] { Integer.toString(Mailbox.TYPE_INBOX) }, null);
- if (c == null) throw new ProviderUnavailableException();
- try {
- while (c.moveToNext()) {
- final long id = c.getLong(EmailContent.ID_PROJECTION_COLUMN);
- updateLastSeenMessageKeyForMailbox(id);
- }
- } finally {
- c.close();
- }
- } else if (mailboxId > 0L) {
- Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
- // mailbox has been removed
- if (mailbox == null) {
- return;
- }
- // We use the highest _id for the account the mailbox table as the "last seen
- // message key". We don't care if the message has been read or not. We only
- // need a point at which we can compare against in the future. By setting this
- // value, we are claiming that every message before this has potentially been
- // seen by the user.
- long mostRecentMessageId = Utility.getFirstRowLong(context,
- ContentUris.withAppendedId(
- EmailContent.MAILBOX_MOST_RECENT_MESSAGE_URI, mailboxId),
- Message.ID_COLUMN_PROJECTION, null, null, null,
- Message.ID_MAILBOX_COLUMN_ID, -1L);
- long lastNotifiedMessageId = mailbox.mLastNotifiedMessageKey;
- // Only update the db if the value has changed
- if (mostRecentMessageId != lastNotifiedMessageId) {
- Log.d(Logging.LOG_TAG, "Most recent = " + mostRecentMessageId +
- ", last notified: " + lastNotifiedMessageId +
- "; updating last notified");
- ContentValues values = mailbox.toContentValues();
- values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, mostRecentMessageId);
- resolver.update(
- Mailbox.CONTENT_URI,
- values,
- EmailContent.ID_SELECTION,
- new String[] { Long.toString(mailbox.mId) });
- } else {
- Log.d(Logging.LOG_TAG, "Most recent = last notified; no change");
- }
- }
- }
-
- @Override
- public void run() {
- updateLastSeenMessageKeyForMailbox(mailboxId);
- }
- });
- }
-
public static long[] toPrimitiveLongArray(Collection collection) {
// Need to do this manually because we're converting to a primitive long array, not
// a Long array.
diff --git a/res/drawable-hdpi/stat_notify_email_generic.png b/res/drawable-hdpi/stat_notify_email_generic.png
deleted file mode 100644
index d40e2fe1e..000000000
Binary files a/res/drawable-hdpi/stat_notify_email_generic.png and /dev/null differ
diff --git a/res/drawable-xhdpi/stat_notify_email_generic.png b/res/drawable-xhdpi/stat_notify_email_generic.png
deleted file mode 100644
index 0317760ee..000000000
Binary files a/res/drawable-xhdpi/stat_notify_email_generic.png and /dev/null differ
diff --git a/res/xml/account_settings_preferences.xml b/res/xml/account_settings_preferences.xml
index 2c6b83026..04fa921bb 100755
--- a/res/xml/account_settings_preferences.xml
+++ b/res/xml/account_settings_preferences.xml
@@ -94,22 +94,19 @@
android:title="@string/account_settings_notifications">
-
+
diff --git a/res/xml/authenticator_alternate.xml b/res/xml/authenticator_alternate.xml
index 003e7f720..1403535c8 100644
--- a/res/xml/authenticator_alternate.xml
+++ b/res/xml/authenticator_alternate.xml
@@ -25,7 +25,7 @@
diff --git a/res/xml/authenticator_eas.xml b/res/xml/authenticator_eas.xml
index 4ee83d2c1..eed4c1019 100644
--- a/res/xml/authenticator_eas.xml
+++ b/res/xml/authenticator_eas.xml
@@ -23,7 +23,7 @@
diff --git a/res/xml/authenticator_imap.xml b/res/xml/authenticator_imap.xml
index 72bcf0431..75445645c 100644
--- a/res/xml/authenticator_imap.xml
+++ b/res/xml/authenticator_imap.xml
@@ -23,7 +23,7 @@
diff --git a/res/xml/authenticator_legacy_eas.xml b/res/xml/authenticator_legacy_eas.xml
index b9e31c3aa..041ad2011 100644
--- a/res/xml/authenticator_legacy_eas.xml
+++ b/res/xml/authenticator_legacy_eas.xml
@@ -23,7 +23,7 @@
diff --git a/res/xml/authenticator_legacy_email.xml b/res/xml/authenticator_legacy_email.xml
index 5aad049c8..532cc058b 100644
--- a/res/xml/authenticator_legacy_email.xml
+++ b/res/xml/authenticator_legacy_email.xml
@@ -23,7 +23,7 @@
diff --git a/res/xml/authenticator_legacy_imap.xml b/res/xml/authenticator_legacy_imap.xml
index 8182cef3c..43e0e5d17 100644
--- a/res/xml/authenticator_legacy_imap.xml
+++ b/res/xml/authenticator_legacy_imap.xml
@@ -23,7 +23,7 @@
diff --git a/res/xml/authenticator_pop3.xml b/res/xml/authenticator_pop3.xml
index 47e022106..6bced4fce 100644
--- a/res/xml/authenticator_pop3.xml
+++ b/res/xml/authenticator_pop3.xml
@@ -23,7 +23,7 @@
diff --git a/res/xml/general_preferences.xml b/res/xml/general_preferences.xml
index 3bc17df1f..f3c0a642f 100644
--- a/res/xml/general_preferences.xml
+++ b/res/xml/general_preferences.xml
@@ -56,8 +56,8 @@
diff --git a/src/com/android/email/EmailIntentService.java b/src/com/android/email/EmailIntentService.java
new file mode 100644
index 000000000..005df2f71
--- /dev/null
+++ b/src/com/android/email/EmailIntentService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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.content.Intent;
+
+import com.android.mail.MailIntentService;
+import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
+
+/**
+ * A service to handle various intents asynchronously.
+ */
+public class EmailIntentService extends MailIntentService {
+ private static final String LOG_TAG = LogTag.getLogTag();
+
+ public EmailIntentService() {
+ super("EmailIntentService");
+ }
+
+ @Override
+ protected void onHandleIntent(final Intent intent) {
+ super.onHandleIntent(intent);
+
+ LogUtils.v(LOG_TAG, "Handling intent %s", intent);
+ }
+}
diff --git a/src/com/android/email/NotificationController.java b/src/com/android/email/NotificationController.java
index c46d8a034..7aacbe800 100644
--- a/src/com/android/email/NotificationController.java
+++ b/src/com/android/email/NotificationController.java
@@ -22,54 +22,45 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
-import android.text.SpannableString;
+import android.provider.Settings;
+import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
-import android.text.style.TextAppearanceSpan;
-import android.util.Log;
-import com.android.email.activity.ContactStatusLoader;
import com.android.email.activity.setup.AccountSecurity;
import com.android.email.activity.setup.AccountSettings;
import com.android.email.provider.EmailProvider;
-import com.android.email.service.EmailBroadcastProcessorService;
-import com.android.email2.ui.MailActivityEmail;
-import com.android.emailcommon.Logging;
-import com.android.emailcommon.mail.Address;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Attachment;
-import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.utility.EmailAsyncTask;
-import com.android.emailcommon.utility.Utility;
-import com.android.mail.providers.Conversation;
+import com.android.mail.preferences.FolderPreferences;
import com.android.mail.providers.Folder;
import com.android.mail.providers.UIProvider;
-import com.android.mail.utils.Utils;
-import com.google.common.annotations.VisibleForTesting;
+import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.NotificationUtils;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
/**
* Class that manages notifications.
*/
public class NotificationController {
- private static final String TAG = "NotificationController";
+ private static final String LOG_TAG = LogTag.getLogTag();
/** Reserved for {@link com.android.exchange.CalendarSyncEnabler} */
@SuppressWarnings("unused")
@@ -84,56 +75,24 @@ public class NotificationController {
private static final int NOTIFICATION_ID_BASE_SECURITY_NEEDED = 0x30000000;
private static final int NOTIFICATION_ID_BASE_SECURITY_CHANGED = 0x40000000;
- /** Selection to retrieve accounts that should we notify user for changes */
- private final static String NOTIFIED_ACCOUNT_SELECTION =
- Account.FLAGS + "&" + Account.FLAGS_NOTIFY_NEW_MAIL + " != 0";
-
- private static final String NEW_MAIL_MAILBOX_ID = "com.android.email.new_mail.mailboxId";
- private static final String NEW_MAIL_MESSAGE_ID = "com.android.email.new_mail.messageId";
- private static final String NEW_MAIL_MESSAGE_COUNT = "com.android.email.new_mail.messageCount";
- private static final String NEW_MAIL_UNREAD_COUNT = "com.android.email.new_mail.unreadCount";
-
private static NotificationThread sNotificationThread;
private static Handler sNotificationHandler;
private static NotificationController sInstance;
private final Context mContext;
private final NotificationManager mNotificationManager;
- private final AudioManager mAudioManager;
- private final Bitmap mGenericSenderIcon;
- private final Bitmap mGenericMultipleSenderIcon;
private final Clock mClock;
/** Maps account id to its observer */
- private final HashMap mNotificationMap;
+ private final Map mNotificationMap =
+ new HashMap();
private ContentObserver mAccountObserver;
- /**
- * Timestamp indicating when the last message notification sound was played.
- * Used for throttling.
- */
- private long mLastMessageNotifyTime;
-
- /**
- * Minimum interval between notification sounds.
- * Since a long sync (either on account setup or after a long period of being offline) can cause
- * several notifications consecutively, it can be pretty overwhelming to get a barrage of
- * notification sounds. Throttle them using this value.
- */
- private static final long MIN_SOUND_INTERVAL_MS = 15 * 1000; // 15 seconds
-
/** Constructor */
- @VisibleForTesting
- NotificationController(Context context, Clock clock) {
+ private NotificationController(Context context, Clock clock) {
mContext = context.getApplicationContext();
EmailContent.init(context);
mNotificationManager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mGenericSenderIcon = BitmapFactory.decodeResource(mContext.getResources(),
- R.drawable.ic_contact_picture);
- mGenericMultipleSenderIcon = BitmapFactory.decodeResource(mContext.getResources(),
- R.drawable.ic_notification_multiple_mail_holo_dark);
mClock = clock;
- mNotificationMap = new HashMap();
}
/** Singleton access */
@@ -149,16 +108,16 @@ public class NotificationController {
* @param notificationId the notification id to check
* @return whether or not the notification must be "ongoing"
*/
- private boolean needsOngoingNotification(int notificationId) {
+ private static boolean needsOngoingNotification(int notificationId) {
// "Security needed" must be ongoing so that the user doesn't close it; otherwise, sync will
// be prevented until a reboot. Consider also doing this for password expired.
return (notificationId & NOTIFICATION_ID_BASE_MASK) == NOTIFICATION_ID_BASE_SECURITY_NEEDED;
}
/**
- * Returns a {@link Notification.Builder}} for an event with the given account. The account
- * contains specific rules on ring tone usage and these will be used to modify the notification
- * behaviour.
+ * Returns a {@link android.support.v4.app.NotificationCompat.Builder} for an event with the
+ * given account. The account contains specific rules on ring tone usage and these will be used
+ * to modify the notification behaviour.
*
* @param accountId The id of the account this notification is being built for.
* @param ticker Text displayed when the notification is first shown. May be {@code null}.
@@ -166,14 +125,13 @@ public class NotificationController {
* @param contentText The second line of text. May NOT be {@code null}.
* @param intent The intent to start if the user clicks on the notification.
* @param largeIcon A large icon. May be {@code null}
- * @param number A number to display using {@link Builder#setNumber(int)}. May
- * be {@code null}.
+ * @param number A number to display using {@link Builder#setNumber(int)}. May be {@code null}.
* @param enableAudio If {@code false}, do not play any sound. Otherwise, play sound according
* to the settings for the given account.
* @return A {@link Notification} that can be sent to the notification service.
*/
- private Notification.Builder createBaseAccountNotificationBuilder(long accountId, String ticker,
- CharSequence title, String contentText, Intent intent, Bitmap largeIcon,
+ private NotificationCompat.Builder createBaseAccountNotificationBuilder(long accountId,
+ String ticker, CharSequence title, String contentText, Intent intent, Bitmap largeIcon,
Integer number, boolean enableAudio, boolean ongoing) {
// Pending Intent
PendingIntent pending = null;
@@ -183,13 +141,13 @@ public class NotificationController {
}
// NOTE: the ticker is not shown for notifications in the Holo UX
- final Notification.Builder builder = new Notification.Builder(mContext)
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
.setContentTitle(title)
.setContentText(contentText)
.setContentIntent(pending)
.setLargeIcon(largeIcon)
.setNumber(number == null ? 0 : number)
- .setSmallIcon(R.drawable.stat_notify_email_generic)
+ .setSmallIcon(R.drawable.stat_notify_email)
.setWhen(mClock.getTime())
.setTicker(ticker)
.setOngoing(ongoing);
@@ -214,16 +172,16 @@ public class NotificationController {
*/
private void showNotification(long accountId, String ticker, String title,
String contentText, Intent intent, int notificationId) {
- final Notification.Builder builder = createBaseAccountNotificationBuilder(accountId, ticker,
- title, contentText, intent, null, null, true,
+ final NotificationCompat.Builder builder = createBaseAccountNotificationBuilder(accountId,
+ ticker, title, contentText, intent, null, null, true,
needsOngoingNotification(notificationId));
- mNotificationManager.notify(notificationId, builder.getNotification());
+ mNotificationManager.notify(notificationId, builder.build());
}
/**
* Returns a notification ID for new message notifications for the given account.
*/
- private int getNewMessageNotificationId(long mailboxId) {
+ private static int getNewMessageNotificationId(long mailboxId) {
// We assume accountId will always be less than 0x0FFFFFFF; is there a better way?
return (int) (NOTIFICATION_ID_BASE_NEW_MESSAGES + mailboxId);
}
@@ -234,42 +192,20 @@ public class NotificationController {
* database changes, we save the state [e.g. message ID and count] of the most recent
* notification shown to the user. And, when we start observing database changes, we restore
* the saved state.
- * @param watch If {@code true}, we register observers for all accounts whose settings have
- * notifications enabled. Otherwise, all observers are unregistered.
*/
- public void watchForMessages(final boolean watch) {
- if (MailActivityEmail.DEBUG) {
- Log.d(Logging.LOG_TAG, "Notifications being toggled: " + watch);
- }
- // Don't create the thread if we're only going to stop watching
- if (!watch && sNotificationThread == null) return;
-
+ public void watchForMessages() {
ensureHandlerExists();
// Run this on the message notification handler
sNotificationHandler.post(new Runnable() {
@Override
public void run() {
ContentResolver resolver = mContext.getContentResolver();
- if (!watch) {
- unregisterMessageNotification(Account.ACCOUNT_ID_COMBINED_VIEW);
- if (mAccountObserver != null) {
- resolver.unregisterContentObserver(mAccountObserver);
- mAccountObserver = null;
- }
-
- // tear down the event loop
- sNotificationThread.quit();
- sNotificationThread = null;
- return;
- }
// otherwise, start new observers for all notified accounts
registerMessageNotification(Account.ACCOUNT_ID_COMBINED_VIEW);
// If we're already observing account changes, don't do anything else
if (mAccountObserver == null) {
- if (MailActivityEmail.DEBUG) {
- Log.i(Logging.LOG_TAG, "Observing account changes for notifications");
- }
+ LogUtils.i(LOG_TAG, "Observing account changes for notifications");
mAccountObserver = new AccountContentObserver(sNotificationHandler, mContext);
resolver.registerContentObserver(Account.NOTIFIER_URI, true, mAccountObserver);
}
@@ -294,12 +230,12 @@ public class NotificationController {
* {@link Account#ACCOUNT_ID_COMBINED_VIEW} to register observers for all
* accounts that allow for user notification.
*/
- private void registerMessageNotification(long accountId) {
+ private void registerMessageNotification(final long accountId) {
ContentResolver resolver = mContext.getContentResolver();
if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
Cursor c = resolver.query(
Account.CONTENT_URI, EmailContent.ID_PROJECTION,
- NOTIFIED_ACCOUNT_SELECTION, null, null);
+ null, null, null);
try {
while (c.moveToNext()) {
long id = c.getLong(EmailContent.ID_PROJECTION_COLUMN);
@@ -311,9 +247,7 @@ public class NotificationController {
} else {
ContentObserver obs = mNotificationMap.get(accountId);
if (obs != null) return; // we're already observing; nothing to do
- if (MailActivityEmail.DEBUG) {
- Log.i(Logging.LOG_TAG, "Registering for notifications for account " + accountId);
- }
+ LogUtils.i(LOG_TAG, "Registering for notifications for account " + accountId);
ContentObserver observer = new MessageContentObserver(
sNotificationHandler, mContext, accountId);
resolver.registerContentObserver(Message.NOTIFIER_URI, true, observer);
@@ -331,21 +265,17 @@ public class NotificationController {
* @param accountId The ID of the account to unregister from. To unregister all accounts that
* have observers, specify an ID of {@link Account#ACCOUNT_ID_COMBINED_VIEW}.
*/
- private void unregisterMessageNotification(long accountId) {
+ private void unregisterMessageNotification(final long accountId) {
ContentResolver resolver = mContext.getContentResolver();
if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
- if (MailActivityEmail.DEBUG) {
- Log.i(Logging.LOG_TAG, "Unregistering notifications for all accounts");
- }
+ LogUtils.i(LOG_TAG, "Unregistering notifications for all accounts");
// cancel all existing message observers
for (ContentObserver observer : mNotificationMap.values()) {
resolver.unregisterContentObserver(observer);
}
mNotificationMap.clear();
} else {
- if (MailActivityEmail.DEBUG) {
- Log.i(Logging.LOG_TAG, "Unregistering notifications for account " + accountId);
- }
+ LogUtils.i(LOG_TAG, "Unregistering notifications for account " + accountId);
ContentObserver observer = mNotificationMap.remove(accountId);
if (observer != null) {
resolver.unregisterContentObserver(observer);
@@ -353,411 +283,60 @@ public class NotificationController {
}
}
- /**
- * Returns a picture of the sender of the given message. If no picture is available, returns
- * {@code null}.
- *
- * NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS)
- */
- private Bitmap getSenderPhoto(Message message) {
- Address sender = Address.unpackFirst(message.mFrom);
- if (sender == null) {
- return null;
- }
- String email = sender.getAddress();
- if (TextUtils.isEmpty(email)) {
- return null;
- }
- Bitmap photo = ContactStatusLoader.getContactInfo(mContext, email).mPhoto;
-
- if (photo != null) {
- final Resources res = mContext.getResources();
- final int idealIconHeight =
- res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
- final int idealIconWidth =
- res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
-
- if (photo.getHeight() < idealIconHeight) {
- // We should scale this image to fit the intended size
- photo = Bitmap.createScaledBitmap(
- photo, idealIconWidth, idealIconHeight, true);
- }
- }
- return photo;
- }
-
public static final String EXTRA_ACCOUNT = "account";
public static final String EXTRA_CONVERSATION = "conversationUri";
public static final String EXTRA_FOLDER = "folder";
- private Intent createViewConversationIntent(Conversation conversation, Folder folder,
- com.android.mail.providers.Account account) {
- final Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- intent.putExtra(EXTRA_ACCOUNT, account.serialize());
- if (folder != null) {
- intent.setDataAndType(folder.uri, account.mimeType);
- intent.putExtra(EXTRA_FOLDER, Folder.toString(folder));
- }
- intent.putExtra(EXTRA_CONVERSATION, conversation);
- return intent;
- }
-
- private Intent createViewMailboxIntent(com.android.mail.providers.Account account,
- Folder folder) {
- final Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- intent.putExtra(EXTRA_ACCOUNT, account.serialize());
- if (folder != null) {
- intent.setDataAndType(folder.uri, account.mimeType);
- intent.putExtra(EXTRA_FOLDER, Folder.toString(folder));
- }
- return intent;
- }
-
- private Cursor getUiCursor(Uri uri, String[] projection) {
- Cursor c = mContext.getContentResolver().query(uri, projection, null, null, null);
- if (c == null) return null;
- if (c.moveToFirst()) {
- return c;
- } else {
- c.close();
- return null;
- }
- }
-
- private Intent createViewConversationIntent(Message message) {
- Cursor c = getUiCursor(EmailProvider.uiUri("uiaccount", message.mAccountKey),
- UIProvider.ACCOUNTS_PROJECTION);
- if (c == null) {
- Log.w(TAG, "Can't find account for message " + message.mId);
- return null;
- }
- com.android.mail.providers.Account acct = new com.android.mail.providers.Account(c);
- c.close();
- c = getUiCursor(EmailProvider.uiUri("uifolder", message.mMailboxKey),
- UIProvider.FOLDERS_PROJECTION);
- if (c == null) {
- Log.w(TAG, "Can't find folder for message " + message.mId + ", folder " +
- message.mMailboxKey);
- return null;
- }
- Folder folder = new Folder(c);
- c.close();
- c = getUiCursor(EmailProvider.uiUri("uiconversation", message.mId),
- UIProvider.CONVERSATION_PROJECTION);
- if (c == null) {
- Log.w(TAG, "Can't find conversation for message " + message.mId);
- return null;
- }
- Conversation conv = new Conversation(c);
- c.close();
- return createViewConversationIntent(conv, folder, acct);
- }
-
- private Intent createViewMailboxIntentForMessage(Message message) {
- Cursor c = null;
- com.android.mail.providers.Account acct = null;
- try {
- c = getUiCursor(EmailProvider.uiUri("uiaccount", message.mAccountKey),
- UIProvider.ACCOUNTS_PROJECTION);
- if (c == null) {
- Log.w(TAG, "Can't find account for message " + message.mId);
- return null;
- }
- acct = new com.android.mail.providers.Account(c);
- } finally {
- if (c != null) {
- c.close();
- c = null;
- }
- }
-
- Folder folder = null;
- try {
- c = getUiCursor(EmailProvider.uiUri("uifolder", message.mMailboxKey),
- UIProvider.FOLDERS_PROJECTION);
- if (c == null) {
- Log.w(TAG, "Can't find folder for message " + message.mId + ", folder " +
- message.mMailboxKey);
- return null;
- }
- folder = new Folder(c);
- } finally {
- if (c != null) {
- c.close();
- c = null;
- }
- }
- return createViewMailboxIntent(acct, folder);
- }
-
- /**
- * Returns a "new message" notification for the given account.
- *
- * NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS)
- */
- @VisibleForTesting
- Notification createNewMessageNotification(long mailboxId, long newMessageId,
- int unseenMessageCount, int unreadCount) {
- final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
- if (mailbox == null) {
- return null;
- }
- final Account account = Account.restoreAccountWithId(mContext, mailbox.mAccountKey);
- if (account == null) {
- return null;
- }
- // Get the latest message
- final Message message = Message.restoreMessageWithId(mContext, newMessageId);
- if (message == null) {
- return null; // no message found???
- }
-
- String senderName = Address.toFriendly(Address.unpack(message.mFrom));
- if (senderName == null) {
- senderName = ""; // Happens when a message has no from.
- }
- final boolean multipleUnseen = unseenMessageCount > 1;
- final Bitmap senderPhoto = multipleUnseen
- ? mGenericMultipleSenderIcon
- : getSenderPhoto(message);
- final SpannableString title = getNewMessageTitle(senderName, unseenMessageCount);
- // TODO: add in display name on the second line for the text, once framework supports
- // multiline texts.
- // Show account name if an inbox; otherwise mailbox name
- final String text = multipleUnseen
- ? ((mailbox.mType == Mailbox.TYPE_INBOX) ? account.mDisplayName :
- mailbox.mDisplayName)
- : message.mSubject;
- final Bitmap largeIcon = senderPhoto != null ? senderPhoto : mGenericSenderIcon;
- final Integer number = unreadCount > 1 ? unreadCount : null;
- final Intent intent;
- if (multipleUnseen) {
- intent = createViewMailboxIntentForMessage(message);
- } else {
- intent = createViewConversationIntent(message);
- }
- if (intent == null) {
- return null;
- }
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
- Intent.FLAG_ACTIVITY_TASK_ON_HOME);
- long now = mClock.getTime();
- boolean enableAudio = (now - mLastMessageNotifyTime) > MIN_SOUND_INTERVAL_MS;
- final Notification.Builder builder = createBaseAccountNotificationBuilder(
- mailbox.mAccountKey, title.toString(), title, text,
- intent, largeIcon, number, enableAudio, false);
- if (Utils.isRunningJellybeanOrLater()) {
- // For a new-style notification
- if (multipleUnseen) {
- final Cursor messageCursor =
- mContext.getContentResolver().query(ContentUris.withAppendedId(
- EmailContent.MAILBOX_NOTIFICATION_URI, mailbox.mAccountKey),
- EmailContent.NOTIFICATION_PROJECTION, null, null, null);
-
- try {
- if (messageCursor != null && messageCursor.getCount() > 0) {
- final int maxNumDigestItems = mContext.getResources().getInteger(
- R.integer.max_num_notification_digest_items);
- // The body of the notification is the account name, or the label name.
- builder.setSubText(text);
-
- Notification.InboxStyle digest = new Notification.InboxStyle(builder);
-
- digest.setBigContentTitle(title);
-
- int numDigestItems = 0;
- // We can assume that the current position of the cursor is on the
- // newest message
- messageCursor.moveToFirst();
- do {
- final long messageId =
- messageCursor.getLong(EmailContent.ID_PROJECTION_COLUMN);
-
- // Get the latest message
- final Message digestMessage =
- Message.restoreMessageWithId(mContext, messageId);
- if (digestMessage != null) {
- final CharSequence digestLine =
- getSingleMessageInboxLine(mContext, digestMessage);
- digest.addLine(digestLine);
- numDigestItems++;
- }
- } while (numDigestItems <= maxNumDigestItems && messageCursor.moveToNext());
-
- // We want to clear the content text in this case. The content text would
- // have been set in createBaseAccountNotificationBuilder, but since the
- // same string was set in as the subtext, we don't want to show a
- // duplicate string.
- builder.setContentText(null);
- }
- } finally {
- if (messageCursor != null) {
- messageCursor.close();
- }
- }
- } else {
- // The notification content will be the subject of the conversation.
- builder.setContentText(getSingleMessageLittleText(mContext, message.mSubject));
-
- // The notification subtext will be the subject of the conversation for inbox
- // notifications, or will based on the the label name for user label notifications.
- builder.setSubText(account.mDisplayName);
-
- final Notification.BigTextStyle bigText = new Notification.BigTextStyle(builder);
- bigText.bigText(getSingleMessageBigText(mContext, message));
- }
- }
-
- mLastMessageNotifyTime = now;
- return builder.getNotification();
- }
-
- /**
- * Sets the bigtext for a notification for a single new conversation
- * @param context
- * @param message New message that triggered the notification.
- * @return a {@link CharSequence} suitable for use in {@link Notification.BigTextStyle}
- */
- private static CharSequence getSingleMessageInboxLine(Context context, Message message) {
- final String subject = message.mSubject;
- final String snippet = message.mSnippet;
- final String senders = Address.toFriendly(Address.unpack(message.mFrom));
-
- final String subjectSnippet = !TextUtils.isEmpty(subject) ? subject : snippet;
-
- final TextAppearanceSpan notificationPrimarySpan =
- new TextAppearanceSpan(context, R.style.NotificationPrimaryText);
-
- if (TextUtils.isEmpty(senders)) {
- // If the senders are empty, just use the subject/snippet.
- return subjectSnippet;
- }
- else if (TextUtils.isEmpty(subjectSnippet)) {
- // If the subject/snippet is empty, just use the senders.
- final SpannableString spannableString = new SpannableString(senders);
- spannableString.setSpan(notificationPrimarySpan, 0, senders.length(), 0);
-
- return spannableString;
- } else {
- final String formatString = context.getResources().getString(
- R.string.multiple_new_message_notification_item);
- final TextAppearanceSpan notificationSecondarySpan =
- new TextAppearanceSpan(context, R.style.NotificationSecondaryText);
-
- final String instantiatedString = String.format(formatString, senders, subjectSnippet);
-
- final SpannableString spannableString = new SpannableString(instantiatedString);
-
- final boolean isOrderReversed = formatString.indexOf("%2$s") <
- formatString.indexOf("%1$s");
- final int primaryOffset =
- (isOrderReversed ? instantiatedString.lastIndexOf(senders) :
- instantiatedString.indexOf(senders));
- final int secondaryOffset =
- (isOrderReversed ? instantiatedString.lastIndexOf(subjectSnippet) :
- instantiatedString.indexOf(subjectSnippet));
- spannableString.setSpan(notificationPrimarySpan,
- primaryOffset, primaryOffset + senders.length(), 0);
- spannableString.setSpan(notificationSecondarySpan,
- secondaryOffset, secondaryOffset + subjectSnippet.length(), 0);
- return spannableString;
- }
- }
-
- /**
- * Sets the bigtext for a notification for a single new conversation
- * @param context
- * @param subject Subject of the new message that triggered the notification
- * @return a {@link CharSequence} suitable for use in {@link Notification.ContentText}
- */
- private static CharSequence getSingleMessageLittleText(Context context, String subject) {
- if (subject == null) {
- return null;
- }
- final TextAppearanceSpan notificationSubjectSpan = new TextAppearanceSpan(
- context, R.style.NotificationPrimaryText);
-
- final SpannableString spannableString = new SpannableString(subject);
- spannableString.setSpan(notificationSubjectSpan, 0, subject.length(), 0);
-
- return spannableString;
- }
-
-
- /**
- * Sets the bigtext for a notification for a single new conversation
- * @param context
- * @param message New message that triggered the notification
- * @return a {@link CharSequence} suitable for use in {@link Notification.BigTextStyle}
- */
- private static CharSequence getSingleMessageBigText(Context context, Message message) {
- final TextAppearanceSpan notificationSubjectSpan = new TextAppearanceSpan(
- context, R.style.NotificationPrimaryText);
-
- final String subject = message.mSubject;
- final String snippet = message.mSnippet;
-
- if (TextUtils.isEmpty(subject)) {
- // If the subject is empty, just use the snippet.
- return snippet;
- }
- else if (TextUtils.isEmpty(snippet)) {
- // If the snippet is empty, just use the subject.
- final SpannableString spannableString = new SpannableString(subject);
- spannableString.setSpan(notificationSubjectSpan, 0, subject.length(), 0);
-
- return spannableString;
- } else {
- final String notificationBigTextFormat = context.getResources().getString(
- R.string.single_new_message_notification_big_text);
-
- // Localizers may change the order of the parameters, look at how the format
- // string is structured.
- final boolean isSubjectFirst = notificationBigTextFormat.indexOf("%2$s") >
- notificationBigTextFormat.indexOf("%1$s");
- final String bigText = String.format(notificationBigTextFormat, subject, snippet);
- final SpannableString spannableString = new SpannableString(bigText);
-
- final int subjectOffset =
- (isSubjectFirst ? bigText.indexOf(subject) : bigText.lastIndexOf(subject));
- spannableString.setSpan(notificationSubjectSpan,
- subjectOffset, subjectOffset + subject.length(), 0);
-
- return spannableString;
- }
- }
-
- /**
- * Creates a notification title for a new message. If there is only a single message,
- * show the sender name. Otherwise, show "X new messages".
- */
- @VisibleForTesting
- SpannableString getNewMessageTitle(String sender, int unseenCount) {
- String title;
- if (unseenCount > 1) {
- title = String.format(
- mContext.getString(R.string.notification_multiple_new_messages_fmt),
- unseenCount);
- } else {
- title = sender;
- }
- return new SpannableString(title);
- }
-
- /** Returns the system's current ringer mode */
- @VisibleForTesting
- int getRingerMode() {
- return mAudioManager.getRingerMode();
- }
-
/** Sets up the notification's sound and vibration based upon account details. */
- @VisibleForTesting
- void setupSoundAndVibration(Notification.Builder builder, Account account) {
- final int flags = account.mFlags;
- final String ringtoneUri = account.mRingtoneUri;
- final boolean vibrate = (flags & Account.FLAGS_VIBRATE) != 0;
+ private void setupSoundAndVibration(
+ NotificationCompat.Builder builder, Account account) {
+ String ringtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI.toString();
+ boolean vibrate = false;
+
+ // Use the Inbox notification preferences
+ final Cursor accountCursor = mContext.getContentResolver().query(EmailProvider.uiUri(
+ "uiaccount", account.mId), UIProvider.ACCOUNTS_PROJECTION, null, null, null);
+
+ com.android.mail.providers.Account uiAccount = null;
+ try {
+ if (accountCursor.moveToFirst()) {
+ uiAccount = new com.android.mail.providers.Account(accountCursor);
+ }
+ } finally {
+ accountCursor.close();
+ }
+
+ if (uiAccount != null) {
+ final Cursor folderCursor =
+ mContext.getContentResolver().query(uiAccount.settings.defaultInbox,
+ UIProvider.FOLDERS_PROJECTION, null, null, null);
+
+ if (folderCursor == null) {
+ LogUtils.e(LOG_TAG, "Null folder cursor for mailbox %s",
+ uiAccount.settings.defaultInbox);
+ }
+
+ Folder folder = null;
+ try {
+ if (folderCursor.moveToFirst()) {
+ folder = new Folder(folderCursor);
+ }
+ } finally {
+ folderCursor.close();
+ }
+
+ if (folder != null) {
+ final FolderPreferences folderPreferences =
+ new FolderPreferences(mContext, uiAccount.name, folder, true /* inbox */);
+
+ ringtoneUri = folderPreferences.getNotificationRingtoneUri();
+ vibrate = folderPreferences.isNotificationVibrateEnabled();
+ } else {
+ LogUtils.e(LOG_TAG, "Null folder for mailbox %s", uiAccount.settings.defaultInbox);
+ }
+ } else {
+ LogUtils.e(LOG_TAG, "Null uiAccount for account id %d", account.mId);
+ }
int defaults = Notification.DEFAULT_LIGHTS;
if (vibrate) {
@@ -790,7 +369,7 @@ public class NotificationController {
/**
* Returns a notification ID for login failed notifications for the given account account.
*/
- private int getLoginFailedNotificationId(long accountId) {
+ private static int getLoginFailedNotificationId(long accountId) {
return NOTIFICATION_ID_BASE_LOGIN_WARNING + (int)accountId;
}
@@ -953,109 +532,100 @@ public class NotificationController {
private final long mAccountId;
public MessageContentObserver(
- Handler handler, Context context, long accountId) {
+ final Handler handler, final Context context, final long accountId) {
super(handler);
mContext = context;
mAccountId = accountId;
}
@Override
- public void onChange(boolean selfChange) {
- ContentObserver observer = sInstance.mNotificationMap.get(mAccountId);
- Account account = Account.restoreAccountWithId(mContext, mAccountId);
- if (observer == null || account == null) {
- Log.w(Logging.LOG_TAG, "Couldn't find account for changed message notification");
+ public void onChange(final boolean selfChange) {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+
+ final Cursor accountCursor = contentResolver.query(
+ EmailProvider.uiUri("uiaccount", mAccountId), UIProvider.ACCOUNTS_PROJECTION,
+ null, null, null);
+
+ if (accountCursor == null) {
+ LogUtils.e(LOG_TAG, "Null account cursor for mAccountId %d", mAccountId);
return;
}
- ContentResolver resolver = mContext.getContentResolver();
- Cursor c = resolver.query(ContentUris.withAppendedId(
- EmailContent.MAILBOX_NOTIFICATION_URI, mAccountId),
- EmailContent.NOTIFICATION_PROJECTION, null, null, null);
+ com.android.mail.providers.Account account = null;
try {
- while (c.moveToNext()) {
- long mailboxId = c.getLong(EmailContent.NOTIFICATION_MAILBOX_ID_COLUMN);
- if (mailboxId == 0) continue;
- int messageCount =
- c.getInt(EmailContent.NOTIFICATION_MAILBOX_MESSAGE_COUNT_COLUMN);
- int unreadCount =
- c.getInt(EmailContent.NOTIFICATION_MAILBOX_UNREAD_COUNT_COLUMN);
-
- Mailbox m = Mailbox.restoreMailboxWithId(mContext, mailboxId);
- long newMessageId = Utility.getFirstRowLong(mContext,
- ContentUris.withAppendedId(
- EmailContent.MAILBOX_MOST_RECENT_MESSAGE_URI, mailboxId),
- Message.ID_COLUMN_PROJECTION, null, null, null,
- Message.ID_MAILBOX_COLUMN_ID, -1L);
- Log.d(Logging.LOG_TAG, "Changes to " + account.mDisplayName + "/" +
- m.mDisplayName + ", count: " + messageCount + ", lastNotified: " +
- m.mLastNotifiedMessageKey + ", mostRecent: " + newMessageId);
- // Broadcast intent here
- Intent i = new Intent(EmailBroadcastProcessorService.ACTION_NOTIFY_NEW_MAIL);
- // Required by UIProvider
- i.setType(EmailProvider.EMAIL_APP_MIME_TYPE);
- i.putExtra(UIProvider.UpdateNotificationExtras.EXTRA_FOLDER,
- Uri.parse(EmailProvider.uiUriString("uifolder", mailboxId)));
- i.putExtra(UIProvider.UpdateNotificationExtras.EXTRA_ACCOUNT,
- Uri.parse(EmailProvider.uiUriString("uiaccount", m.mAccountKey)));
- i.putExtra(UIProvider.UpdateNotificationExtras.EXTRA_UPDATED_UNREAD_COUNT,
- unreadCount);
- // Required by our notification controller
- i.putExtra(NEW_MAIL_MAILBOX_ID, mailboxId);
- i.putExtra(NEW_MAIL_MESSAGE_ID, newMessageId);
- i.putExtra(NEW_MAIL_MESSAGE_COUNT, messageCount);
- i.putExtra(NEW_MAIL_UNREAD_COUNT, unreadCount);
- mContext.sendOrderedBroadcast(i, null);
+ if (accountCursor.moveToFirst()) {
+ account = new com.android.mail.providers.Account(accountCursor);
}
} finally {
- c.close();
+ accountCursor.close();
+ }
+
+ final Cursor mailboxCursor = contentResolver.query(
+ ContentUris.withAppendedId(EmailContent.MAILBOX_NOTIFICATION_URI, mAccountId),
+ null, null, null, null);
+ try {
+ while (mailboxCursor.moveToNext()) {
+ final long mailboxId =
+ mailboxCursor.getLong(EmailContent.NOTIFICATION_MAILBOX_ID_COLUMN);
+ if (mailboxId == 0) continue;
+
+ final int unreadCount = mailboxCursor.getInt(
+ EmailContent.NOTIFICATION_MAILBOX_UNREAD_COUNT_COLUMN);
+ final int unseenCount = mailboxCursor.getInt(
+ EmailContent.NOTIFICATION_MAILBOX_UNSEEN_COUNT_COLUMN);
+
+ final Cursor folderCursor = contentResolver.query(
+ EmailProvider.uiUri("uifolder", mailboxId),
+ UIProvider.FOLDERS_PROJECTION, null, null, null);
+
+ if (folderCursor == null) {
+ LogUtils.e(LOG_TAG, "Null folder cursor for mMailboxId %d", mailboxId);
+ return;
+ }
+
+ Folder folder = null;
+ try {
+ if (folderCursor.moveToFirst()) {
+ folder = new Folder(folderCursor);
+ }
+ } finally {
+ folderCursor.close();
+ }
+
+ LogUtils.d(LOG_TAG, "Changes to account " + account.name + ", folder: "
+ + folder.name + ", unreadCount: " + unreadCount + ", unseenCount: "
+ + unseenCount);
+
+ NotificationUtils.setNewEmailIndicator(mContext, unreadCount, unseenCount,
+ account, folder, true);
+ }
+ } finally {
+ mailboxCursor.close();
}
}
}
- public static void notifyNewMail(Context context, Intent i) {
- Log.d(Logging.LOG_TAG, "Sending notification to system...");
- NotificationController nc = NotificationController.getInstance(context);
- ContentResolver resolver = context.getContentResolver();
- long mailboxId = i.getLongExtra(NEW_MAIL_MAILBOX_ID, -1);
- long newMessageId = i.getLongExtra(NEW_MAIL_MESSAGE_ID, -1);
- int messageCount = i.getIntExtra(NEW_MAIL_MESSAGE_COUNT, 0);
- int unreadCount = i.getIntExtra(NEW_MAIL_UNREAD_COUNT, 0);
- Notification n = nc.createNewMessageNotification(mailboxId, newMessageId,
- messageCount, unreadCount);
- if (n != null) {
- // Make the notification visible
- nc.mNotificationManager.notify(nc.getNewMessageNotificationId(mailboxId), n);
- }
- // Save away the new values
- ContentValues cv = new ContentValues();
- cv.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, newMessageId);
- cv.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT, messageCount);
- resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId), cv,
- null, null);
- }
-
/**
* Observer invoked whenever an account is modified. This could mean the user changed the
* notification settings.
*/
private static class AccountContentObserver extends ContentObserver {
private final Context mContext;
- public AccountContentObserver(Handler handler, Context context) {
+ public AccountContentObserver(final Handler handler, final Context context) {
super(handler);
mContext = context;
}
@Override
- public void onChange(boolean selfChange) {
+ public void onChange(final boolean selfChange) {
final ContentResolver resolver = mContext.getContentResolver();
final Cursor c = resolver.query(Account.CONTENT_URI, EmailContent.ID_PROJECTION,
- NOTIFIED_ACCOUNT_SELECTION, null, null);
- final HashSet newAccountList = new HashSet();
- final HashSet removedAccountList = new HashSet();
+ null, null, null);
+ final Set newAccountList = new HashSet();
+ final Set removedAccountList = new HashSet();
if (c == null) {
// Suspender time ... theoretically, this will never happen
- Log.wtf(Logging.LOG_TAG, "#onChange(); NULL response for account id query");
+ LogUtils.wtf(LOG_TAG, "#onChange(); NULL response for account id query");
return;
}
try {
@@ -1071,22 +641,22 @@ public class NotificationController {
// NOTE: Looping over three lists is not necessarily the most efficient. However, the
// account lists are going to be very small, so, this will not be necessarily bad.
// Cycle through existing notification list and adjust as necessary
- for (long accountId : sInstance.mNotificationMap.keySet()) {
+ for (final long accountId : sInstance.mNotificationMap.keySet()) {
if (!newAccountList.remove(accountId)) {
// account id not in the current set of notifiable accounts
removedAccountList.add(accountId);
}
}
// A new account was added to the notification list
- for (long accountId : newAccountList) {
+ for (final long accountId : newAccountList) {
sInstance.registerMessageNotification(accountId);
}
// An account was removed from the notification list
- for (long accountId : removedAccountList) {
+ for (final long accountId : removedAccountList) {
sInstance.unregisterMessageNotification(accountId);
- int notificationId = sInstance.getNewMessageNotificationId(accountId);
- sInstance.mNotificationManager.cancel(notificationId);
}
+
+ NotificationUtils.resendNotifications(mContext, false);
}
}
@@ -1099,7 +669,7 @@ public class NotificationController {
/** The {@link Looper} that handles messages for this thread */
private Looper mLooper;
- NotificationThread() {
+ public NotificationThread() {
new Thread(null, this, "EmailNotification").start();
synchronized (mLock) {
while (mLooper == null) {
@@ -1121,10 +691,8 @@ public class NotificationController {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Looper.loop();
}
- void quit() {
- mLooper.quit();
- }
- Looper getLooper() {
+
+ public Looper getLooper() {
return mLooper;
}
}
diff --git a/src/com/android/email/Preferences.java b/src/com/android/email/Preferences.java
index 079d5b200..4d1042c0f 100644
--- a/src/com/android/email/Preferences.java
+++ b/src/com/android/email/Preferences.java
@@ -75,13 +75,6 @@ public class Preferences {
// "normal" will be the default
public static final int TEXT_ZOOM_DEFAULT = TEXT_ZOOM_NORMAL;
- // Starting something new here:
- // REPLY_ALL is saved by the framework (CheckBoxPreference's parent, Preference).
- // i.e. android:persistent=true in general_preferences.xml
- public static final String REPLY_ALL = "reply_all";
- // Reply All Default - when changing this, be sure to update general_preferences.xml
- public static final boolean REPLY_ALL_DEFAULT = false;
-
private static Preferences sPreferences;
private final SharedPreferences mSharedPreferences;
diff --git a/src/com/android/email/activity/setup/AccountSettingsFragment.java b/src/com/android/email/activity/setup/AccountSettingsFragment.java
index bad4c15e1..8a8c60c81 100644
--- a/src/com/android/email/activity/setup/AccountSettingsFragment.java
+++ b/src/com/android/email/activity/setup/AccountSettingsFragment.java
@@ -21,8 +21,10 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.res.Resources;
+import android.database.Cursor;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -32,14 +34,16 @@ import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
-import android.preference.RingtonePreference;
+import android.preference.Preference.OnPreferenceClickListener;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.android.email.R;
import com.android.email.SecurityPolicy;
+import com.android.email.provider.EmailProvider;
import com.android.email.provider.FolderPickerActivity;
import com.android.email.service.EmailServiceUtils;
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
@@ -51,6 +55,11 @@ import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.provider.Policy;
import com.android.emailcommon.utility.Utility;
+import com.android.mail.preferences.AccountPreferences;
+import com.android.mail.preferences.FolderPreferences;
+import com.android.mail.providers.Folder;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.NotificationUtils;
import java.util.ArrayList;
@@ -79,10 +88,6 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
private static final String PREFERENCE_DEFAULT = "account_default";
private static final String PREFERENCE_CATEGORY_DATA_USAGE = "data_usage";
private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "account_notifications";
- private static final String PREFERENCE_NOTIFY = "account_notify";
- private static final String PREFERENCE_VIBRATE = "account_settings_vibrate";
- private static final String PREFERENCE_VIBRATE_OLD = "account_settings_vibrate_when";
- private static final String PREFERENCE_RINGTONE = "account_ringtone";
private static final String PREFERENCE_CATEGORY_SERVER = "account_servers";
private static final String PREFERENCE_CATEGORY_POLICIES = "account_policies";
private static final String PREFERENCE_POLICIES_ENFORCED = "policies_enforced";
@@ -98,6 +103,9 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
private static final String PREFERENCE_SYSTEM_FOLDERS_TRASH = "system_folders_trash";
private static final String PREFERENCE_SYSTEM_FOLDERS_SENT = "system_folders_sent";
+ // Request code to start different activities.
+ private static final int RINGTONE_REQUEST_CODE = 0;
+
private EditTextPreference mAccountDescription;
private EditTextPreference mAccountName;
private EditTextPreference mAccountSignature;
@@ -105,9 +113,9 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
private ListPreference mSyncWindow;
private CheckBoxPreference mAccountBackgroundAttachments;
private CheckBoxPreference mAccountDefault;
- private CheckBoxPreference mAccountNotify;
- private CheckBoxPreference mAccountVibrate;
- private RingtonePreference mAccountRingtone;
+ private CheckBoxPreference mInboxNotify;
+ private CheckBoxPreference mInboxVibrate;
+ private Preference mInboxRingtone;
private CheckBoxPreference mSyncContacts;
private CheckBoxPreference mSyncCalendar;
private CheckBoxPreference mSyncEmail;
@@ -121,6 +129,11 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
private boolean mLoaded;
private boolean mSaveOnExit;
+ private Ringtone mRingtone;
+
+ private AccountPreferences mAccountPreferences;
+ private FolderPreferences mInboxFolderPreferences;
+
/** The e-mail of the account being edited. */
private String mAccountEmail;
@@ -178,8 +191,6 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
}
super.onCreate(savedInstanceState);
- upgradeVibrateSetting();
-
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.account_settings_preferences);
@@ -197,20 +208,6 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
mAccountDirty = false;
}
- /**
- * Upgrades the old tri-state vibrate setting to the new boolean value.
- */
- private void upgradeVibrateSetting() {
- final SharedPreferences sharedPreferences = getPreferenceManager().getSharedPreferences();
-
- if (!sharedPreferences.contains(PREFERENCE_VIBRATE)) {
- // Try to migrate the old one
- final boolean vibrate =
- "always".equals(sharedPreferences.getString(PREFERENCE_VIBRATE_OLD, ""));
- sharedPreferences.edit().putBoolean(PREFERENCE_VIBRATE, vibrate);
- }
- }
-
@Override
public void onActivityCreated(Bundle savedInstanceState) {
if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
@@ -293,6 +290,41 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
mStarted = false;
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RINGTONE_REQUEST_CODE:
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+ setRingtone(uri);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Sets the current ringtone.
+ */
+ private void setRingtone(Uri ringtone) {
+ if (ringtone != null) {
+ mInboxFolderPreferences.setNotificationRingtoneUri(ringtone.toString());
+ mRingtone = RingtoneManager.getRingtone(getActivity(), ringtone);
+ } else {
+ // Null means silent was selected.
+ mInboxFolderPreferences.setNotificationRingtoneUri("");
+ mRingtone = null;
+ }
+
+ setRingtoneSummary();
+ }
+
+ private void setRingtoneSummary() {
+ final String summary = mRingtone != null ? mRingtone.getTitle(mContext)
+ : mContext.getString(R.string.silent_ringtone);
+
+ mInboxRingtone.setSummary(summary);
+ }
+
/**
* Listen to all preference changes in this class.
* @param preference
@@ -338,11 +370,16 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
preferenceChanged(PREFERENCE_NAME, summary);
}
return false;
- } else if (key.equals(PREFERENCE_VIBRATE)) {
+ } else if (FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE.equals(key)) {
final boolean vibrateSetting = (Boolean) newValue;
- mAccountVibrate.setChecked(vibrateSetting);
- preferenceChanged(PREFERENCE_VIBRATE, newValue);
- return false;
+ mInboxVibrate.setChecked(vibrateSetting);
+ mInboxFolderPreferences.setNotificationVibrateEnabled(vibrateSetting);
+ preferenceChanged(FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE, newValue);
+ return true;
+ } else if (FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED.equals(key)) {
+ mInboxFolderPreferences.setNotificationsEnabled((Boolean) newValue);
+ preferenceChanged(FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED, newValue);
+ return true;
} else {
// Default behavior, just indicate that the preferences were written
preferenceChanged(key, newValue);
@@ -474,7 +511,72 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
}
/**
- * Load account data into preference UI
+ * Loads settings that are dependent on a {@link com.android.mail.providers.Account}, which
+ * must be obtained off the main thread. This will also call {@link #loadMainThreadSettings()}.
+ */
+ private void loadSettingsOffMainThread() {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ final Cursor accountCursor = mContext.getContentResolver().query(EmailProvider
+ .uiUri("uiaccount", mAccount.mId), UIProvider.ACCOUNTS_PROJECTION, null,
+ null, null);
+
+ com.android.mail.providers.Account account = null;
+ try {
+ if (accountCursor.moveToFirst()) {
+ account = new com.android.mail.providers.Account(accountCursor);
+ }
+ } finally {
+ accountCursor.close();
+ }
+
+ final Cursor folderCursor = mContext.getContentResolver().query(
+ account.settings.defaultInbox, UIProvider.FOLDERS_PROJECTION, null, null,
+ null);
+
+ Folder folder = null;
+ try {
+ if (folderCursor.moveToFirst()) {
+ folder = new Folder(folderCursor);
+ }
+ } finally {
+ folderCursor.close();
+ }
+
+ mAccountPreferences = new AccountPreferences(mContext, account.name);
+ mInboxFolderPreferences =
+ new FolderPreferences(mContext, account.name, folder, true);
+
+ NotificationUtils.moveNotificationSetting(
+ mAccountPreferences, mInboxFolderPreferences);
+
+ final String ringtoneUri = mInboxFolderPreferences.getNotificationRingtoneUri();
+ if (!TextUtils.isEmpty(ringtoneUri)) {
+ mRingtone = RingtoneManager.getRingtone(getActivity(), Uri.parse(ringtoneUri));
+ }
+
+ final Activity activity = getActivity();
+ if (activity != null) {
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mInboxNotify.setChecked(
+ mInboxFolderPreferences.areNotificationsEnabled());
+
+ mInboxVibrate.setChecked(
+ mInboxFolderPreferences.isNotificationVibrateEnabled());
+
+ setRingtoneSummary();
+ }
+ });
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * Load account data into preference UI. This must be called on the main thread.
*/
private void loadSettings() {
// We can only do this once, so prevent repeat
@@ -482,6 +584,8 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
// Once loaded the data is ready to be saved, as well
mSaveOnExit = false;
+ loadSettingsOffMainThread();
+
mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION);
mAccountDescription.setSummary(mAccount.getDisplayName());
mAccountDescription.setText(mAccount.getDisplayName());
@@ -549,8 +653,7 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
PreferenceCategory folderPrefs =
(PreferenceCategory) findPreference(PREFERENCE_SYSTEM_FOLDERS);
if (info.requiresSetup) {
- Preference trashPreference =
- (Preference) findPreference(PREFERENCE_SYSTEM_FOLDERS_TRASH);
+ Preference trashPreference = findPreference(PREFERENCE_SYSTEM_FOLDERS_TRASH);
Intent i = new Intent(mContext, FolderPickerActivity.class);
Uri uri = EmailContent.CONTENT_URI.buildUpon().appendQueryParameter(
"account", Long.toString(mAccount.mId)).build();
@@ -558,8 +661,7 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_TRASH);
trashPreference.setIntent(i);
- Preference sentPreference =
- (Preference) findPreference(PREFERENCE_SYSTEM_FOLDERS_SENT);
+ Preference sentPreference = findPreference(PREFERENCE_SYSTEM_FOLDERS_SENT);
i = new Intent(mContext, FolderPickerActivity.class);
i.setData(uri);
i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_SENT);
@@ -582,33 +684,35 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
mAccountDefault.setChecked(mAccount.mId == mDefaultAccountId);
mAccountDefault.setOnPreferenceChangeListener(this);
- mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY);
- mAccountNotify.setChecked(0 != (mAccount.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL));
- mAccountNotify.setOnPreferenceChangeListener(this);
+ mInboxNotify = (CheckBoxPreference) findPreference(
+ FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED);
+ mInboxNotify.setOnPreferenceChangeListener(this);
- mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE);
- mAccountRingtone.setOnPreferenceChangeListener(this);
+ mInboxRingtone = findPreference(FolderPreferences.PreferenceKeys.NOTIFICATION_RINGTONE);
+ mInboxRingtone.setOnPreferenceChangeListener(this);
+ mInboxRingtone.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(final Preference preference) {
+ showRingtonePicker();
- // The following two lines act as a workaround for the RingtonePreference
- // which does not let us set/get the value programmatically
- SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
- prefs.edit().putString(PREFERENCE_RINGTONE, mAccount.getRingtone()).apply();
+ return true;
+ }
+ });
// Set the vibrator value, or hide it on devices w/o a vibrator
- mAccountVibrate = (CheckBoxPreference) findPreference(PREFERENCE_VIBRATE);
+ mInboxVibrate = (CheckBoxPreference) findPreference(
+ FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE);
Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator.hasVibrator()) {
- // Calculate the value to set based on the choices, and set the value.
- final boolean vibrateSetting = 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE);
- mAccountVibrate.setChecked(vibrateSetting);
+ // Checked state will be set when we obtain it in #loadSettingsOffMainThread()
// When the value is changed, update the setting.
- mAccountVibrate.setOnPreferenceChangeListener(this);
+ mInboxVibrate.setOnPreferenceChangeListener(this);
} else {
// No vibrator present. Remove the preference altogether.
PreferenceCategory notificationsCategory = (PreferenceCategory)
findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS);
- notificationsCategory.removePreference(mAccountVibrate);
+ notificationsCategory.removePreference(mInboxVibrate);
}
final Preference retryAccount = findPreference(PREFERENCE_POLICIES_RETRY_ACCOUNT);
@@ -725,10 +829,7 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
*/
private void saveSettings() {
// Turn off all controlled flags - will turn them back on while checking UI elements
- int newFlags = mAccount.getFlags() &
- ~(Account.FLAGS_NOTIFY_NEW_MAIL |
- Account.FLAGS_VIBRATE |
- Account.FLAGS_BACKGROUND_ATTACHMENTS);
+ int newFlags = mAccount.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS);
newFlags |= mAccountBackgroundAttachments.isChecked() ?
Account.FLAGS_BACKGROUND_ATTACHMENTS : 0;
@@ -738,16 +839,10 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
// The sender name must never be empty (this is enforced by the preference editor)
mAccount.setSenderName(mAccountName.getText().trim());
mAccount.setSignature(mAccountSignature.getText());
- newFlags |= mAccountNotify.isChecked() ? Account.FLAGS_NOTIFY_NEW_MAIL : 0;
mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue()));
if (mSyncWindow != null) {
mAccount.setSyncLookback(Integer.parseInt(mSyncWindow.getValue()));
}
- if (mAccountVibrate.isChecked()) {
- newFlags |= Account.FLAGS_VIBRATE;
- }
- SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
- mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null));
mAccount.setFlags(newFlags);
EmailServiceInfo info =
@@ -779,4 +874,21 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
// Get the e-mail address of the account being editted, if this is for an existing account.
return mAccountEmail;
}
+
+ /**
+ * Shows the system ringtone picker.
+ */
+ private void showRingtonePicker() {
+ Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ final String ringtoneUri = mInboxFolderPreferences.getNotificationRingtoneUri();
+ if (!TextUtils.isEmpty(ringtoneUri)) {
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(ringtoneUri));
+ }
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
+ Settings.System.DEFAULT_NOTIFICATION_URI);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
+ startActivityForResult(intent, RINGTONE_REQUEST_CODE);
+ }
}
diff --git a/src/com/android/email/activity/setup/AccountSettingsUtils.java b/src/com/android/email/activity/setup/AccountSettingsUtils.java
index 2613a53f6..d1644d974 100644
--- a/src/com/android/email/activity/setup/AccountSettingsUtils.java
+++ b/src/com/android/email/activity/setup/AccountSettingsUtils.java
@@ -92,7 +92,6 @@ public class AccountSettingsUtils {
cv.put(AccountColumns.SENDER_NAME, account.getSenderName());
cv.put(AccountColumns.SIGNATURE, account.getSignature());
cv.put(AccountColumns.SYNC_INTERVAL, account.mSyncInterval);
- cv.put(AccountColumns.RINGTONE_URI, account.mRingtoneUri);
cv.put(AccountColumns.FLAGS, account.mFlags);
cv.put(AccountColumns.SYNC_LOOKBACK, account.mSyncLookback);
cv.put(AccountColumns.SECURITY_SYNC_KEY, account.mSecuritySyncKey);
diff --git a/src/com/android/email/activity/setup/AccountSetupOptions.java b/src/com/android/email/activity/setup/AccountSetupOptions.java
index 6a8ca3536..215d31f04 100644
--- a/src/com/android/email/activity/setup/AccountSetupOptions.java
+++ b/src/com/android/email/activity/setup/AccountSetupOptions.java
@@ -28,6 +28,7 @@ import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.database.Cursor;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
@@ -40,6 +41,7 @@ import android.widget.Spinner;
import com.android.email.R;
import com.android.email.activity.ActivityHelper;
import com.android.email.activity.UiUtilities;
+import com.android.email.provider.EmailProvider;
import com.android.email.service.EmailServiceUtils;
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
import com.android.email.service.MailService;
@@ -50,10 +52,17 @@ import com.android.emailcommon.provider.Policy;
import com.android.emailcommon.service.EmailServiceProxy;
import com.android.emailcommon.service.SyncWindow;
import com.android.emailcommon.utility.Utility;
+import com.android.mail.preferences.AccountPreferences;
+import com.android.mail.preferences.FolderPreferences;
+import com.android.mail.providers.Folder;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
import java.io.IOException;
public class AccountSetupOptions extends AccountSetupActivity implements OnClickListener {
+ private static final String LOG_TAG = LogTag.getLogTag();
private Spinner mCheckFrequencyView;
private Spinner mSyncWindowView;
@@ -126,8 +135,8 @@ public class AccountSetupOptions extends AccountSetupActivity implements OnClick
if (account.mIsDefault || SetupData.isDefault()) {
mDefaultView.setChecked(true);
}
- mNotifyView.setChecked(
- (account.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL) != 0);
+
+ mNotifyView.setChecked(true); // By default, we want notifications on
SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, account.getSyncInterval());
if (mServiceInfo.syncContacts) {
@@ -210,11 +219,7 @@ public class AccountSetupOptions extends AccountSetupActivity implements OnClick
return;
}
account.setDisplayName(account.getEmailAddress());
- int newFlags = account.getFlags() &
- ~(Account.FLAGS_NOTIFY_NEW_MAIL | Account.FLAGS_BACKGROUND_ATTACHMENTS);
- if (mNotifyView.isChecked()) {
- newFlags |= Account.FLAGS_NOTIFY_NEW_MAIL;
- }
+ int newFlags = account.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS);
if (mServiceInfo.offerAttachmentPreload && mBackgroundAttachmentsView.isChecked()) {
newFlags |= Account.FLAGS_BACKGROUND_ATTACHMENTS;
}
@@ -269,6 +274,12 @@ public class AccountSetupOptions extends AccountSetupActivity implements OnClick
AccountSettingsUtils.commitSettings(context, account);
MailService.setupAccountManagerAccount(context, account,
email2, calendar2, contacts2, mAccountManagerCallback);
+
+ // We can move the notification setting to the inbox FolderPreferences later, once
+ // we know what the inbox is
+ final AccountPreferences accountPreferences =
+ new AccountPreferences(context, account.mEmailAddress);
+ accountPreferences.setDefaultInboxNotificationsEnabled(mNotifyView.isChecked());
}
});
}
diff --git a/src/com/android/email/activity/setup/GeneralPreferences.java b/src/com/android/email/activity/setup/GeneralPreferences.java
index 12f9680c8..084353824 100644
--- a/src/com/android/email/activity/setup/GeneralPreferences.java
+++ b/src/com/android/email/activity/setup/GeneralPreferences.java
@@ -29,6 +29,7 @@ import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.provider.EmailProvider;
+import com.android.mail.preferences.MailPrefs;
import com.android.mail.utils.Utils;
public class GeneralPreferences extends EmailPreferenceFragment implements
@@ -41,8 +42,8 @@ public class GeneralPreferences extends EmailPreferenceFragment implements
private static final String PREFERENCE_KEY_SWIPE_DELETE = "swipe_delete";
private static final String PREFERENCE_KEY_SHOW_CHECKBOXES = "show_checkboxes";
private static final String PREFERENCE_KEY_CLEAR_TRUSTED_SENDERS = "clear_trusted_senders";
- private static final String PREFERNECE_REPLY_ALL = "reply_all";
+ private MailPrefs mMailPrefs;
private Preferences mPreferences;
private ListPreference mAutoAdvance;
/**
@@ -64,6 +65,7 @@ public class GeneralPreferences extends EmailPreferenceFragment implements
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mMailPrefs = MailPrefs.get(getActivity());
getPreferenceManager().setSharedPreferencesName(Preferences.PREFERENCES_FILE);
// Load the preferences from an XML resource
@@ -75,7 +77,7 @@ public class GeneralPreferences extends EmailPreferenceFragment implements
// Disabling reply-all on tablets, as this setting is just for phones
if (Utils.useTabletUI(getActivity().getResources())) {
- ps.removePreference(findPreference(PREFERNECE_REPLY_ALL));
+ ps.removePreference(findPreference(MailPrefs.PreferenceKeys.DEFAULT_REPLY_ALL));
}
}
@@ -108,6 +110,9 @@ public class GeneralPreferences extends EmailPreferenceFragment implements
mPreferences.setTextZoom(mTextZoom.findIndexOfValue((String) newValue));
reloadDynamicSummaries();
return true;
+ } else if (MailPrefs.PreferenceKeys.DEFAULT_REPLY_ALL.equals(key)) {
+ mMailPrefs.setDefaultReplyAll((Boolean) newValue);
+ return true;
}
return false;
}
@@ -158,6 +163,13 @@ public class GeneralPreferences extends EmailPreferenceFragment implements
mShowCheckboxes = (CheckBoxPreference) findPreference(PREFERENCE_KEY_SHOW_CHECKBOXES);
mSwipeDelete = (CheckBoxPreference) findPreference(PREFERENCE_KEY_SWIPE_DELETE);
+ final Preference replyAllPreference =
+ findPreference(MailPrefs.PreferenceKeys.DEFAULT_REPLY_ALL);
+ // This preference is removed on tablets
+ if (replyAllPreference != null) {
+ replyAllPreference.setOnPreferenceChangeListener(this);
+ }
+
reloadDynamicSummaries();
}
diff --git a/src/com/android/email/provider/DBHelper.java b/src/com/android/email/provider/DBHelper.java
index bfba3f6a2..bba9be001 100644
--- a/src/com/android/email/provider/DBHelper.java
+++ b/src/com/android/email/provider/DBHelper.java
@@ -131,8 +131,9 @@ public final class DBHelper {
// Version 102&103: Add hierarchicalName to Mailbox
// Version 104&105: add syncData to Message
// Version 106: Add certificate to HostAuth
+ // Version 107: Add a SEEN column to the message table
- public static final int DATABASE_VERSION = 106;
+ public static final int DATABASE_VERSION = 107;
// Any changes to the database format *must* include update-in-place code.
// Original version: 2
@@ -178,7 +179,8 @@ public final class DBHelper {
+ MessageColumns.SNIPPET + " text, "
+ MessageColumns.PROTOCOL_SEARCH_INFO + " text, "
+ MessageColumns.THREAD_TOPIC + " text, "
- + MessageColumns.SYNC_DATA + " text"
+ + MessageColumns.SYNC_DATA + " text, "
+ + MessageColumns.FLAG_SEEN + " integer"
+ ");";
// This String and the following String MUST have the same columns, except for the type
@@ -1012,6 +1014,20 @@ public final class DBHelper {
}
oldVersion = 106;
}
+ if (oldVersion == 106) {
+ try {
+ db.execSQL("alter table " + Message.TABLE_NAME
+ + " add " + MessageColumns.FLAG_SEEN + " integer");
+ db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
+ + " add " + MessageColumns.FLAG_SEEN + " integer");
+ db.execSQL("alter table " + Message.DELETED_TABLE_NAME
+ + " add " + MessageColumns.FLAG_SEEN + " integer");
+ } catch (SQLException e) {
+ // Shouldn't be needed unless we're debugging and interrupt the process
+ Log.w(TAG, "Exception upgrading EmailProvider.db from v106 to v107", e);
+ }
+ oldVersion = 107;
+ }
}
@Override
diff --git a/src/com/android/email/provider/EmailProvider.java b/src/com/android/email/provider/EmailProvider.java
index 3117f3d13..3c3476830 100644
--- a/src/com/android/email/provider/EmailProvider.java
+++ b/src/com/android/email/provider/EmailProvider.java
@@ -93,7 +93,6 @@ import com.android.mail.utils.MatrixCursorWithExtra;
import com.android.mail.utils.Utils;
import com.android.mail.widget.BaseWidgetProvider;
import com.android.mail.widget.WidgetProvider;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -119,33 +118,29 @@ public class EmailProvider extends ContentProvider {
public static String EMAIL_APP_MIME_TYPE;
- protected static final String DATABASE_NAME = "EmailProvider.db";
- protected static final String BODY_DATABASE_NAME = "EmailProviderBody.db";
- protected static final String BACKUP_DATABASE_NAME = "EmailProviderBackup.db";
-
- public static final String ACTION_ATTACHMENT_UPDATED = "com.android.email.ATTACHMENT_UPDATED";
- public static final String ATTACHMENT_UPDATED_EXTRA_FLAGS =
- "com.android.email.ATTACHMENT_UPDATED_FLAGS";
+ private static final String DATABASE_NAME = "EmailProvider.db";
+ private static final String BODY_DATABASE_NAME = "EmailProviderBody.db";
+ private static final String BACKUP_DATABASE_NAME = "EmailProviderBackup.db";
/**
* Notifies that changes happened. Certain UI components, e.g., widgets, can register for this
* {@link android.content.Intent} and update accordingly. However, this can be very broad and
* is NOT the preferred way of getting notification.
*/
- public static final String ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED =
+ private static final String ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED =
"com.android.email.MESSAGE_LIST_DATASET_CHANGED";
- public static final String EMAIL_MESSAGE_MIME_TYPE =
+ private static final String EMAIL_MESSAGE_MIME_TYPE =
"vnd.android.cursor.item/email-message";
- public static final String EMAIL_ATTACHMENT_MIME_TYPE =
+ private static final String EMAIL_ATTACHMENT_MIME_TYPE =
"vnd.android.cursor.item/email-attachment";
/** Appended to the notification URI for delete operations */
- public static final String NOTIFICATION_OP_DELETE = "delete";
+ private static final String NOTIFICATION_OP_DELETE = "delete";
/** Appended to the notification URI for insert operations */
- public static final String NOTIFICATION_OP_INSERT = "insert";
+ private static final String NOTIFICATION_OP_INSERT = "insert";
/** Appended to the notification URI for update operations */
- public static final String NOTIFICATION_OP_UPDATE = "update";
+ private static final String NOTIFICATION_OP_UPDATE = "update";
// Definitions for our queries looking for orphaned messages
private static final String[] ORPHANS_PROJECTION
@@ -339,7 +334,7 @@ public class EmailProvider extends ContentProvider {
private static ContentValues CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT;
private static final ContentValues EMPTY_CONTENT_VALUES = new ContentValues();
- public static final String MESSAGE_URI_PARAMETER_MAILBOX_ID = "mailboxId";
+ private static final String MESSAGE_URI_PARAMETER_MAILBOX_ID = "mailboxId";
// For undo handling
private int mLastSequence = -1;
@@ -368,10 +363,9 @@ public class EmailProvider extends ContentProvider {
return match;
}
- public static Uri INTEGRITY_CHECK_URI;
+ private static Uri INTEGRITY_CHECK_URI;
public static Uri ACCOUNT_BACKUP_URI;
- public static Uri FOLDER_STATUS_URI;
- public static Uri FOLDER_REFRESH_URI;
+ private static Uri FOLDER_STATUS_URI;
private SQLiteDatabase mDatabase;
private SQLiteDatabase mBodyDatabase;
@@ -399,9 +393,8 @@ public class EmailProvider extends ContentProvider {
* @param foreignColumn the column in the foreign table whose absence will trigger the deletion
* @param foreignTable the foreign table
*/
- @VisibleForTesting
- void deleteUnlinked(SQLiteDatabase db, String table, String column, String foreignColumn,
- String foreignTable) {
+ private static void deleteUnlinked(SQLiteDatabase db, String table, String column,
+ String foreignColumn, String foreignTable) {
int count = db.delete(table, column + " not in (select " + foreignColumn + " from " +
foreignTable + ")", null);
if (count > 0) {
@@ -409,8 +402,7 @@ public class EmailProvider extends ContentProvider {
}
}
- @VisibleForTesting
- synchronized SQLiteDatabase getDatabase(Context context) {
+ private synchronized SQLiteDatabase getDatabase(Context context) {
// Always return the cached database, if we've got one
if (mDatabase != null) {
return mDatabase;
@@ -542,15 +534,10 @@ public class EmailProvider extends ContentProvider {
}
}
- /*package*/ static SQLiteDatabase getReadableDatabase(Context context) {
- DBHelper.DatabaseHelper helper = new DBHelper.DatabaseHelper(context, DATABASE_NAME);
- return helper.getReadableDatabase();
- }
-
/**
* Restore user Account and HostAuth data from our backup database
*/
- public static void restoreIfNeeded(Context context, SQLiteDatabase mainDatabase) {
+ private static void restoreIfNeeded(Context context, SQLiteDatabase mainDatabase) {
if (MailActivityEmail.DEBUG) {
Log.w(TAG, "restoreIfNeeded...");
}
@@ -596,7 +583,7 @@ public class EmailProvider extends ContentProvider {
}
}
- /*package*/ static void deleteMessageOrphans(SQLiteDatabase database, String tableName) {
+ private static void deleteMessageOrphans(SQLiteDatabase database, String tableName) {
if (database != null) {
// We'll look at all of the items in the table; there won't be many typically
Cursor c = database.query(tableName, ORPHANS_PROJECTION, null, null, null, null, null);
@@ -1061,8 +1048,6 @@ public class EmailProvider extends ContentProvider {
Uri.parse("content://" + EmailContent.AUTHORITY + "/accountBackup");
FOLDER_STATUS_URI =
Uri.parse("content://" + EmailContent.AUTHORITY + "/status");
- FOLDER_REFRESH_URI =
- Uri.parse("content://" + EmailContent.AUTHORITY + "/refresh");
EMAIL_APP_MIME_TYPE = context.getString(R.string.application_mime_type);
}
checkDatabases();
@@ -1303,7 +1288,16 @@ public class EmailProvider extends ContentProvider {
if (selection != null || selectionArgs != null) {
throw new IllegalArgumentException("UI queries can't have selection/args");
}
- c = uiQuery(match, uri, projection);
+
+ final String seenParam = uri.getQueryParameter(UIProvider.SEEN_QUERY_PARAMETER);
+ final boolean unseenOnly;
+ if (seenParam != null && Boolean.FALSE.toString().equals(seenParam)) {
+ unseenOnly = true;
+ } else {
+ unseenOnly = false;
+ }
+
+ c = uiQuery(match, uri, projection, unseenOnly);
return c;
case UI_FOLDERS:
c = uiFolders(uri, projection);
@@ -1451,7 +1445,7 @@ public class EmailProvider extends ContentProvider {
return c;
}
- private String whereWithId(String id, String selection) {
+ private static String whereWithId(String id, String selection) {
StringBuilder sb = new StringBuilder(256);
sb.append("_id=");
sb.append(id);
@@ -1473,7 +1467,7 @@ public class EmailProvider extends ContentProvider {
* @param selection user-provided selection, may be null
* @return a single selection string
*/
- private String whereWith(String where, String selection) {
+ private static String whereWith(String where, String selection) {
if (selection == null) {
return where;
}
@@ -1689,7 +1683,7 @@ outer:
case ACCOUNT_PICK_SENT_FOLDER:
return pickSentFolder(uri);
case UI_FOLDER:
- return uiUpdateFolder(uri, values);
+ return uiUpdateFolder(context, uri, values);
case UI_RECENT_FOLDERS:
return uiUpdateRecentFolders(uri, values);
case UI_DEFAULT_RECENT_FOLDERS:
@@ -1933,7 +1927,7 @@ outer:
*
* @param match The type of content that was modified.
*/
- private Uri getBaseNotificationUri(int match) {
+ private static Uri getBaseNotificationUri(int match) {
Uri baseUri = null;
switch (match) {
case MESSAGE:
@@ -2013,22 +2007,6 @@ outer:
}
}
- /**
- * For testing purposes, check whether a given row is cached
- * @param baseUri the base uri of the EmailContent
- * @param id the row id of the EmailContent
- * @return whether or not the row is currently cached
- */
- @VisibleForTesting
- protected boolean isCached(Uri baseUri, long id) {
- int match = findMatch(baseUri, "isCached");
- int table = match >> BASE_SHIFT;
- ContentCache cache = mContentCaches[table];
- if (cache == null) return false;
- Cursor cc = cache.get(Long.toString(id));
- return (cc != null);
- }
-
public static interface AttachmentService {
/**
* Notify the service that an attachment has changed.
@@ -2045,44 +2023,31 @@ outer:
};
private AttachmentService mAttachmentService = DEFAULT_ATTACHMENT_SERVICE;
- /**
- * Injects a custom attachment service handler. If null is specified, will reset to the
- * default service.
- */
- public void injectAttachmentService(AttachmentService as) {
- mAttachmentService = (as == null) ? DEFAULT_ATTACHMENT_SERVICE : as;
+ private Cursor notificationQuery(final Uri uri) {
+ final SQLiteDatabase db = getDatabase(getContext());
+ final String accountId = uri.getLastPathSegment();
+
+ final StringBuilder sqlBuilder = new StringBuilder();
+ sqlBuilder.append("SELECT ");
+ sqlBuilder.append(MessageColumns.MAILBOX_KEY).append(", ");
+ sqlBuilder.append("SUM(CASE ")
+ .append(MessageColumns.FLAG_READ).append(" WHEN 0 THEN 1 ELSE 0 END), ");
+ sqlBuilder.append("SUM(CASE ")
+ .append(MessageColumns.FLAG_SEEN).append(" WHEN 0 THEN 1 ELSE 0 END)\n");
+ sqlBuilder.append("FROM ");
+ sqlBuilder.append(Message.TABLE_NAME).append('\n');
+ sqlBuilder.append("WHERE ");
+ sqlBuilder.append(MessageColumns.ACCOUNT_KEY).append(" = ?\n");
+ sqlBuilder.append("GROUP BY ");
+ sqlBuilder.append(MessageColumns.MAILBOX_KEY);
+
+ final String sql = sqlBuilder.toString();
+
+ final String[] selectionArgs = {accountId};
+
+ return db.rawQuery(sql, selectionArgs);
}
- // SELECT DISTINCT Boxes._id, Boxes.unreadCount count(Message._id) from Message,
- // (SELECT _id, unreadCount, messageCount, lastNotifiedMessageCount, lastNotifiedMessageKey
- // FROM Mailbox WHERE accountKey=6 AND ((type = 0) OR (syncInterval!=0 AND syncInterval!=-1)))
- // AS Boxes
- // WHERE Boxes.messageCount!=Boxes.lastNotifiedMessageCount
- // OR (Boxes._id=Message.mailboxKey AND Message._id>Boxes.lastNotifiedMessageKey)
- // TODO: This query can be simplified a bit
- private static final String NOTIFICATION_QUERY =
- "SELECT DISTINCT Boxes." + MailboxColumns.ID + ", Boxes." + MailboxColumns.UNREAD_COUNT +
- ", count(" + Message.TABLE_NAME + "." + MessageColumns.ID + ")" +
- " FROM " +
- Message.TABLE_NAME + "," +
- "(SELECT " + MailboxColumns.ID + "," + MailboxColumns.UNREAD_COUNT + "," +
- MailboxColumns.MESSAGE_COUNT + "," + MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT +
- "," + MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY + " FROM " + Mailbox.TABLE_NAME +
- " WHERE " + MailboxColumns.ACCOUNT_KEY + "=?" +
- " AND (" + MailboxColumns.TYPE + "=" + Mailbox.TYPE_INBOX + " OR ("
- + MailboxColumns.SYNC_INTERVAL + "!=0 AND " +
- MailboxColumns.SYNC_INTERVAL + "!=-1))) AS Boxes " +
- "WHERE Boxes." + MailboxColumns.ID + '=' + Message.TABLE_NAME + "." +
- MessageColumns.MAILBOX_KEY + " AND " + Message.TABLE_NAME + "." +
- MessageColumns.ID + ">Boxes." + MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY +
- " AND " + MessageColumns.FLAG_READ + "=0 AND " + MessageColumns.TIMESTAMP + "!=0";
-
- public Cursor notificationQuery(Uri uri) {
- SQLiteDatabase db = getDatabase(getContext());
- String accountId = uri.getLastPathSegment();
- return db.rawQuery(NOTIFICATION_QUERY, new String[] {accountId});
- }
-
public Cursor mostRecentMessageQuery(Uri uri) {
SQLiteDatabase db = getDatabase(getContext());
String mailboxId = uri.getLastPathSegment();
@@ -2145,7 +2110,7 @@ outer:
* Mapping of UIProvider columns to EmailProvider columns for the message list (called the
* conversation list in UnifiedEmail)
*/
- private ProjectionMap getMessageListMap() {
+ private static ProjectionMap getMessageListMap() {
if (sMessageListMap == null) {
sMessageListMap = ProjectionMap.builder()
.add(BaseColumns._ID, MessageColumns.ID)
@@ -2163,6 +2128,7 @@ outer:
.add(UIProvider.ConversationColumns.PRIORITY,
Integer.toString(ConversationPriority.LOW))
.add(UIProvider.ConversationColumns.READ, MessageColumns.FLAG_READ)
+ .add(UIProvider.ConversationColumns.SEEN, MessageColumns.FLAG_SEEN)
.add(UIProvider.ConversationColumns.STARRED, MessageColumns.FLAG_FAVORITE)
.add(UIProvider.ConversationColumns.FLAGS, CONVERSATION_FLAGS)
.add(UIProvider.ConversationColumns.ACCOUNT_URI,
@@ -2197,7 +2163,7 @@ outer:
* Mapping of UIProvider columns to EmailProvider columns for a detailed message view in
* UnifiedEmail
*/
- private ProjectionMap getMessageViewMap() {
+ private static ProjectionMap getMessageViewMap() {
if (sMessageViewMap == null) {
sMessageViewMap = ProjectionMap.builder()
.add(BaseColumns._ID, Message.TABLE_NAME + "." + EmailContent.MessageColumns.ID)
@@ -2233,6 +2199,7 @@ outer:
uriWithColumn("account", MessageColumns.ACCOUNT_KEY))
.add(UIProvider.MessageColumns.STARRED, EmailContent.MessageColumns.FLAG_FAVORITE)
.add(UIProvider.MessageColumns.READ, EmailContent.MessageColumns.FLAG_READ)
+ .add(UIProvider.MessageColumns.SEEN, EmailContent.MessageColumns.FLAG_SEEN)
.add(UIProvider.MessageColumns.SPAM_WARNING_STRING, null)
.add(UIProvider.MessageColumns.SPAM_WARNING_LEVEL,
Integer.toString(UIProvider.SpamWarningLevel.NO_WARNING))
@@ -2274,10 +2241,11 @@ outer:
+ " WHEN " + Mailbox.TYPE_STARRED + " THEN " + R.drawable.ic_menu_star_holo_light
+ " ELSE -1 END";
- private ProjectionMap getFolderListMap() {
+ private static ProjectionMap getFolderListMap() {
if (sFolderListMap == null) {
sFolderListMap = ProjectionMap.builder()
.add(BaseColumns._ID, MailboxColumns.ID)
+ .add(UIProvider.FolderColumns.PERSISTENT_ID, MailboxColumns.SERVER_ID)
.add(UIProvider.FolderColumns.URI, uriWithId("uifolder"))
.add(UIProvider.FolderColumns.NAME, "displayName")
.add(UIProvider.FolderColumns.HAS_CHILDREN,
@@ -2304,7 +2272,7 @@ outer:
* Constructs the map of default entries for accounts. These values can be overridden in
* {@link #genQueryAccount(String[], String)}.
*/
- private ProjectionMap getAccountListMap() {
+ private static ProjectionMap getAccountListMap() {
if (sAccountListMap == null) {
sAccountListMap = ProjectionMap.builder()
.add(BaseColumns._ID, AccountColumns.ID)
@@ -2355,7 +2323,7 @@ outer:
/**
* Mapping of UIProvider columns to EmailProvider columns for a message's attachments
*/
- private ProjectionMap getAttachmentMap() {
+ private static ProjectionMap getAttachmentMap() {
if (sAttachmentMap == null) {
sAttachmentMap = ProjectionMap.builder()
.add(UIProvider.AttachmentColumns.NAME, AttachmentColumns.FILENAME)
@@ -2377,14 +2345,14 @@ outer:
* Generate the SELECT clause using a specified mapping and the original UI projection
* @param map the ProjectionMap to use for this projection
* @param projection the projection as sent by UnifiedEmail
- * @param values ContentValues to be used if the ProjectionMap entry is null
* @return a StringBuilder containing the SELECT expression for a SQLite query
*/
- private StringBuilder genSelect(ProjectionMap map, String[] projection) {
+ private static StringBuilder genSelect(ProjectionMap map, String[] projection) {
return genSelect(map, projection, EMPTY_CONTENT_VALUES);
}
- private StringBuilder genSelect(ProjectionMap map, String[] projection, ContentValues values) {
+ private static StringBuilder genSelect(ProjectionMap map, String[] projection,
+ ContentValues values) {
StringBuilder sb = new StringBuilder("SELECT ");
boolean first = true;
for (String column: projection) {
@@ -2545,12 +2513,16 @@ outer:
* Generate the "message list" SQLite query, given a projection from UnifiedEmail
*
* @param uiProjection as passed from UnifiedEmail
+ * @param unseenOnly true
to only return unseen messages
* @return the SQLite query to be executed on the EmailProvider database
*/
- private String genQueryMailboxMessages(String[] uiProjection) {
+ private static String genQueryMailboxMessages(String[] uiProjection, final boolean unseenOnly) {
StringBuilder sb = genSelect(getMessageListMap(), uiProjection);
- sb.append(" FROM " + Message.TABLE_NAME + " WHERE " + Message.MAILBOX_KEY + "=? ORDER BY " +
- MessageColumns.TIMESTAMP + " DESC");
+ sb.append(" FROM " + Message.TABLE_NAME + " WHERE " + Message.MAILBOX_KEY + "=? ");
+ if (unseenOnly) {
+ sb.append("AND ").append(MessageColumns.FLAG_SEEN).append(" = 0 ");
+ }
+ sb.append("ORDER BY " + MessageColumns.TIMESTAMP + " DESC");
return sb.toString();
}
@@ -2558,11 +2530,12 @@ outer:
* Generate various virtual mailbox SQLite queries, given a projection from UnifiedEmail
*
* @param uiProjection as passed from UnifiedEmail
- * @param id the id of the virtual mailbox
+ * @param mailboxId the id of the virtual mailbox
+ * @param unseenOnly true
to only return unseen messages
* @return the SQLite query to be executed on the EmailProvider database
*/
- private Cursor getVirtualMailboxMessagesCursor(SQLiteDatabase db, String[] uiProjection,
- long mailboxId) {
+ private static Cursor getVirtualMailboxMessagesCursor(SQLiteDatabase db, String[] uiProjection,
+ long mailboxId, final boolean unseenOnly) {
ContentValues values = new ContentValues();
values.put(UIProvider.ConversationColumns.COLOR, CONVERSATION_COLOR);
StringBuilder sb = genSelect(getMessageListMap(), uiProjection, values);
@@ -2572,13 +2545,19 @@ outer:
sb.append(" FROM " + Message.TABLE_NAME + " WHERE " +
MessageColumns.MAILBOX_KEY + " IN (SELECT " + MailboxColumns.ID +
" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.TYPE +
- "=" + Mailbox.TYPE_INBOX + ") ORDER BY " + MessageColumns.TIMESTAMP +
- " DESC");
+ "=" + Mailbox.TYPE_INBOX + ") ");
+ if (unseenOnly) {
+ sb.append("AND ").append(MessageColumns.FLAG_SEEN).append(" = 0 ");
+ }
+ sb.append("ORDER BY " + MessageColumns.TIMESTAMP + " DESC");
break;
case Mailbox.TYPE_STARRED:
sb.append(" FROM " + Message.TABLE_NAME + " WHERE " +
- MessageColumns.FLAG_FAVORITE + "=1 ORDER BY " +
- MessageColumns.TIMESTAMP + " DESC");
+ MessageColumns.FLAG_FAVORITE + "=1 ");
+ if (unseenOnly) {
+ sb.append("AND ").append(MessageColumns.FLAG_SEEN).append(" = 0 ");
+ }
+ sb.append("ORDER BY " + MessageColumns.TIMESTAMP + " DESC");
break;
default:
throw new IllegalArgumentException("No virtual mailbox for: " + mailboxId);
@@ -2606,7 +2585,7 @@ outer:
* @param uiProjection as passed from UnifiedEmail
* @return the SQLite query to be executed on the EmailProvider database
*/
- private String genQueryConversation(String[] uiProjection) {
+ private static String genQueryConversation(String[] uiProjection) {
StringBuilder sb = genSelect(getMessageListMap(), uiProjection);
sb.append(" FROM " + Message.TABLE_NAME + " WHERE " + Message.RECORD_ID + "=?");
return sb.toString();
@@ -2618,7 +2597,7 @@ outer:
* @param uiProjection as passed from UnifiedEmail
* @return the SQLite query to be executed on the EmailProvider database
*/
- private String genQueryAccountMailboxes(String[] uiProjection) {
+ private static String genQueryAccountMailboxes(String[] uiProjection) {
StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
"=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
@@ -2634,7 +2613,7 @@ outer:
* @param uiProjection as passed from UnifiedEmail
* @return the SQLite query to be executed on the EmailProvider database
*/
- private String genQueryAccountAllMailboxes(String[] uiProjection) {
+ private static String genQueryAccountAllMailboxes(String[] uiProjection) {
StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
// Use a derived column to choose either hierarchicalName or displayName
sb.append(", case when " + MailboxColumns.HIERARCHICAL_NAME + " is null then " +
@@ -2653,7 +2632,7 @@ outer:
* @param uiProjection as passed from UnifiedEmail
* @return the SQLite query to be executed on the EmailProvider database
*/
- private String genQueryRecentMailboxes(String[] uiProjection) {
+ private static String genQueryRecentMailboxes(String[] uiProjection) {
StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
"=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
@@ -2663,7 +2642,8 @@ outer:
return sb.toString();
}
- private int getFolderCapabilities(EmailServiceInfo info, int flags, int type, long mailboxId) {
+ private static int getFolderCapabilities(EmailServiceInfo info, int flags, int type,
+ long mailboxId) {
// All folders support delete
int caps = UIProvider.FolderCapabilities.DELETE;
if (info != null && info.offerLookback) {
@@ -2739,7 +2719,7 @@ outer:
.appendQueryParameter("account", account).build().toString();
}
- private String getBits(int bitField) {
+ private static String getBits(int bitField) {
StringBuilder sb = new StringBuilder(" ");
for (int i = 0; i < 32; i++, bitField >>= 1) {
if ((bitField & 1) != 0) {
@@ -2833,7 +2813,7 @@ outer:
values.put(UIProvider.AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE,
textZoomToUiValue(textZoom));
}
- // Set default inbox, if we've got an inbox; otherwise, say initial sync needed
+ // Set default inbox, if we've got an inbox; otherwise, say initial sync needed
long mailboxId = Mailbox.findMailboxOfType(context, accountId, Mailbox.TYPE_INBOX);
if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX) &&
mailboxId != Mailbox.NO_MAILBOX) {
@@ -2878,7 +2858,7 @@ outer:
return sb.toString();
}
- private int autoAdvanceToUiValue(int autoAdvance) {
+ private static int autoAdvanceToUiValue(int autoAdvance) {
switch(autoAdvance) {
case Preferences.AUTO_ADVANCE_OLDER:
return UIProvider.AutoAdvance.OLDER;
@@ -2890,7 +2870,7 @@ outer:
}
}
- private int textZoomToUiValue(int textZoom) {
+ private static int textZoomToUiValue(int textZoom) {
switch(textZoom) {
case Preferences.TEXT_ZOOM_HUGE:
return UIProvider.MessageTextSize.HUGE;
@@ -3130,7 +3110,7 @@ outer:
* or null if there are no query parameters
* @return the SQLite query to be executed on the EmailProvider database
*/
- private String genQueryAttachments(String[] uiProjection,
+ private static String genQueryAttachments(String[] uiProjection,
List contentTypeQueryParameters) {
StringBuilder sb = genSelect(getAttachmentMap(), uiProjection);
sb.append(" FROM " + Attachment.TABLE_NAME + " WHERE " + AttachmentColumns.MESSAGE_KEY +
@@ -3180,7 +3160,7 @@ outer:
* @param uiProjection as passed from UnifiedEmail
* @return the SQLite query to be executed on the EmailProvider database
*/
- private String genQuerySubfolders(String[] uiProjection) {
+ private static String genQuerySubfolders(String[] uiProjection) {
StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.PARENT_KEY +
" =? ORDER BY ");
@@ -3324,12 +3304,21 @@ outer:
if (params.containsKey(
UIProvider.ConversationCursorCommand.COMMAND_KEY_ENTERED_FOLDER)) {
Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mMailboxId);
- if (mailbox != null && mailbox.mVisibleLimit > 0) {
- ContentValues values = new ContentValues();
- values.put(MailboxColumns.VISIBLE_LIMIT, 0);
- mContext.getContentResolver().update(
- ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailboxId),
- values, null, null);
+ if (mailbox != null) {
+ if (mailbox.mVisibleLimit > 0) {
+ ContentValues values = new ContentValues();
+ values.put(MailboxColumns.VISIBLE_LIMIT, 0);
+ mContext.getContentResolver().update(ContentUris.withAppendedId(
+ Mailbox.CONTENT_URI, mMailboxId), values, null, null);
+ }
+
+ // Mark all messages as seen
+ final ContentValues contentValues = new ContentValues(1);
+ contentValues.put(MessageColumns.FLAG_SEEN, true);
+ final Uri uri = EmailContent.Message.CONTENT_URI;
+ mContext.getContentResolver().update(uri, contentValues,
+ MessageColumns.MAILBOX_KEY + " = ?",
+ new String[] {String.valueOf(mailbox.mId)});
}
}
}
@@ -3388,7 +3377,7 @@ outer:
* We need to do individual queries for the mailboxes in order to get correct
* folder capabilities.
*/
- Cursor getFolderListCursor(SQLiteDatabase db, Cursor c, String[] uiProjection) {
+ private Cursor getFolderListCursor(SQLiteDatabase db, Cursor c, String[] uiProjection) {
final MatrixCursor mc = new MatrixCursorWithCachedColumns(uiProjection);
Object[] values = new Object[uiProjection.length];
String[] args = new String[1];
@@ -3417,9 +3406,10 @@ outer:
* @param match the UriMatcher match for the original uri passed in from UnifiedEmail
* @param uri the original uri passed in from UnifiedEmail
* @param uiProjection the projection passed in from UnifiedEmail
+ * @param unseenOnly true
to only return unseen messages (where supported)
* @return the result Cursor
*/
- private Cursor uiQuery(int match, Uri uri, String[] uiProjection) {
+ private Cursor uiQuery(int match, Uri uri, String[] uiProjection, final boolean unseenOnly) {
Context context = getContext();
ContentResolver resolver = context.getContentResolver();
SQLiteDatabase db = getDatabase(context);
@@ -3443,9 +3433,10 @@ outer:
case UI_MESSAGES:
long mailboxId = Long.parseLong(id);
if (isVirtualMailbox(mailboxId)) {
- c = getVirtualMailboxMessagesCursor(db, uiProjection, mailboxId);
+ c = getVirtualMailboxMessagesCursor(db, uiProjection, mailboxId, unseenOnly);
} else {
- c = db.rawQuery(genQueryMailboxMessages(uiProjection), new String[] {id});
+ c = db.rawQuery(
+ genQueryMailboxMessages(uiProjection, unseenOnly), new String[] {id});
}
notifyUri = UIPROVIDER_CONVERSATION_NOTIFIER.buildUpon().appendPath(id).build();
c = new VisibilityCursor(context, c, mailboxId);
@@ -3508,7 +3499,7 @@ outer:
* @param uiAtt the UIProvider attachment to convert
* @return the EmailProvider attachment
*/
- private Attachment convertUiAttachmentToAttachment(
+ private static Attachment convertUiAttachmentToAttachment(
com.android.mail.providers.Attachment uiAtt) {
Attachment att = new Attachment();
att.setContentUri(uiAtt.contentUri.toString());
@@ -3572,7 +3563,7 @@ outer:
/**
* Given an account name and a mailbox type, return that mailbox, creating it if necessary
- * @param accountName the account name to use
+ * @param accountId the account id to use
* @param mailboxType the type of mailbox we're trying to find
* @return the mailbox of the given type for the account in the uri, or null if not found
*/
@@ -3619,6 +3610,7 @@ outer:
msg.mDisplayName = msg.mTo;
msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
msg.mFlagRead = true;
+ msg.mFlagSeen = true;
Integer quoteStartPos = values.getAsInteger(UIProvider.MessageColumns.QUOTE_START_POS);
msg.mQuotedTextStartPos = quoteStartPos == null ? 0 : quoteStartPos;
int flags = 0;
@@ -3629,7 +3621,7 @@ outer:
break;
case DraftType.REPLY_ALL:
flags |= Message.FLAG_TYPE_REPLY_ALL;
- // Fall through
+ //$FALL-THROUGH$
case DraftType.REPLY:
flags |= Message.FLAG_TYPE_REPLY;
break;
@@ -3818,7 +3810,8 @@ outer:
return 1;
}
- private void putIntegerLongOrBoolean(ContentValues values, String columnName, Object value) {
+ private static void putIntegerLongOrBoolean(ContentValues values, String columnName,
+ Object value) {
if (value instanceof Integer) {
Integer intValue = (Integer)value;
values.put(columnName, intValue);
@@ -3836,7 +3829,7 @@ outer:
* @param folders
* @return number of folders updated
*/
- private int updateTimestamp(final Context context, String id, Uri[] folders){
+ private static int updateTimestamp(final Context context, String id, Uri[] folders){
int updated = 0;
final long now = System.currentTimeMillis();
final ContentResolver resolver = context.getContentResolver();
@@ -3941,8 +3934,23 @@ outer:
return 0;
}
- private int uiUpdateFolder(Uri uri, ContentValues uiValues) {
- Uri ourUri = convertToEmailProviderUri(uri, Mailbox.CONTENT_URI, true);
+ private int uiUpdateFolder(final Context context, Uri uri, ContentValues uiValues) {
+ // We need to mark seen separately
+ if (uiValues.containsKey(UIProvider.ConversationColumns.SEEN)) {
+ final int seenValue = uiValues.getAsInteger(UIProvider.ConversationColumns.SEEN);
+
+ if (seenValue == 1) {
+ final String mailboxId = uri.getLastPathSegment();
+ final int rows = markAllSeen(context, mailboxId);
+
+ if (uiValues.size() == 1) {
+ // Nothing else to do, so return this value
+ return rows;
+ }
+ }
+ }
+
+ final Uri ourUri = convertToEmailProviderUri(uri, Mailbox.CONTENT_URI, true);
if (ourUri == null) return 0;
ContentValues ourValues = new ContentValues();
// This should only be called via update to "recent folders"
@@ -3954,6 +3962,17 @@ outer:
return update(ourUri, ourValues, null, null);
}
+ private int markAllSeen(final Context context, final String mailboxId) {
+ final SQLiteDatabase db = getDatabase(context);
+ final String table = Message.TABLE_NAME;
+ final ContentValues values = new ContentValues(1);
+ values.put(MessageColumns.FLAG_SEEN, 1);
+ final String whereClause = MessageColumns.MAILBOX_KEY + " = ?";
+ final String[] whereArgs = new String[] {mailboxId};
+
+ return db.update(table, values, whereClause, whereArgs);
+ }
+
private ContentValues convertUiMessageValues(Message message, ContentValues values) {
ContentValues ourValues = new ContentValues();
for (String columnName : values.keySet()) {
@@ -3962,6 +3981,8 @@ outer:
putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_FAVORITE, val);
} else if (columnName.equals(UIProvider.ConversationColumns.READ)) {
putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_READ, val);
+ } else if (columnName.equals(UIProvider.ConversationColumns.SEEN)) {
+ putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_SEEN, val);
} else if (columnName.equals(MessageColumns.MAILBOX_KEY)) {
putIntegerLongOrBoolean(ourValues, MessageColumns.MAILBOX_KEY, val);
} else if (columnName.equals(UIProvider.ConversationColumns.RAW_FOLDERS)) {
@@ -3994,7 +4015,7 @@ outer:
return ourValues;
}
- private Uri convertToEmailProviderUri(Uri uri, Uri newBaseUri, boolean asProvider) {
+ private static Uri convertToEmailProviderUri(Uri uri, Uri newBaseUri, boolean asProvider) {
String idString = uri.getLastPathSegment();
try {
long id = Long.parseLong(idString);
@@ -4034,7 +4055,7 @@ outer:
}
// TODO: This should depend on flags on the mailbox...
- private boolean uploadsToServer(Context context, Mailbox m) {
+ private static boolean uploadsToServer(Context context, Mailbox m) {
if (m.mType == Mailbox.TYPE_DRAFTS || m.mType == Mailbox.TYPE_OUTBOX ||
m.mType == Mailbox.TYPE_SEARCH) {
return false;
@@ -4084,6 +4105,8 @@ outer:
undoValues.put(MessageColumns.MAILBOX_KEY, msg.mMailboxKey);
} else if (columnName.equals(MessageColumns.FLAG_READ)) {
undoValues.put(MessageColumns.FLAG_READ, msg.mFlagRead);
+ } else if (columnName.equals(MessageColumns.FLAG_SEEN)) {
+ undoValues.put(MessageColumns.FLAG_SEEN, msg.mFlagSeen);
} else if (columnName.equals(MessageColumns.FLAG_FAVORITE)) {
undoValues.put(MessageColumns.FLAG_FAVORITE, msg.mFlagFavorite);
}
@@ -4403,7 +4426,7 @@ outer:
// This will look just like a "normal" folder
return uiQuery(UI_FOLDER, ContentUris.withAppendedId(Mailbox.CONTENT_URI,
- searchMailbox.mId), projection);
+ searchMailbox.mId), projection, false);
}
private static final String MAILBOXES_FOR_ACCOUNT_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?";
@@ -4454,7 +4477,7 @@ outer:
return 1;
}
- private void deleteAccountData(Context context, long accountId) {
+ private static void deleteAccountData(Context context, long accountId) {
// Delete synced attachments
AttachmentUtilities.deleteAllAccountAttachmentFiles(context, accountId);
diff --git a/src/com/android/email/provider/WidgetProvider.java b/src/com/android/email/provider/WidgetProvider.java
index 5920ee944..f94a25da2 100644
--- a/src/com/android/email/provider/WidgetProvider.java
+++ b/src/com/android/email/provider/WidgetProvider.java
@@ -16,21 +16,16 @@
package com.android.email.provider;
-import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
-import android.database.CursorWrapper;
import android.net.Uri;
-import android.provider.BaseColumns;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.Mailbox;
import com.android.mail.providers.Folder;
import com.android.mail.providers.UIProvider;
-import com.android.mail.providers.UIProvider.AccountColumns;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
import com.android.mail.widget.BaseWidgetProvider;
@@ -43,56 +38,6 @@ public class WidgetProvider extends BaseWidgetProvider {
private static final String LOG_TAG = LogTag.getLogTag();
- // This projection is needed, as if we were to request the capabilities of the account,
- // that provider attempts to bind to the email service to get this information. It is not
- // valid to bind to a service in a broadcast receiver, as the bind just blocks, for the amount
- // of time specified in the timeout.
- // Instead, this projection doesn't include the capabilities column. The cursor wrapper then
- // makes sure that the Account objects can find all of the columns it expects.
- private static final String[] WIDGET_ACCOUNTS_PROJECTION = {
- BaseColumns._ID,
- AccountColumns.NAME,
- AccountColumns.PROVIDER_VERSION,
- AccountColumns.URI,
- AccountColumns.FOLDER_LIST_URI,
- AccountColumns.FULL_FOLDER_LIST_URI,
- AccountColumns.SEARCH_URI,
- AccountColumns.ACCOUNT_FROM_ADDRESSES,
- AccountColumns.SAVE_DRAFT_URI,
- AccountColumns.SEND_MAIL_URI,
- AccountColumns.EXPUNGE_MESSAGE_URI,
- AccountColumns.UNDO_URI,
- AccountColumns.SETTINGS_INTENT_URI,
- AccountColumns.SYNC_STATUS,
- AccountColumns.HELP_INTENT_URI,
- AccountColumns.SEND_FEEDBACK_INTENT_URI,
- AccountColumns.REAUTHENTICATION_INTENT_URI,
- AccountColumns.COMPOSE_URI,
- AccountColumns.MIME_TYPE,
- AccountColumns.RECENT_FOLDER_LIST_URI,
- AccountColumns.COLOR,
- AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI,
- AccountColumns.MANUAL_SYNC_URI,
- AccountColumns.VIEW_INTENT_PROXY_URI,
- AccountColumns.SettingsColumns.SIGNATURE,
- AccountColumns.SettingsColumns.AUTO_ADVANCE,
- AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE,
- AccountColumns.SettingsColumns.SNAP_HEADERS,
- AccountColumns.SettingsColumns.REPLY_BEHAVIOR,
- AccountColumns.SettingsColumns.SHOW_CHECKBOXES,
- AccountColumns.SettingsColumns.CONFIRM_DELETE,
- AccountColumns.SettingsColumns.CONFIRM_ARCHIVE,
- AccountColumns.SettingsColumns.CONFIRM_SEND,
- AccountColumns.SettingsColumns.DEFAULT_INBOX,
- AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME,
- AccountColumns.SettingsColumns.FORCE_REPLY_FROM_DEFAULT,
- AccountColumns.SettingsColumns.MAX_ATTACHMENT_SIZE,
- AccountColumns.SettingsColumns.SWIPE,
- AccountColumns.SettingsColumns.PRIORITY_ARROWS_ENABLED,
- AccountColumns.SettingsColumns.SETUP_INTENT_URI
- };
-
-
/**
* Remove preferences when deleting widget
*/
@@ -115,10 +60,10 @@ public class WidgetProvider extends BaseWidgetProvider {
protected com.android.mail.providers.Account getAccountObject(
Context context, String accountUri) {
final ContentResolver resolver = context.getContentResolver();
- final Cursor sparseAccountCursor = resolver.query(Uri.parse(accountUri),
- WIDGET_ACCOUNTS_PROJECTION, null, null, null);
+ final Cursor accountCursor = resolver.query(Uri.parse(accountUri),
+ UIProvider.ACCOUNTS_PROJECTION_NO_CAPABILITIES, null, null, null);
- return getPopulatedAccountObject(sparseAccountCursor);
+ return getPopulatedAccountObject(accountCursor);
}
@@ -126,13 +71,13 @@ public class WidgetProvider extends BaseWidgetProvider {
protected boolean isAccountValid(Context context, com.android.mail.providers.Account account) {
if (account != null) {
final ContentResolver resolver = context.getContentResolver();
- final Cursor sparseAccountCursor = resolver.query(account.uri,
- WIDGET_ACCOUNTS_PROJECTION, null, null, null);
- if (sparseAccountCursor != null) {
+ final Cursor accountCursor = resolver.query(account.uri,
+ UIProvider.ACCOUNTS_PROJECTION_NO_CAPABILITIES, null, null, null);
+ if (accountCursor != null) {
try {
- return sparseAccountCursor.getCount() > 0;
+ return accountCursor.getCount() > 0;
} finally {
- sparseAccountCursor.close();
+ accountCursor.close();
}
}
}
@@ -169,24 +114,23 @@ public class WidgetProvider extends BaseWidgetProvider {
editor.apply();
}
- private com.android.mail.providers.Account getAccount(Context context, long accountId) {
+ private static com.android.mail.providers.Account getAccount(Context context, long accountId) {
final ContentResolver resolver = context.getContentResolver();
final Cursor ac = resolver.query(EmailProvider.uiUri("uiaccount", accountId),
- WIDGET_ACCOUNTS_PROJECTION, null, null, null);
+ UIProvider.ACCOUNTS_PROJECTION_NO_CAPABILITIES, null, null, null);
com.android.mail.providers.Account uiAccount = getPopulatedAccountObject(ac);
return uiAccount;
}
- private com.android.mail.providers.Account getPopulatedAccountObject(final Cursor ac) {
- if (ac == null) {
+ private static com.android.mail.providers.Account getPopulatedAccountObject(
+ final Cursor accountCursor) {
+ if (accountCursor == null) {
LogUtils.e(LOG_TAG, "Null account cursor");
return null;
}
- final Cursor accountCursor = new SparseAccountCursorWrapper(ac);
-
com.android.mail.providers.Account uiAccount = null;
try {
if (accountCursor.moveToFirst()) {
@@ -198,7 +142,7 @@ public class WidgetProvider extends BaseWidgetProvider {
return uiAccount;
}
- private Folder getFolder(Context context, long mailboxId) {
+ private static Folder getFolder(Context context, long mailboxId) {
final ContentResolver resolver = context.getContentResolver();
final Cursor fc = resolver.query(EmailProvider.uiUri("uifolder", mailboxId),
UIProvider.FOLDERS_PROJECTION, null, null, null);
@@ -240,64 +184,4 @@ public class WidgetProvider extends BaseWidgetProvider {
long mailboxId = prefs.getLong(LEGACY_MAILBOX_ID_PREFIX + appWidgetId, Mailbox.NO_MAILBOX);
return mailboxId;
}
-
- private class SparseAccountCursorWrapper extends CursorWrapper {
- public SparseAccountCursorWrapper(Cursor cursor) {
- super(cursor);
- }
-
- @Override
- public int getColumnCount () {
- return UIProvider.ACCOUNTS_PROJECTION.length;
- }
-
- @Override
- public int getColumnIndex (String columnName) {
- for (int i = 0; i < UIProvider.ACCOUNTS_PROJECTION.length; i++) {
- if (UIProvider.ACCOUNTS_PROJECTION[i].equals(columnName)) {
- return i;
- }
- }
- return -1;
- }
-
- @Override
- public String getColumnName (int columnIndex) {
- return UIProvider.ACCOUNTS_PROJECTION[columnIndex];
- }
-
- @Override
- public String[] getColumnNames () {
- return UIProvider.ACCOUNTS_PROJECTION;
- }
-
- @Override
- public int getInt (int columnIndex) {
- if (columnIndex == UIProvider.ACCOUNT_CAPABILITIES_COLUMN) {
- return 0;
- }
- return super.getInt(convertColumnIndex(columnIndex));
- }
-
- @Override
- public long getLong (int columnIndex) {
- return super.getLong(convertColumnIndex(columnIndex));
- }
-
- @Override
- public String getString (int columnIndex) {
- return super.getString(convertColumnIndex(columnIndex));
- }
-
- private int convertColumnIndex(int columnIndex) {
- // Since this sparse cursor doesn't have the capabilities column,
- // we need to adjust all of the column indexes that come after where the
- // capabilities column should be
- if (columnIndex > UIProvider.ACCOUNT_CAPABILITIES_COLUMN) {
- return columnIndex - 1;
- }
- return columnIndex;
- }
- }
-
}
diff --git a/src/com/android/email/service/EmailBroadcastProcessorService.java b/src/com/android/email/service/EmailBroadcastProcessorService.java
index 442feac57..e5d3d3755 100644
--- a/src/com/android/email/service/EmailBroadcastProcessorService.java
+++ b/src/com/android/email/service/EmailBroadcastProcessorService.java
@@ -29,7 +29,6 @@ import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
-import com.android.email.NotificationController;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.SecurityPolicy;
@@ -65,10 +64,6 @@ public class EmailBroadcastProcessorService extends IntentService {
private static final String ACTION_DEVICE_POLICY_ADMIN = "com.android.email.devicepolicy";
private static final String EXTRA_DEVICE_POLICY_ADMIN = "message_code";
- // Broadcast received to initiate new message notification updates
- public static final String ACTION_NOTIFY_NEW_MAIL =
- "com.android.mail.action.update_notification";
-
public EmailBroadcastProcessorService() {
// Class name will be the thread name.
super(EmailBroadcastProcessorService.class.getName());
@@ -116,8 +111,6 @@ public class EmailBroadcastProcessorService extends IntentService {
AccountSettings.actionSettingsWithDebug(this);
} else if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(broadcastAction)) {
onSystemAccountChanged();
- } else if (ACTION_NOTIFY_NEW_MAIL.equals(broadcastAction)) {
- NotificationController.notifyNewMail(this, broadcastIntent);
}
} else if (ACTION_DEVICE_POLICY_ADMIN.equals(action)) {
int message = intent.getIntExtra(EXTRA_DEVICE_POLICY_ADMIN, -1);
diff --git a/src/com/android/email2/ui/MailActivityEmail.java b/src/com/android/email2/ui/MailActivityEmail.java
index 7a0870c3a..c9c7612a7 100644
--- a/src/com/android/email2/ui/MailActivityEmail.java
+++ b/src/com/android/email2/ui/MailActivityEmail.java
@@ -144,7 +144,7 @@ public class MailActivityEmail extends com.android.mail.ui.MailActivity {
// Start/stop the various services depending on whether there are any accounts
startOrStopService(enabled, context, new Intent(context, AttachmentDownloadService.class));
- NotificationController.getInstance(context).watchForMessages(enabled);
+ NotificationController.getInstance(context).watchForMessages();
}
/**
diff --git a/src/com/android/mail/preferences/PreferenceMigrator.java b/src/com/android/mail/preferences/PreferenceMigrator.java
new file mode 100644
index 000000000..d5ef3688c
--- /dev/null
+++ b/src/com/android/mail/preferences/PreferenceMigrator.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013 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.mail.preferences;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+
+import com.android.emailcommon.provider.EmailContent;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.Folder;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Migrates Email settings to UnifiedEmail
+ */
+public class PreferenceMigrator extends BasePreferenceMigrator {
+ private static final String LOG_TAG = LogTag.getLogTag();
+
+ @Override
+ protected void migrate(final Context context, final int oldVersion, final int newVersion) {
+ final List accounts = new ArrayList();
+
+ final Cursor accountCursor = context.getContentResolver().query(Uri.parse(
+ EmailContent.CONTENT_URI + "/uiaccts"),
+ UIProvider.ACCOUNTS_PROJECTION_NO_CAPABILITIES, null, null, null);
+ try {
+ while (accountCursor.moveToNext()) {
+ accounts.add(new Account(accountCursor));
+ }
+ } finally {
+ accountCursor.close();
+ }
+
+ migrate(context, oldVersion, newVersion, accounts);
+ }
+
+ public static final String REPLY_ALL = "reply_all";
+
+ private static final String PREFERENCE_NOTIFY = "account_notify";
+ private static final String PREFERENCE_VIBRATE = "account_settings_vibrate";
+ private static final String PREFERENCE_VIBRATE_OLD = "account_settings_vibrate_when";
+ private static final String PREFERENCE_RINGTONE = "account_ringtone";
+
+ protected static void migrate(final Context context, final int oldVersion, final int newVersion,
+ final List accounts) {
+ if (oldVersion < 1) {
+ // No global settings to move yet
+
+ // Move folder notification settings
+ for (final Account account : accounts) {
+ // The only setting in AccountPreferences so far is a global notification toggle,
+ // but we only allow Inbox notifications, so it will remain unused
+ final Cursor folderCursor =
+ context.getContentResolver().query(account.settings.defaultInbox,
+ UIProvider.FOLDERS_PROJECTION, null, null, null);
+
+ if (folderCursor == null) {
+ LogUtils.e(LOG_TAG, "Null folder cursor for mailbox %s",
+ account.settings.defaultInbox);
+ continue;
+ }
+
+ Folder folder = null;
+ try {
+ if (folderCursor.moveToFirst()) {
+ folder = new Folder(folderCursor);
+ }
+ } finally {
+ folderCursor.close();
+ }
+
+ final FolderPreferences folderPreferences =
+ new FolderPreferences(context, account.name, folder, true /* inbox */);
+
+ final SharedPreferences sharedPreferences =
+ PreferenceManager.getDefaultSharedPreferences(context);
+
+ if (sharedPreferences.contains(PREFERENCE_NOTIFY)) {
+ final boolean notify = sharedPreferences.getBoolean(PREFERENCE_NOTIFY, true);
+ folderPreferences.setNotificationsEnabled(notify);
+ }
+
+ if (sharedPreferences.contains(PREFERENCE_RINGTONE)) {
+ final String ringtoneUri =
+ sharedPreferences.getString(PREFERENCE_RINGTONE, null);
+ folderPreferences.setNotificationRingtoneUri(ringtoneUri);
+ }
+
+ if (sharedPreferences.contains(PREFERENCE_VIBRATE)) {
+ final boolean vibrate = sharedPreferences.getBoolean(PREFERENCE_VIBRATE, false);
+ folderPreferences.setNotificationVibrateEnabled(vibrate);
+ } else if (sharedPreferences.contains(PREFERENCE_VIBRATE_OLD)) {
+ final boolean vibrate = "always".equals(
+ sharedPreferences.getString(PREFERENCE_VIBRATE_OLD, ""));
+ folderPreferences.setNotificationVibrateEnabled(vibrate);
+ }
+ }
+ }
+ }
+}