Example usage for android.content ContentValues putAll

List of usage examples for android.content ContentValues putAll

Introduction

In this page you can find the example usage for android.content ContentValues putAll.

Prototype

public void putAll(ContentValues other) 

Source Link

Document

Adds all values from the passed in ContentValues.

Usage

From source file:edu.mit.mobile.android.locast.data.JsonSyncableItem.java

/**
 * Given a JSON item and a sync map, create a ContentValues map to be inserted into the DB.
 *
 * @param context//from  w  ww.  j  a  va  2 s.c  o  m
 * @param localItem will be null if item is new to mobile. If it's been sync'd before, will point to local entry.
 * @param item incoming JSON item.
 * @param mySyncMap A mapping between the JSON object and the content values.
 * @return new ContentValues, ready to be inserted into the database.
 * @throws JSONException
 * @throws IOException
 * @throws NetworkProtocolException
 */
public final static ContentValues fromJSON(Context context, Uri localItem, JSONObject item, SyncMap mySyncMap)
        throws JSONException, IOException, NetworkProtocolException {
    final ContentValues cv = new ContentValues();

    for (final String propName : mySyncMap.keySet()) {
        final SyncItem map = mySyncMap.get(propName);
        if (!map.isDirection(SyncItem.SYNC_FROM)) {
            continue;
        }
        if (map.isOptional() && (!item.has(map.remoteKey) || item.isNull(map.remoteKey))) {
            continue;
        }
        final ContentValues cv2 = map.fromJSON(context, localItem, item, propName);
        if (cv2 != null) {
            cv.putAll(cv2);
        }

    }
    return cv;
}

From source file:com.amazonaws.mobileconnectors.s3.transferutility.TransferDBUtil.java

/**
 * Generates a ContentValues object to insert into the database with the
 * given values for a single chunk upload or download.
 *
 * @param type The type of the transfer, can be "upload" or "download".
 * @param bucket The name of the bucket to upload to.
 * @param key The key in the specified bucket by which to store the new
 *            object.//w w w  .java 2s. c  o m
 * @param file The file to upload.
 * @param metadata The S3 ObjectMetadata to send along with the object
 * @param cannedAcl The canned ACL associated with the object
 * @return The ContentValues object generated.
 */
private ContentValues generateContentValuesForSinglePartTransfer(TransferType type, String bucket, String key,
        File file, ObjectMetadata metadata, CannedAccessControlList cannedAcl) {
    final ContentValues values = new ContentValues();
    values.put(TransferTable.COLUMN_TYPE, type.toString());
    values.put(TransferTable.COLUMN_STATE, TransferState.WAITING.toString());
    values.put(TransferTable.COLUMN_BUCKET_NAME, bucket);
    values.put(TransferTable.COLUMN_KEY, key);
    values.put(TransferTable.COLUMN_FILE, file.getAbsolutePath());
    values.put(TransferTable.COLUMN_BYTES_CURRENT, 0L);
    if (type.equals(TransferType.UPLOAD)) {
        values.put(TransferTable.COLUMN_BYTES_TOTAL, file == null ? 0L : file.length());
    }
    values.put(TransferTable.COLUMN_IS_MULTIPART, 0);
    values.put(TransferTable.COLUMN_PART_NUM, 0);
    values.put(TransferTable.COLUMN_IS_ENCRYPTED, 0);
    values.putAll(generateContentValuesForObjectMetadata(metadata));
    if (cannedAcl != null) {
        values.put(TransferTable.COLUMN_CANNED_ACL, cannedAcl.toString());
    }
    return values;
}

From source file:com.amazonaws.mobileconnectors.s3.transferutility.TransferDBUtil.java

/**
 * Generates a ContentValues object to insert into the database with the
 * given values for a multipart upload record.
 *
 * @param bucket The name of the bucket to upload to.
 * @param key The key in the specified bucket by which to store the new
 *            object.//from  w w  w .  j  a va2  s. com
 * @param file The file to upload.
 * @param fileOffset The byte offset for the file to upload.
 * @param partNumber The part number of this part.
 * @param uploadId The multipart upload id of the upload.
 * @param bytesTotal The Total bytes of the file.
 * @param isLastPart Whether this part is the last part of the upload.
 * @param metadata The S3 ObjectMetadata to send along with the object
 * @param cannedAcl The canned ACL associated with the object
 * @return The ContentValues object generated.
 */
