replicant-packages_apps_Email/emailcommon/src/com/android/emailcommon/provider/Account.java

1007 lines
37 KiB
Java
Executable File

/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.emailcommon.provider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.ContentObserver;
import android.database.Cursor;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import com.android.emailcommon.service.EmailServiceProxy;
import com.android.emailcommon.utility.Utility;
import com.android.mail.utils.LogUtils;
import com.google.common.annotations.VisibleForTesting;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
public final class Account extends EmailContent implements Parcelable {
public static final String TABLE_NAME = "Account";
// Define all pseudo account IDs here to avoid conflict with one another.
/**
* Pseudo account ID to represent a "combined account" that includes messages and mailboxes
* from all defined accounts.
*
* <em>IMPORTANT</em>: This must never be stored to the database.
*/
public static final long ACCOUNT_ID_COMBINED_VIEW = 0x1000000000000000L;
/**
* Pseudo account ID to represent "no account". This may be used any time the account ID
* may not be known or when we want to specifically select "no" account.
*
* <em>IMPORTANT</em>: This must never be stored to the database.
*/
public static final long NO_ACCOUNT = -1L;
/**
* Whether or not the user has asked for notifications of new mail in this account
*
* @deprecated Used only for migration
*/
@Deprecated
public final static int FLAGS_NOTIFY_NEW_MAIL = 1<<0;
/**
* Whether or not the user has asked for vibration notifications with all new mail
*
* @deprecated Used only for migration
*/
@Deprecated
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;
// Whether the account is in the process of being created; any account reconciliation code
// MUST ignore accounts with this bit set; in addition, ContentObservers for this data
// SHOULD consider the state of this flag during operation
public static final int FLAGS_INCOMPLETE = 1<<4;
// Security hold is used when the device is not in compliance with security policies
// required by the server; in this state, the user MUST be alerted to the need to update
// security settings. Sync adapters SHOULD NOT attempt to sync when this flag is set.
public static final int FLAGS_SECURITY_HOLD = 1<<5;
// Whether the account supports "smart forward" (i.e. the server appends the original
// message along with any attachments to the outgoing message)
public static final int FLAGS_SUPPORTS_SMART_FORWARD = 1<<7;
// Whether the account should try to cache attachments in the background
public static final int FLAGS_BACKGROUND_ATTACHMENTS = 1<<8;
// Available to sync adapter
public static final int FLAGS_SYNC_ADAPTER = 1<<9;
// Sync disabled is a status commanded by the server; the sync adapter SHOULD NOT try to
// sync mailboxes in this account automatically. A manual sync request to sync a mailbox
// with sync disabled SHOULD try to sync and report any failure result via the UI.
public static final int FLAGS_SYNC_DISABLED = 1<<10;
// Whether or not server-side search is supported by this account
public static final int FLAGS_SUPPORTS_SEARCH = 1<<11;
// Whether or not server-side search supports global search (i.e. all mailboxes); only valid
// if FLAGS_SUPPORTS_SEARCH is true
public static final int FLAGS_SUPPORTS_GLOBAL_SEARCH = 1<<12;
// Whether or not the initial folder list has been loaded
public static final int FLAGS_INITIAL_FOLDER_LIST_LOADED = 1<<13;
// Deletion policy (see FLAGS_DELETE_POLICY_MASK, above)
public static final int DELETE_POLICY_NEVER = 0;
public static final int DELETE_POLICY_7DAYS = 1<<0; // not supported
public static final int DELETE_POLICY_ON_DELETE = 1<<1;
// Sentinel values for the mSyncInterval field of both Account records
public static final int CHECK_INTERVAL_NEVER = -1;
public static final int CHECK_INTERVAL_PUSH = -2;
public static final int CHECK_INTERVAL_DEFAULT_PULL = 15;
public static Uri CONTENT_URI;
public static Uri RESET_NEW_MESSAGE_COUNT_URI;
public static Uri NOTIFIER_URI;
public static Uri SYNC_SETTING_CHANGED_URI;
public static void initAccount() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account");
RESET_NEW_MESSAGE_COUNT_URI = Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount");
NOTIFIER_URI = Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account");
SYNC_SETTING_CHANGED_URI = Uri.parse(
EmailContent.CONTENT_SYNC_SETTING_CHANGED_URI + "/account");
}
public String mDisplayName;
public String mEmailAddress;
public String mSyncKey;
public int mSyncLookback;
private int mSyncInterval;
public long mHostAuthKeyRecv;
public long mHostAuthKeySend;
public int mFlags;
public String mSenderName;
/** @deprecated Used only for migration */
@Deprecated
private String mRingtoneUri;
public String mProtocolVersion;
public String mSecuritySyncKey;
public String mSignature;
public long mPolicyKey;
public long mPingDuration;
public int mCapabilities;
@VisibleForTesting
static final String JSON_TAG_HOST_AUTH_RECV = "hostAuthRecv";
@VisibleForTesting
static final String JSON_TAG_HOST_AUTH_SEND = "hostAuthSend";
// Convenience for creating/working with an account
public transient HostAuth mHostAuthRecv;
public transient HostAuth mHostAuthSend;
public transient Policy mPolicy;
// Marks this account as being a temporary entry, so we know to use it directly and not go
// through the database or any caches
private transient boolean mTemporary;
public static final int CONTENT_ID_COLUMN = 0;
public static final int CONTENT_DISPLAY_NAME_COLUMN = 1;
public static final int CONTENT_EMAIL_ADDRESS_COLUMN = 2;
public static final int CONTENT_SYNC_KEY_COLUMN = 3;
public static final int CONTENT_SYNC_LOOKBACK_COLUMN = 4;
public static final int CONTENT_SYNC_INTERVAL_COLUMN = 5;
public static final int CONTENT_HOST_AUTH_KEY_RECV_COLUMN = 6;
public static final int CONTENT_HOST_AUTH_KEY_SEND_COLUMN = 7;
public static final int CONTENT_FLAGS_COLUMN = 8;
public static final int CONTENT_SENDER_NAME_COLUMN = 9;
public static final int CONTENT_RINGTONE_URI_COLUMN = 10;
public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 11;
public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 12;
public static final int CONTENT_SIGNATURE_COLUMN = 13;
public static final int CONTENT_POLICY_KEY_COLUMN = 14;
public static final int CONTENT_PING_DURATION_COLUMN = 15;
public static final int CONTENT_MAX_ATTACHMENT_SIZE_COLUMN = 16;
public static final int CONTENT_CAPABILITIES_COLUMN = 17;
public static final String[] CONTENT_PROJECTION = {
AttachmentColumns._ID, AccountColumns.DISPLAY_NAME,
AccountColumns.EMAIL_ADDRESS, AccountColumns.SYNC_KEY, AccountColumns.SYNC_LOOKBACK,
AccountColumns.SYNC_INTERVAL, AccountColumns.HOST_AUTH_KEY_RECV,
AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS,
AccountColumns.SENDER_NAME,
AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION,
AccountColumns.SECURITY_SYNC_KEY,
AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY, AccountColumns.PING_DURATION,
AccountColumns.MAX_ATTACHMENT_SIZE, AccountColumns.CAPABILITIES
};
public static final int ACCOUNT_FLAGS_COLUMN_ID = 0;
public static final int ACCOUNT_FLAGS_COLUMN_FLAGS = 1;
public static final String[] ACCOUNT_FLAGS_PROJECTION = {
AccountColumns._ID, AccountColumns.FLAGS};
public static final String SECURITY_NONZERO_SELECTION =
AccountColumns.POLICY_KEY + " IS NOT NULL AND " + AccountColumns.POLICY_KEY + "!=0";
private static final String FIND_INBOX_SELECTION =
MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX +
" AND " + MailboxColumns.ACCOUNT_KEY + " =?";
public Account() {
mBaseUri = CONTENT_URI;
// other defaults (policy)
mRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).toString();
mSyncInterval = -1;
mSyncLookback = -1;
mFlags = 0;
}
public static Account restoreAccountWithId(Context context, long id) {
return restoreAccountWithId(context, id, null);
}
public static Account restoreAccountWithId(Context context, long id, ContentObserver observer) {
return EmailContent.restoreContentWithId(context, Account.class,
Account.CONTENT_URI, Account.CONTENT_PROJECTION, id, observer);
}
public static Account restoreAccountWithAddress(Context context, String emailAddress) {
return restoreAccountWithAddress(context, emailAddress, null);
}
public static Account restoreAccountWithAddress(Context context, String emailAddress,
ContentObserver observer) {
final Cursor c = context.getContentResolver().query(CONTENT_URI,
new String[] {AccountColumns._ID},
AccountColumns.EMAIL_ADDRESS + "=?", new String[] {emailAddress},
null);
try {
if (c == null || !c.moveToFirst()) {
return null;
}
final long id = c.getLong(c.getColumnIndex(AccountColumns._ID));
return restoreAccountWithId(context, id, observer);
} finally {
if (c != null) {
c.close();
}
}
}
@Override
protected Uri getContentNotificationUri() {
return Account.CONTENT_URI;
}
/**
* Refresh an account that has already been loaded. This is slightly less expensive
* that generating a brand-new account object.
*/
public void refresh(Context context) {
Cursor c = context.getContentResolver().query(getUri(), Account.CONTENT_PROJECTION,
null, null, null);
try {
c.moveToFirst();
restore(c);
} finally {
if (c != null) {
c.close();
}
}
}
@Override
public void restore(Cursor cursor) {
mId = cursor.getLong(CONTENT_ID_COLUMN);
mBaseUri = CONTENT_URI;
mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN);
mEmailAddress = cursor.getString(CONTENT_EMAIL_ADDRESS_COLUMN);
mSyncKey = cursor.getString(CONTENT_SYNC_KEY_COLUMN);
mSyncLookback = cursor.getInt(CONTENT_SYNC_LOOKBACK_COLUMN);
mSyncInterval = cursor.getInt(CONTENT_SYNC_INTERVAL_COLUMN);
mHostAuthKeyRecv = cursor.getLong(CONTENT_HOST_AUTH_KEY_RECV_COLUMN);
mHostAuthKeySend = cursor.getLong(CONTENT_HOST_AUTH_KEY_SEND_COLUMN);
mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
mSenderName = cursor.getString(CONTENT_SENDER_NAME_COLUMN);
mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN);
mProtocolVersion = cursor.getString(CONTENT_PROTOCOL_VERSION_COLUMN);
mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN);
mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN);
mPolicyKey = cursor.getLong(CONTENT_POLICY_KEY_COLUMN);
mPingDuration = cursor.getLong(CONTENT_PING_DURATION_COLUMN);
mCapabilities = cursor.getInt(CONTENT_CAPABILITIES_COLUMN);
}
public boolean isTemporary() {
return mTemporary;
}
public void setTemporary(boolean temporary) {
mTemporary = temporary;
}
private static long getId(Uri u) {
return Long.parseLong(u.getPathSegments().get(1));
}
public long getId() {
return mId;
}
/**
* Returns the user-visible name for the account, eg. "My work address"
* or "foo@exemple.com".
* @return the user-visible name for the account.
*/
public String getDisplayName() {
return mDisplayName;
}
/**
* Set the description. Be sure to call save() to commit to database.
* @param description the new description
*/
public void setDisplayName(String description) {
mDisplayName = description;
}
/**
* @return the email address for this account
*/
public String getEmailAddress() {
return mEmailAddress;
}
/**
* Set the Email address for this account. Be sure to call save() to commit to database.
* @param emailAddress the new email address for this account
*/
public void setEmailAddress(String emailAddress) {
mEmailAddress = emailAddress;
}
/**
* @return the sender's name for this account
*/
public String getSenderName() {
return mSenderName;
}
/**
* Set the sender's name. Be sure to call save() to commit to database.
* @param name the new sender name
*/
public void setSenderName(String name) {
mSenderName = name;
}
public String getSignature() {
return mSignature;
}
@VisibleForTesting
public void setSignature(String signature) {
mSignature = signature;
}
/**
* @return the minutes per check (for polling)
* TODO define sentinel values for "never", "push", etc. See Account.java
*/
public int getSyncInterval() {
// Fixed unsynced value and account capability. Change to default pull value
if (!hasCapability(EmailServiceProxy.CAPABILITY_PUSH)
&& mSyncInterval == CHECK_INTERVAL_PUSH) {
return CHECK_INTERVAL_DEFAULT_PULL;
}
return mSyncInterval;
}
/**
* Set the minutes per check (for polling). Be sure to call save() to commit to database.
* TODO define sentinel values for "never", "push", etc. See Account.java
* @param minutes the number of minutes between polling checks
*/
public void setSyncInterval(int minutes) {
// Fixed unsynced value and account capability. Change to default pull value
if (!hasCapability(EmailServiceProxy.CAPABILITY_PUSH)
&& mSyncInterval == CHECK_INTERVAL_PUSH) {
mSyncInterval = CHECK_INTERVAL_DEFAULT_PULL;
} else {
mSyncInterval = minutes;
}
}
/**
* @return One of the {@code Account.SYNC_WINDOW_*} constants that represents the sync
* lookback window.
* TODO define sentinel values for "all", "1 month", etc. See Account.java
*/
public int getSyncLookback() {
return mSyncLookback;
}
/**
* Set the sync lookback window. Be sure to call save() to commit to database.
* TODO define sentinel values for "all", "1 month", etc. See Account.java
* @param value One of the {@link com.android.emailcommon.service.SyncWindow} constants
*/
public void setSyncLookback(int value) {
mSyncLookback = value;
}
/**
* @return the current ping duration.
*/
public long getPingDuration() {
return mPingDuration;
}
/**
* Set the ping duration. Be sure to call save() to commit to database.
*/
public void setPingDuration(long value) {
mPingDuration = value;
}
/**
* @return the current account capabilities.
*/
public int getCapabilities() {
return mCapabilities;
}
/**
* Set the account capabilities. Be sure to call save() to commit to database.
*/
public void setCapabilities(int value) {
mCapabilities = value;
}
/**
* @return the flags for this account
*/
public int getFlags() {
return mFlags;
}
/**
* Set the flags for this account
* @param newFlags the new value for the flags
*/
public void setFlags(int newFlags) {
mFlags = newFlags;
}
/**
* @return the ringtone Uri for this account
* @deprecated Used only for migration
*/
@Deprecated
public String getRingtone() {
return mRingtoneUri;
}
/**
* Set the "delete policy" as a simple 0,1,2 value set.
* @param newPolicy the new delete policy
*/
public void setDeletePolicy(int newPolicy) {
mFlags &= ~FLAGS_DELETE_POLICY_MASK;
mFlags |= (newPolicy << FLAGS_DELETE_POLICY_SHIFT) & FLAGS_DELETE_POLICY_MASK;
}
/**
* Return the "delete policy" as a simple 0,1,2 value set.
* @return the current delete policy
*/
public int getDeletePolicy() {
return (mFlags & FLAGS_DELETE_POLICY_MASK) >> FLAGS_DELETE_POLICY_SHIFT;
}
public HostAuth getOrCreateHostAuthSend(Context context) {
if (mHostAuthSend == null) {
if (mHostAuthKeySend != 0) {
mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend);
} else {
mHostAuthSend = new HostAuth();
}
}
return mHostAuthSend;
}
public HostAuth getOrCreateHostAuthRecv(Context context) {
if (mHostAuthRecv == null) {
if (mHostAuthKeyRecv != 0) {
mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv);
} else {
mHostAuthRecv = new HostAuth();
}
}
return mHostAuthRecv;
}
/**
* Return the id of the default account. If one hasn't been explicitly specified, return the
* first one in the database. If no account exists, returns {@link #NO_ACCOUNT}.
*
* @param context the caller's context
* @param lastUsedAccountId the last used account id, which is the basis of the default account
*/
public static long getDefaultAccountId(final Context context, final long lastUsedAccountId) {
final Cursor cursor = context.getContentResolver().query(
CONTENT_URI, ID_PROJECTION, null, null, null);
long firstAccount = NO_ACCOUNT;
try {
if (cursor != null && cursor.moveToFirst()) {
do {
final long accountId = cursor.getLong(Account.ID_PROJECTION_COLUMN);
if (accountId == lastUsedAccountId) {
return accountId;
}
if (firstAccount == NO_ACCOUNT) {
firstAccount = accountId;
}
} while (cursor.moveToNext());
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return firstAccount;
}
/**
* Given an account id, return the account's protocol
* @param context the caller's context
* @param accountId the id of the account to be examined
* @return the account's protocol (or null if the Account or HostAuth do not exist)
*/
public static String getProtocol(Context context, long accountId) {
Account account = Account.restoreAccountWithId(context, accountId);
if (account != null) {
return account.getProtocol(context);
}
return null;
}
/**
* Return the account's protocol
* @param context the caller's context
* @return the account's protocol (or null if the HostAuth doesn't not exist)
*/
public String getProtocol(Context context) {
HostAuth hostAuth = getOrCreateHostAuthRecv(context);
if (hostAuth != null) {
return hostAuth.mProtocol;
}
return null;
}
/**
* Return a corresponding account manager object using the passed in type
*
* @param type We can't look up the account type from here, so pass it in
* @return system account object
*/
public android.accounts.Account getAccountManagerAccount(String type) {
return new android.accounts.Account(mEmailAddress, type);
}
/**
* Return the account ID for a message with a given id
*
* @param context the caller's context
* @param messageId the id of the message
* @return the account ID, or -1 if the account doesn't exist
*/
public static long getAccountIdForMessageId(Context context, long messageId) {
return Message.getKeyColumnLong(context, messageId, MessageColumns.ACCOUNT_KEY);
}
/**
* Return the account for a message with a given id
* @param context the caller's context
* @param messageId the id of the message
* @return the account, or null if the account doesn't exist
*/
public static Account getAccountForMessageId(Context context, long messageId) {
long accountId = getAccountIdForMessageId(context, messageId);
if (accountId != -1) {
return Account.restoreAccountWithId(context, accountId);
}
return null;
}
/**
* @return true if an {@code accountId} is assigned to any existing account.
*/
public static boolean isValidId(Context context, long accountId) {
return null != Utility.getFirstRowLong(context, CONTENT_URI, ID_PROJECTION,
ID_SELECTION, new String[] {Long.toString(accountId)}, null,
ID_PROJECTION_COLUMN);
}
/**
* Check a single account for security hold status.
*/
public static boolean isSecurityHold(Context context, long accountId) {
return (Utility.getFirstRowLong(context,
ContentUris.withAppendedId(Account.CONTENT_URI, accountId),
ACCOUNT_FLAGS_PROJECTION, null, null, null, ACCOUNT_FLAGS_COLUMN_FLAGS, 0L)
& Account.FLAGS_SECURITY_HOLD) != 0;
}
/**
* @return id of the "inbox" mailbox, or -1 if not found.
*/
public static long getInboxId(Context context, long accountId) {
return Utility.getFirstRowLong(context, Mailbox.CONTENT_URI, ID_PROJECTION,
FIND_INBOX_SELECTION, new String[] {Long.toString(accountId)}, null,
ID_PROJECTION_COLUMN, -1L);
}
/**
* Clear all account hold flags that are set.
*
* (This will trigger watchers, and in particular will cause EAS to try and resync the
* account(s).)
*/
public static void clearSecurityHoldOnAllAccounts(Context context) {
ContentResolver resolver = context.getContentResolver();
Cursor c = resolver.query(Account.CONTENT_URI, ACCOUNT_FLAGS_PROJECTION,
SECURITY_NONZERO_SELECTION, null, null);
try {
while (c.moveToNext()) {
int flags = c.getInt(ACCOUNT_FLAGS_COLUMN_FLAGS);
if (0 != (flags & FLAGS_SECURITY_HOLD)) {
ContentValues cv = new ContentValues();
cv.put(AccountColumns.FLAGS, flags & ~FLAGS_SECURITY_HOLD);
long accountId = c.getLong(ACCOUNT_FLAGS_COLUMN_ID);
Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
resolver.update(uri, cv, null, null);
}
}
} finally {
c.close();
}
}
/*
* Override this so that we can store the HostAuth's first and link them to the Account
* (non-Javadoc)
* @see com.android.email.provider.EmailContent#save(android.content.Context)
*/
@Override
public Uri save(Context context) {
if (isSaved()) {
throw new UnsupportedOperationException();
}
// This logic is in place so I can (a) short circuit the expensive stuff when
// possible, and (b) override (and throw) if anyone tries to call save() or update()
// directly for Account, which are unsupported.
if (mHostAuthRecv == null && mHostAuthSend == null && mPolicy != null) {
return super.save(context);
}
int index = 0;
int recvIndex = -1;
int recvCredentialsIndex = -1;
int sendIndex = -1;
int sendCredentialsIndex = -1;
// Create operations for saving the send and recv hostAuths, and their credentials.
// Also, remember which operation in the array they represent
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
if (mHostAuthRecv != null) {
if (mHostAuthRecv.mCredential != null) {
recvCredentialsIndex = index++;
ops.add(ContentProviderOperation.newInsert(mHostAuthRecv.mCredential.mBaseUri)
.withValues(mHostAuthRecv.mCredential.toContentValues())
.build());
}
recvIndex = index++;
final ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(
mHostAuthRecv.mBaseUri);
b.withValues(mHostAuthRecv.toContentValues());
if (recvCredentialsIndex >= 0) {
final ContentValues cv = new ContentValues();
cv.put(HostAuthColumns.CREDENTIAL_KEY, recvCredentialsIndex);
b.withValueBackReferences(cv);
}
ops.add(b.build());
}
if (mHostAuthSend != null) {
if (mHostAuthSend.mCredential != null) {
if (mHostAuthRecv.mCredential != null &&
mHostAuthRecv.mCredential.equals(mHostAuthSend.mCredential)) {
// These two credentials are identical, use the same row.
sendCredentialsIndex = recvCredentialsIndex;
} else {
sendCredentialsIndex = index++;
ops.add(ContentProviderOperation.newInsert(mHostAuthSend.mCredential.mBaseUri)
.withValues(mHostAuthSend.mCredential.toContentValues())
.build());
}
}
sendIndex = index++;
final ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(
mHostAuthSend.mBaseUri);
b.withValues(mHostAuthSend.toContentValues());
if (sendCredentialsIndex >= 0) {
final ContentValues cv = new ContentValues();
cv.put(HostAuthColumns.CREDENTIAL_KEY, sendCredentialsIndex);
b.withValueBackReferences(cv);
}
ops.add(b.build());
}
// Now do the Account
ContentValues cv = null;
if (recvIndex >= 0 || sendIndex >= 0) {
cv = new ContentValues();
if (recvIndex >= 0) {
cv.put(AccountColumns.HOST_AUTH_KEY_RECV, recvIndex);
}
if (sendIndex >= 0) {
cv.put(AccountColumns.HOST_AUTH_KEY_SEND, sendIndex);
}
}
ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(mBaseUri);
b.withValues(toContentValues());
if (cv != null) {
b.withValueBackReferences(cv);
}
ops.add(b.build());
try {
ContentProviderResult[] results =
context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops);
// If saving, set the mId's of the various saved objects
if (recvIndex >= 0) {
long newId = getId(results[recvIndex].uri);
mHostAuthKeyRecv = newId;
mHostAuthRecv.mId = newId;
}
if (sendIndex >= 0) {
long newId = getId(results[sendIndex].uri);
mHostAuthKeySend = newId;
mHostAuthSend.mId = newId;
}
Uri u = results[index].uri;
mId = getId(u);
return u;
} catch (RemoteException e) {
// There is nothing to be done here; fail by returning null
} catch (OperationApplicationException e) {
// There is nothing to be done here; fail by returning null
}
return null;
}
@Override
public ContentValues toContentValues() {
ContentValues values = new ContentValues();
values.put(AccountColumns.DISPLAY_NAME, mDisplayName);
values.put(AccountColumns.EMAIL_ADDRESS, mEmailAddress);
values.put(AccountColumns.SYNC_KEY, mSyncKey);
values.put(AccountColumns.SYNC_LOOKBACK, mSyncLookback);
values.put(AccountColumns.SYNC_INTERVAL, mSyncInterval);
values.put(AccountColumns.HOST_AUTH_KEY_RECV, mHostAuthKeyRecv);
values.put(AccountColumns.HOST_AUTH_KEY_SEND, mHostAuthKeySend);
values.put(AccountColumns.FLAGS, mFlags);
values.put(AccountColumns.SENDER_NAME, mSenderName);
values.put(AccountColumns.RINGTONE_URI, mRingtoneUri);
values.put(AccountColumns.PROTOCOL_VERSION, mProtocolVersion);
values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey);
values.put(AccountColumns.SIGNATURE, mSignature);
values.put(AccountColumns.POLICY_KEY, mPolicyKey);
values.put(AccountColumns.PING_DURATION, mPingDuration);
values.put(AccountColumns.CAPABILITIES, mCapabilities);
return values;
}
public String toJsonString(final Context context) {
ensureLoaded(context);
final JSONObject json = toJson();
if (json != null) {
return json.toString();
}
return null;
}
protected JSONObject toJson() {
try {
final JSONObject json = new JSONObject();
json.putOpt(AccountColumns.DISPLAY_NAME, mDisplayName);
json.put(AccountColumns.EMAIL_ADDRESS, mEmailAddress);
json.put(AccountColumns.SYNC_LOOKBACK, mSyncLookback);
json.put(AccountColumns.SYNC_INTERVAL, mSyncInterval);
final JSONObject recvJson = mHostAuthRecv.toJson();
json.put(JSON_TAG_HOST_AUTH_RECV, recvJson);
if (mHostAuthSend != null) {
final JSONObject sendJson = mHostAuthSend.toJson();
json.put(JSON_TAG_HOST_AUTH_SEND, sendJson);
}
json.put(AccountColumns.FLAGS, mFlags);
json.putOpt(AccountColumns.SENDER_NAME, mSenderName);
json.putOpt(AccountColumns.PROTOCOL_VERSION, mProtocolVersion);
json.putOpt(AccountColumns.SIGNATURE, mSignature);
json.put(AccountColumns.PING_DURATION, mPingDuration);
json.put(AccountColumns.CAPABILITIES, mCapabilities);
return json;
} catch (final JSONException e) {
LogUtils.d(LogUtils.TAG, e, "Exception while serializing Account");
}
return null;
}
public static Account fromJsonString(final String jsonString) {
try {
final JSONObject json = new JSONObject(jsonString);
return fromJson(json);
} catch (final JSONException e) {
LogUtils.d(LogUtils.TAG, e, "Could not parse json for account");
}
return null;
}
protected static Account fromJson(final JSONObject json) {
try {
final Account a = new Account();
a.mDisplayName = json.optString(AccountColumns.DISPLAY_NAME);
a.mEmailAddress = json.getString(AccountColumns.EMAIL_ADDRESS);
// SYNC_KEY is not stored
a.mSyncLookback = json.getInt(AccountColumns.SYNC_LOOKBACK);
a.mSyncInterval = json.getInt(AccountColumns.SYNC_INTERVAL);
final JSONObject recvJson = json.getJSONObject(JSON_TAG_HOST_AUTH_RECV);
a.mHostAuthRecv = HostAuth.fromJson(recvJson);
final JSONObject sendJson = json.optJSONObject(JSON_TAG_HOST_AUTH_SEND);
if (sendJson != null) {
a.mHostAuthSend = HostAuth.fromJson(sendJson);
}
a.mFlags = json.getInt(AccountColumns.FLAGS);
a.mSenderName = json.optString(AccountColumns.SENDER_NAME);
a.mProtocolVersion = json.optString(AccountColumns.PROTOCOL_VERSION);
// SECURITY_SYNC_KEY is not stored
a.mSignature = json.optString(AccountColumns.SIGNATURE);
// POLICY_KEY is not stored
a.mPingDuration = json.optInt(AccountColumns.PING_DURATION, 0);
a.mCapabilities = json.optInt(AccountColumns.CAPABILITIES, 0);
return a;
} catch (final JSONException e) {
LogUtils.d(LogUtils.TAG, e, "Exception while deserializing Account");
}
return null;
}
/**
* Ensure that all optionally-loaded fields are populated from the provider.
* @param context for provider loads
*/
public void ensureLoaded(final Context context) {
if (mHostAuthKeyRecv == 0 && mHostAuthRecv == null) {
throw new IllegalStateException("Trying to load incomplete Account object");
}
getOrCreateHostAuthRecv(context).ensureLoaded(context);
if (mHostAuthKeySend != 0) {
getOrCreateHostAuthSend(context);
if (mHostAuthSend != null) {
mHostAuthSend.ensureLoaded(context);
}
}
}
/**
* Returns whether or not the capability is supported by the account.
* @see EmailServiceProxy#CAPABILITY_*
*/
public boolean hasCapability(int capability) {
return (mCapabilities & capability) != 0;
}
/**
* Supports Parcelable
*/
@Override
public int describeContents() {
return 0;
}
/**
* Supports Parcelable
*/
public static final Parcelable.Creator<Account> CREATOR
= new Parcelable.Creator<Account>() {
@Override
public Account createFromParcel(Parcel in) {
return new Account(in);
}
@Override
public Account[] newArray(int size) {
return new Account[size];
}
};
/**
* Supports Parcelable
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
// mBaseUri is not parceled
dest.writeLong(mId);
dest.writeString(mDisplayName);
dest.writeString(mEmailAddress);
dest.writeString(mSyncKey);
dest.writeInt(mSyncLookback);
dest.writeInt(mSyncInterval);
dest.writeLong(mHostAuthKeyRecv);
dest.writeLong(mHostAuthKeySend);
dest.writeInt(mFlags);
dest.writeString("" /* mCompatibilityUuid */);
dest.writeString(mSenderName);
dest.writeString(mRingtoneUri);
dest.writeString(mProtocolVersion);
dest.writeInt(0 /* mNewMessageCount */);
dest.writeString(mSecuritySyncKey);
dest.writeString(mSignature);
dest.writeLong(mPolicyKey);
if (mHostAuthRecv != null) {
dest.writeByte((byte)1);
mHostAuthRecv.writeToParcel(dest, flags);
} else {
dest.writeByte((byte)0);
}
if (mHostAuthSend != null) {
dest.writeByte((byte)1);
mHostAuthSend.writeToParcel(dest, flags);
} else {
dest.writeByte((byte)0);
}
dest.writeInt(mCapabilities);
}
/**
* Supports Parcelable
*/
public Account(Parcel in) {
mBaseUri = Account.CONTENT_URI;
mId = in.readLong();
mDisplayName = in.readString();
mEmailAddress = in.readString();
mSyncKey = in.readString();
mSyncLookback = in.readInt();
mSyncInterval = in.readInt();
mHostAuthKeyRecv = in.readLong();
mHostAuthKeySend = in.readLong();
mFlags = in.readInt();
/* mCompatibilityUuid = */ in.readString();
mSenderName = in.readString();
mRingtoneUri = in.readString();
mProtocolVersion = in.readString();
/* mNewMessageCount = */ in.readInt();
mSecuritySyncKey = in.readString();
mSignature = in.readString();
mPolicyKey = in.readLong();
mHostAuthRecv = null;
if (in.readByte() == 1) {
mHostAuthRecv = new HostAuth(in);
}
mHostAuthSend = null;
if (in.readByte() == 1) {
mHostAuthSend = new HostAuth(in);
}
mCapabilities = in.readInt();
}
/**
* For debugger support only - DO NOT use for code.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
if (mHostAuthRecv != null && mHostAuthRecv.mProtocol != null) {
sb.append(mHostAuthRecv.mProtocol);
sb.append(':');
}
if (mDisplayName != null) sb.append(mDisplayName);
sb.append(':');
if (mEmailAddress != null) sb.append(mEmailAddress);
sb.append(':');
if (mSenderName != null) sb.append(mSenderName);
sb.append(']');
return sb.toString();
}
}