public ContentValues generateContentValuesForMultiPartUpload(String bucket, String key, File file,
        long fileOffset, int partNumber, String uploadId, long bytesTotal, int isLastPart,
        ObjectMetadata metadata, CannedAccessControlList cannedAcl) {
    final ContentValues values = new ContentValues();
    values.put(TransferTable.COLUMN_TYPE, TransferType.UPLOAD.toString());
    values.put(TransferTable.COLUMN_STATE, TransferState.WAITING.toString());
    values.put(TransferTable.COLUMN_BUCKET_NAME, bucket);
    values.put(TransferTable.COLUMN_KEY, key);
    values.put(TransferTable.COLUMN_FILE, file.getAbsolutePath());
    values.put(TransferTable.COLUMN_BYTES_CURRENT, 0L);
    values.put(TransferTable.COLUMN_BYTES_TOTAL, bytesTotal);
    values.put(TransferTable.COLUMN_IS_MULTIPART, 1);
    values.put(TransferTable.COLUMN_PART_NUM, partNumber);
    values.put(TransferTable.COLUMN_FILE_OFFSET, fileOffset);
    values.put(TransferTable.COLUMN_MULTIPART_ID, uploadId);
    values.put(TransferTable.COLUMN_IS_LAST_PART, isLastPart);
    values.put(TransferTable.COLUMN_IS_ENCRYPTED, 0);
    values.putAll(generateContentValuesForObjectMetadata(metadata));
    if (cannedAcl != null) {
        values.put(TransferTable.COLUMN_CANNED_ACL, cannedAcl.toString());
    }
    return values;
}

From source file:org.opendatakit.common.android.utilities.ODKDatabaseUtils.java

/**
 * Insert the given rowId with the values in the cvValues. If certain metadata
 * values are not specified in the cvValues, then suitable default values may
 * be supplied for them.// w  w w  . j ava  2  s  .  c  om
 * 
 * If a row with this rowId and certain matching metadata fields is present,
 * then an exception is thrown.
 * 
 * @param db
 * @param tableId
 * @param orderedColumns
 * @param cvValues
 * @param uuid
 */
public void insertDataIntoExistingDBTableWithId(SQLiteDatabase db, String tableId,
        ArrayList<ColumnDefinition> orderedColumns, ContentValues cvValues, String uuid) {

    if (cvValues.size() <= 0) {
        throw new IllegalArgumentException(t + ": No values to add into table " + tableId);
    }

    ContentValues cvDataTableVal = new ContentValues();
    cvDataTableVal.put(DataTableColumns.ID, uuid);
    cvDataTableVal.putAll(cvValues);

    upsertDataIntoExistingDBTable(db, tableId, orderedColumns, cvDataTableVal, false);
}

From source file:org.opendatakit.common.android.utilities.ODKDatabaseUtils.java

/**
 * Update the given rowId with the values in the cvValues. If certain metadata
 * values are not specified in the cvValues, then suitable default values may
 * be supplied for them. Furthermore, if the cvValues do not specify certain
 * metadata fields, then an exception may be thrown if there are more than one
 * row matching this rowId.//  w  ww .  j a v a2  s  .  c  om
 * 
 * @param db
 * @param tableId
 * @param orderedColumns
 * @param cvValues
 * @param rowId
 */
public void updateDataInExistingDBTableWithId(SQLiteDatabase db, String tableId,
        ArrayList<ColumnDefinition> orderedColumns, ContentValues cvValues, String rowId) {

    if (cvValues.size() <= 0) {
        throw new IllegalArgumentException(t + ": No values to add into table " + tableId);
    }

    ContentValues cvDataTableVal = new ContentValues();
    cvDataTableVal.put(DataTableColumns.ID, rowId);
    cvDataTableVal.putAll(cvValues);

    upsertDataIntoExistingDBTable(db, tableId, orderedColumns, cvDataTableVal, true);
}

From source file:org.opendatakit.common.android.utilities.ODKDatabaseUtils.java

private void upsertDataIntoExistingDBTable(SQLiteDatabase db, String tableId,
        ArrayList<ColumnDefinition> orderedColumns, ContentValues cvValues, boolean shouldUpdate) {
    String rowId = null;//from w  w  w.j av  a 2 s  .  co m
    String whereClause = null;
    boolean specifiesConflictType = cvValues.containsKey(DataTableColumns.CONFLICT_TYPE);
    boolean nullConflictType = specifiesConflictType && (cvValues.get(DataTableColumns.CONFLICT_TYPE) == null);
    String[] whereArgs = new String[specifiesConflictType ? (1 + (nullConflictType ? 0 : 1)) : 1];
    boolean update = false;

    if (cvValues.size() <= 0) {
        throw new IllegalArgumentException(t + ": No values to add into table " + tableId);
    }

    ContentValues cvDataTableVal = new ContentValues();
    cvDataTableVal.putAll(cvValues);

    if (cvDataTableVal.containsKey(DataTableColumns.ID)) {
        // The user specified a row id; we need to determine whether to
        // insert or update the record, or to reject the action because
        // there are either checkpoint records for this row id, or, if
        // a server conflict is associated with this row, that the
        // _conflict_type to update was not specified.
        //
        // i.e., the tuple (_id, _conflict_type) should be unique. If
        // we find that there are more than 0 or 1 records matching this
        // tuple, then we should reject the update request.
        //
        // TODO: perhaps we want to allow updates to the local conflict
        // row if there are no checkpoints on it? I.e., change the
        // tri-state conflict type to a pair of states (local / remote).
        // and all local changes are flagged local. Remote only exists
        // if the server is in conflict.

        rowId = cvDataTableVal.getAsString(DataTableColumns.ID);
        if (rowId == null) {
            throw new IllegalArgumentException(DataTableColumns.ID + ", if specified, cannot be null");
        }

        if (specifiesConflictType) {
            if (nullConflictType) {
                whereClause = DataTableColumns.ID + " = ?" + " AND " + DataTableColumns.CONFLICT_TYPE
                        + " IS NULL";
                whereArgs[0] = rowId;
            } else {
                whereClause = DataTableColumns.ID + " = ?" + " AND " + DataTableColumns.CONFLICT_TYPE + " = ?";
                whereArgs[0] = rowId;
                whereArgs[1] = cvValues.getAsString(DataTableColumns.CONFLICT_TYPE);
            }
        } else {
            whereClause = DataTableColumns.ID + " = ?";
            whereArgs[0] = rowId;
        }

        String sel = "SELECT * FROM " + tableId + " WHERE " + whereClause;
        String[] selArgs = whereArgs;
        Cursor cursor = rawQuery(db, sel, selArgs);

        // There must be only one row in the db for the update to work
        if (shouldUpdate) {
            if (cursor.getCount() == 1) {
                update = true;
            } else if (cursor.getCount() > 1) {
                throw new IllegalArgumentException(
                        t + ": row id " + rowId + " has more than 1 row in table " + tableId);
            }
        } else {
            if (cursor.getCount() > 0) {
                throw new IllegalArgumentException(
                        t + ": id " + rowId + " is already present in table " + tableId);
            }
        }

    } else {
        rowId = "uuid:" + UUID.randomUUID().toString();
    }

    // TODO: This is broken w.r.t. updates of partial fields
    // TODO: This is broken w.r.t. updates of partial fields
    // TODO: This is broken w.r.t. updates of partial fields
    // TODO: This is broken w.r.t. updates of partial fields

    if (!cvDataTableVal.containsKey(DataTableColumns.ID)) {
        cvDataTableVal.put(DataTableColumns.ID, rowId);
    }

    if (update) {
        if (!cvDataTableVal.containsKey(DataTableColumns.SYNC_STATE)
                || (cvDataTableVal.get(DataTableColumns.SYNC_STATE) == null)) {
            cvDataTableVal.put(DataTableColumns.SYNC_STATE, SyncState.changed.name());
        }

        if (cvDataTableVal.containsKey(DataTableColumns.LOCALE)
                && (cvDataTableVal.get(DataTableColumns.LOCALE) == null)) {
            cvDataTableVal.put(DataTableColumns.LOCALE, DataTableColumns.DEFAULT_LOCALE);
        }

        if (cvDataTableVal.containsKey(DataTableColumns.SAVEPOINT_TYPE)
                && (cvDataTableVal.get(DataTableColumns.SAVEPOINT_TYPE) == null)) {
            cvDataTableVal.put(DataTableColumns.SAVEPOINT_TYPE, SavepointTypeManipulator.complete());
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.SAVEPOINT_TIMESTAMP)
                || cvDataTableVal.get(DataTableColumns.SAVEPOINT_TIMESTAMP) == null) {
            String timeStamp = TableConstants.nanoSecondsFromMillis(System.currentTimeMillis());
            cvDataTableVal.put(DataTableColumns.SAVEPOINT_TIMESTAMP, timeStamp);
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.SAVEPOINT_CREATOR)
                || (cvDataTableVal.get(DataTableColumns.SAVEPOINT_CREATOR) == null)) {
            cvDataTableVal.put(DataTableColumns.SAVEPOINT_CREATOR, DataTableColumns.DEFAULT_SAVEPOINT_CREATOR);
        }
    } else {

        if (!cvDataTableVal.containsKey(DataTableColumns.ROW_ETAG)
                || cvDataTableVal.get(DataTableColumns.ROW_ETAG) == null) {
            cvDataTableVal.put(DataTableColumns.ROW_ETAG, DataTableColumns.DEFAULT_ROW_ETAG);
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.SYNC_STATE)
                || (cvDataTableVal.get(DataTableColumns.SYNC_STATE) == null)) {
            cvDataTableVal.put(DataTableColumns.SYNC_STATE, SyncState.new_row.name());
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.CONFLICT_TYPE)) {
            cvDataTableVal.putNull(DataTableColumns.CONFLICT_TYPE);
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.FILTER_TYPE)
                || (cvDataTableVal.get(DataTableColumns.FILTER_TYPE) == null)) {
            cvDataTableVal.put(DataTableColumns.FILTER_TYPE, DataTableColumns.DEFAULT_FILTER_TYPE);
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.FILTER_VALUE)
                || (cvDataTableVal.get(DataTableColumns.FILTER_VALUE) == null)) {
            cvDataTableVal.put(DataTableColumns.FILTER_VALUE, DataTableColumns.DEFAULT_FILTER_VALUE);
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.FORM_ID)) {
            cvDataTableVal.putNull(DataTableColumns.FORM_ID);
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.LOCALE)
                || (cvDataTableVal.get(DataTableColumns.LOCALE) == null)) {
            cvDataTableVal.put(DataTableColumns.LOCALE, DataTableColumns.DEFAULT_LOCALE);
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.SAVEPOINT_TYPE)
                || (cvDataTableVal.get(DataTableColumns.SAVEPOINT_TYPE) == null)) {
            cvDataTableVal.put(DataTableColumns.SAVEPOINT_TYPE, SavepointTypeManipulator.complete());
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.SAVEPOINT_TIMESTAMP)
                || cvDataTableVal.get(DataTableColumns.SAVEPOINT_TIMESTAMP) == null) {
            String timeStamp = TableConstants.nanoSecondsFromMillis(System.currentTimeMillis());
            cvDataTableVal.put(DataTableColumns.SAVEPOINT_TIMESTAMP, timeStamp);
        }

        if (!cvDataTableVal.containsKey(DataTableColumns.SAVEPOINT_CREATOR)
                || (cvDataTableVal.get(DataTableColumns.SAVEPOINT_CREATOR) == null)) {
            cvDataTableVal.put(DataTableColumns.SAVEPOINT_CREATOR, DataTableColumns.DEFAULT_SAVEPOINT_CREATOR);
        }
    }

    cleanUpValuesMap(orderedColumns, cvDataTableVal);

    boolean dbWithinTransaction = db.inTransaction();
    try {
        if (!dbWithinTransaction) {
            db.beginTransaction();
        }

        if (update) {
            db.update(tableId, cvDataTableVal, whereClause, whereArgs);
        } else {
            db.insertOrThrow(tableId, null, cvDataTableVal);
        }

        if (!dbWithinTransaction) {
            db.setTransactionSuccessful();
        }
    } finally {
        if (!dbWithinTransaction) {
            db.endTransaction();
        }
    }

}

From source file:com.android.mail.compose.ComposeActivity.java

private void sendOrSaveInternal(Context context, int requestId, ReplyFromAccount currReplyFromAccount,
        ReplyFromAccount originalReplyFromAccount, Message message, Message refMessage, CharSequence quotedText,
        SendOrSaveCallback callback, boolean save, int composeMode, ContentValues extraValues,
        Bundle optionalAttachmentFds) {/*from   w w w.  ja  va2 s.  c  o  m*/
    final ContentValues values = new ContentValues();

    final String refMessageId = refMessage != null ? refMessage.uri.toString() : "";

    MessageModification.putToAddresses(values, message.getToAddresses());
    MessageModification.putCcAddresses(values, message.getCcAddresses());
    MessageModification.putBccAddresses(values, message.getBccAddresses());
    MessageModification.putCustomFromAddress(values, message.getFrom());

    MessageModification.putSubject(values, message.subject);

    // bodyHtml already have the composing spans removed.
    final String htmlBody = message.bodyHtml;
    final String textBody = message.bodyText;
    // fullbodyhtml/fullbodytext will contain the actual body plus the quoted text.
    String fullBodyHtml = htmlBody;
    String fullBodyText = textBody;
    String quotedString = null;
    final boolean hasQuotedText = !TextUtils.isEmpty(quotedText);
    if (hasQuotedText) {
        // The quoted text is HTML at this point.
        quotedString = quotedText.toString();
        fullBodyHtml = htmlBody + quotedString;
        fullBodyText = textBody + Utils.convertHtmlToPlainText(quotedString);
        MessageModification.putForward(values, composeMode == ComposeActivity.FORWARD);
        MessageModification.putAppendRefMessageContent(values, true /* include quoted */);
    }

    // Only take refMessage into account if either one of its html/text is not empty.
    int quotedTextPos = -1;
    if (refMessage != null
            && !(TextUtils.isEmpty(refMessage.bodyHtml) && TextUtils.isEmpty(refMessage.bodyText))) {
        // The code below might need to be revisited. The quoted text position is different
        // between text/html and text/plain parts and they should be stored seperately and
        // the right version should be used in the UI. text/html should have preference
        // if both exist.  Issues like this made me file b/14256940 to make sure that we
        // properly handle the existing of both text/html and text/plain parts and to verify
        // that we are not making some assumptions that break if there is no text/html part.
        if (!TextUtils.isEmpty(refMessage.bodyHtml)) {
            MessageModification.putBodyHtml(values, fullBodyHtml);
            if (hasQuotedText) {
                quotedTextPos = htmlBody.length() + QuotedTextView.getQuotedTextOffset(quotedString);
            }
        }
        if (!TextUtils.isEmpty(refMessage.bodyText)) {
            MessageModification.putBody(values, fullBodyText);
            if (hasQuotedText && (quotedTextPos == -1)) {
                quotedTextPos = textBody.length();
            }
        }
        if (quotedTextPos != -1) {
            // The quoted text pos is the text/html version first and the text/plan version
            // if there is no text/html part. The reason for this is because preference
            // is given to text/html in the compose window if it exists. In the future, we
            // should calculate the index for both since the user could choose to compose
            // explicitly in text/plain.
            MessageModification.putQuoteStartPos(values, quotedTextPos);
        }
    } else {
        MessageModification.putBodyHtml(values, fullBodyHtml);
        MessageModification.putBody(values, fullBodyText);
    }
    int draftType = getDraftType(composeMode);
    MessageModification.putDraftType(values, draftType);
    MessageModification.putAttachments(values, message.getAttachments());
    if (!TextUtils.isEmpty(refMessageId)) {
        MessageModification.putRefMessageId(values, refMessageId);
    }
    if (extraValues != null) {
        values.putAll(extraValues);
    }

    SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(context, requestId, values, refMessageId,
            message.getAttachments(), optionalAttachmentFds, save);
    runSendOrSaveProviderCalls(sendOrSaveMessage, callback, currReplyFromAccount, originalReplyFromAccount);

    LogUtils.i(LOG_TAG,
            "[compose] SendOrSaveMessage [%s] posted (isSave: %s) - "
                    + "bodyHtml length: %d, bodyText length: %d, quoted text pos: %d, attach count: %d",
            requestId, save, message.bodyHtml.length(), message.bodyText.length(), quotedTextPos,
            message.getAttachmentCount(true));
}

From source file:com.tct.mail.compose.ComposeActivity.java

private int sendOrSaveInternal(Context context, ReplyFromAccount replyFromAccount, Message message,
        final Message refMessage, final CharSequence quotedText, SendOrSaveCallback callback, Handler handler,
        boolean save, int composeMode, ReplyFromAccount draftAccount, final ContentValues extraValues) {
    final ContentValues values = new ContentValues();

    final String refMessageId = refMessage != null ? refMessage.uri.toString() : "";

    MessageModification.putToAddresses(values, message.getToAddresses());
    MessageModification.putCcAddresses(values, message.getCcAddresses());
    MessageModification.putBccAddresses(values, message.getBccAddresses());
    MessageModification.putCustomFromAddress(values, message.getFrom());
    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
    MessageModification.putPriority(values, message.mPriority);
    //[FEATURE]-Add-END by TSCD.chao zhang
    MessageModification.putSubject(values, message.subject);
    // TS: tao.gan 2015-12-25 EMAIL FEATURE-1239148 ADD_S
    MessageModification.putRepylToAddress(values, message.getReplyTo());
    // TS: tao.gan 2015-12-25 EMAIL FEATURE-1239148 ADD_E

    // bodyHtml already have the composing spans removed.
    final String htmlBody = message.bodyHtml;
    final String textBody = message.bodyText;
    // fullbody will contain the actual body plus the quoted text.
    final String fullBody;
    final String quotedString;
    final boolean hasQuotedText = !TextUtils.isEmpty(quotedText);
    if (hasQuotedText) {
        // The quoted text is HTML at this point.
        quotedString = quotedText.toString();
        fullBody = htmlBody + quotedString;
        MessageModification.putForward(values, composeMode == ComposeActivity.FORWARD);
        MessageModification.putAppendRefMessageContent(values, true /* include quoted */);
    } else {//from   w  w  w.  j  ava 2 s  .  c o m
        fullBody = htmlBody;
        quotedString = null;
    }
    // Only take refMessage into account if either one of its html/text is not empty.
    if (refMessage != null
            && !(TextUtils.isEmpty(refMessage.bodyHtml) && TextUtils.isEmpty(refMessage.bodyText))) {
        // The code below might need to be revisited. The quoted text position is different
        // between text/html and text/plain parts and they should be stored seperately and
        // the right version should be used in the UI. text/html should have preference
        // if both exist.  Issues like this made me file b/14256940 to make sure that we
        // properly handle the existing of both text/html and text/plain parts and to verify
        // that we are not making some assumptions that break if there is no text/html part.
        int quotedTextPos = -1;
        //[FEATURE]-Add-BEGIN by TSNJ,Zhenhua.Fan,06/11/2014,FR-622609 1471
        if (EmailApplication.isOrangeImapFeatureOn() && message.serverId != null) {
            values.put(UIProvider.MessageColumns.SERVER_ID, message.serverId);
        }
        //[FEATURE]-Add-END by TSNJ,Zhenhua.Fan
        if (!TextUtils.isEmpty(refMessage.bodyHtml)) {
            MessageModification.putBodyHtml(values, fullBody.toString());
            if (hasQuotedText) {
                quotedTextPos = htmlBody.length() + QuotedTextView.getQuotedTextOffset(quotedString);
            }
        }
        if (!TextUtils.isEmpty(refMessage.bodyText)) {
            MessageModification.putBody(values, Utils.convertHtmlToPlainText(fullBody.toString()));
            if (hasQuotedText && (quotedTextPos == -1)) {
                quotedTextPos = textBody.length();
            }
        }
        if (quotedTextPos != -1) {
            // The quoted text pos is the text/html version first and the text/plan version
            // if there is no text/html part. The reason for this is because preference
            // is given to text/html in the compose window if it exists. In the future, we
            // should calculate the index for both since the user could choose to compose
            // explicitly in text/plain.
            MessageModification.putQuoteStartPos(values, quotedTextPos);
        }
    } else {
        MessageModification.putBodyHtml(values, fullBody.toString());
        MessageModification.putBody(values, Utils.convertHtmlToPlainText(fullBody.toString()));
    }
    int draftType = getDraftType(composeMode);
    MessageModification.putDraftType(values, draftType);
    MessageModification.putAttachments(values, message.getAttachments());
    if (!TextUtils.isEmpty(refMessageId)) {
        MessageModification.putRefMessageId(values, refMessageId);
    }
    if (extraValues != null) {
        values.putAll(extraValues);
    }
    SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(context, replyFromAccount, values, refMessageId,
            message.getAttachments(), save);
    SendOrSaveTask sendOrSaveTask = new SendOrSaveTask(context, sendOrSaveMessage, callback, draftAccount);

    callback.initializeSendOrSave(sendOrSaveTask);
    // Do the send/save action on the specified handler to avoid possible
    // ANRs
    handler.post(sendOrSaveTask);

    return sendOrSaveMessage.requestId();
}