Example usage for android.util SparseArray SparseArray

List of usage examples for android.util SparseArray SparseArray

Introduction

In this page you can find the example usage for android.util SparseArray SparseArray.

Prototype

public SparseArray(int initialCapacity) 

Source Link

Document

Creates a new SparseArray containing no mappings that will not require any additional memory allocation to store the specified number of mappings.

Usage

From source file:com.tct.mail.ui.AnimatedAdapter.java

public AnimatedAdapter(Context context, ConversationCursor cursor, ConversationSelectionSet batch,
        ControllableActivity activity, SwipeableListView listView,
        final List<ConversationSpecialItemView> specialViews) {
    super(context, -1, cursor, UIProvider.CONVERSATION_PROJECTION, null, 0);
    mContext = context;//from ww  w.  j  ava2s .  co m
    mBatchConversations = batch;
    setAccount(mAccountListener.initialize(activity.getAccountController()));
    mActivity = activity;
    mShowFooter = false;
    mListView = listView;
    /// M: add search params @{
    ActivityController controller = (ActivityController) mActivity.getAccountController();
    mListContext = controller.getCurrentListContext();
    /// @}

    mSendersImagesCache = mActivity.getSenderImageCache();

    mContactResolver = mActivity.getContactResolver(mContext.getContentResolver(), mSendersImagesCache);

    mHandler = new Handler();
    if (sDismissAllShortDelay == -1) {
        final Resources r = context.getResources();
        sDismissAllShortDelay = r.getInteger(R.integer.dismiss_all_leavebehinds_short_delay);
        sDismissAllLongDelay = r.getInteger(R.integer.dismiss_all_leavebehinds_long_delay);
    }
    if (specialViews != null) {
        mFleetingViews = new ArrayList<ConversationSpecialItemView>(specialViews);
    } else {
        mFleetingViews = new ArrayList<ConversationSpecialItemView>(0);
    }
    /** Total number of special views */
    final int size = mFleetingViews.size();
    mSpecialViews = new SparseArray<ConversationSpecialItemView>(size);
    mColumnViews = new SparseArray<ConversationTimeColumnView>(size); //TS: zheng.zou 2015-09-01 EMAIL BUGFIX_539892 ADD

    // Set the adapter in teaser views.
    for (final ConversationSpecialItemView view : mFleetingViews) {
        view.setAdapter(this);
    }
    updateSpecialViews();
}

From source file:android.support.transition.Transition.java

/**
 * This method, essentially a wrapper around all calls to createAnimator for all
 * possible target views, is called with the entire set of start/end
 * values. The implementation in Transition iterates through these lists
 * and calls {@link #createAnimator(android.view.ViewGroup, android.support.transition.TransitionValues, android.support.transition.TransitionValues)}
 * with each set of start/end values on this transition. The
 * TransitionSet subclass overrides this method and delegates it to
 * each of its children in succession.//w w  w  . j a va2 s . c  o m
 *
 * @hide
 */
protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
        TransitionValuesMaps endValues) {
    if (DBG) {
        Log.d(LOG_TAG, "createAnimators() for " + this);
    }
    ArrayMap<View, TransitionValues> endCopy = new ArrayMap<View, TransitionValues>(endValues.viewValues);
    SparseArray<TransitionValues> endIdCopy = new SparseArray<TransitionValues>(endValues.idValues.size());
    for (int i = 0; i < endValues.idValues.size(); ++i) {
        int id = endValues.idValues.keyAt(i);
        endIdCopy.put(id, endValues.idValues.valueAt(i));
    }
    LongSparseArray<TransitionValues> endItemIdCopy = new LongSparseArray<TransitionValues>(
            endValues.itemIdValues.size());
    for (int i = 0; i < endValues.itemIdValues.size(); ++i) {
        long id = endValues.itemIdValues.keyAt(i);
        endItemIdCopy.put(id, endValues.itemIdValues.valueAt(i));
    }
    // Walk through the start values, playing everything we find
    // Remove from the end set as we go
    ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>();
    ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>();
    for (View view : startValues.viewValues.keySet()) {
        TransitionValues start = null;
        TransitionValues end = null;
        boolean isInListView = false;
        if (view.getParent() instanceof ListView) {
            isInListView = true;
        }
        if (!isInListView) {
            int id = view.getId();
            start = startValues.viewValues.get(view) != null ? startValues.viewValues.get(view)
                    : startValues.idValues.get(id);
            if (endValues.viewValues.get(view) != null) {
                end = endValues.viewValues.get(view);
                endCopy.remove(view);
            } else if (id != View.NO_ID) {
                end = endValues.idValues.get(id);
                View removeView = null;
                for (View viewToRemove : endCopy.keySet()) {
                    if (viewToRemove.getId() == id) {
                        removeView = viewToRemove;
                    }
                }
                if (removeView != null) {
                    endCopy.remove(removeView);
                }
            }
            endIdCopy.remove(id);
            if (isValidTarget(view, id)) {
                startValuesList.add(start);
                endValuesList.add(end);
            }
        } else {
            ListView parent = (ListView) view.getParent();
            if (parent.getAdapter().hasStableIds()) {
                int position = parent.getPositionForView(view);
                long itemId = parent.getItemIdAtPosition(position);
                start = startValues.itemIdValues.get(itemId);
                endItemIdCopy.remove(itemId);
                // TODO: deal with targetIDs for itemIDs for ListView items
                startValuesList.add(start);
                endValuesList.add(end);
            }
        }
    }
    int startItemIdCopySize = startValues.itemIdValues.size();
    for (int i = 0; i < startItemIdCopySize; ++i) {
        long id = startValues.itemIdValues.keyAt(i);
        if (isValidTarget(null, id)) {
            TransitionValues start = startValues.itemIdValues.get(id);
            TransitionValues end = endValues.itemIdValues.get(id);
            endItemIdCopy.remove(id);
            startValuesList.add(start);
            endValuesList.add(end);
        }
    }
    // Now walk through the remains of the end set
    for (View view : endCopy.keySet()) {
        int id = view.getId();
        if (isValidTarget(view, id)) {
            TransitionValues start = startValues.viewValues.get(view) != null ? startValues.viewValues.get(view)
                    : startValues.idValues.get(id);
            TransitionValues end = endCopy.get(view);
            endIdCopy.remove(id);
            startValuesList.add(start);
            endValuesList.add(end);
        }
    }
    int endIdCopySize = endIdCopy.size();
    for (int i = 0; i < endIdCopySize; ++i) {
        int id = endIdCopy.keyAt(i);
        if (isValidTarget(null, id)) {
            TransitionValues start = startValues.idValues.get(id);
            TransitionValues end = endIdCopy.get(id);
            startValuesList.add(start);
            endValuesList.add(end);
        }
    }
    int endItemIdCopySize = endItemIdCopy.size();
    for (int i = 0; i < endItemIdCopySize; ++i) {
        long id = endItemIdCopy.keyAt(i);
        // TODO: Deal with targetIDs and itemIDs
        TransitionValues start = startValues.itemIdValues.get(id);
        TransitionValues end = endItemIdCopy.get(id);
        startValuesList.add(start);
        endValuesList.add(end);
    }
    ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
    for (int i = 0; i < startValuesList.size(); ++i) {
        TransitionValues start = startValuesList.get(i);
        TransitionValues end = endValuesList.get(i);
        // Only bother trying to animate with values that differ between start/end
        if (start != null || end != null) {
            if (start == null || !start.equals(end)) {
                if (DBG) {
                    View view = (end != null) ? end.view : start.view;
                    Log.d(LOG_TAG, "  differing start/end values for view " + view);
                    if (start == null || end == null) {
                        Log.d(LOG_TAG, "    "
                                + ((start == null) ? "start null, end non-null" : "start non-null, end null"));
                    } else {
                        for (String key : start.values.keySet()) {
                            Object startValue = start.values.get(key);
                            Object endValue = end.values.get(key);
                            if (startValue != endValue && !startValue.equals(endValue)) {
                                Log.d(LOG_TAG,
                                        "    " + key + ": start(" + startValue + "), end(" + endValue + ")");
                            }
                        }
                    }
                }
                // TODO: what to do about targetIds and itemIds?
                Animator animator = createAnimator(sceneRoot, start, end);
                if (animator != null) {
                    // Save animation info for future cancellation purposes
                    View view = null;
                    TransitionValues infoValues = null;
                    if (end != null) {
                        view = end.view;
                        String[] properties = getTransitionProperties();
                        if (view != null && properties != null && properties.length > 0) {
                            infoValues = new TransitionValues();
                            infoValues.view = view;
                            TransitionValues newValues = endValues.viewValues.get(view);
                            if (newValues != null) {
                                for (int j = 0; j < properties.length; ++j) {
                                    infoValues.values.put(properties[j], newValues.values.get(properties[j]));
                                }
                            }
                            int numExistingAnims = runningAnimators.size();
                            for (int j = 0; j < numExistingAnims; ++j) {
                                Animator anim = runningAnimators.keyAt(j);
                                AnimationInfo info = runningAnimators.get(anim);
                                if (info.values != null && info.view == view
                                        && ((info.name == null && getName() == null)
                                                || info.name.equals(getName()))) {
                                    if (info.values.equals(infoValues)) {
                                        // Favor the old animator
                                        animator = null;
                                        break;
                                    }
                                }
                            }
                        }
                    } else {
                        view = (start != null) ? start.view : null;
                    }
                    if (animator != null) {
                        AnimationInfo info = new AnimationInfo(view, getName(), infoValues);
                        runningAnimators.put(animator, info);
                        mAnimators.add(animator);
                    }
                }
            }
        }
    }
}

From source file:com.twistedequations.rotor.MediaMetadataCompat.java

@TargetApi(Build.VERSION_CODES.KITKAT)
private static void fillEditorTypeMapping() {
    EDITOR_KEYS_TYPE = new SparseArray<Integer>(26);
    EDITOR_KEYS_TYPE.put(MediaMetadataEditor.BITMAP_KEY_ARTWORK, METADATA_TYPE_BITMAP);
    EDITOR_KEYS_TYPE.put(MediaMetadataEditor.RATING_KEY_BY_OTHERS, METADATA_TYPE_RATING);
    EDITOR_KEYS_TYPE.put(MediaMetadataEditor.RATING_KEY_BY_USER, METADATA_TYPE_RATING);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, METADATA_TYPE_LONG);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_TYPE_LONG);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_TYPE_LONG);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
    EDITOR_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
}

From source file:com.blestep.sportsbracelet.view.TimelineChartViewSleep.java

private void processData() {
    if (mCursor != null && !mCursor.isClosed() && mCursor.moveToFirst()) {
        // Load the cursor to memory
        double max = 0d;
        final SparseArray<Object[]> data = new SparseArray<>(mCursor.getCount());

        do {//from   w w w.  j  a va2  s.  co m
            int position = mCursor.getInt(0);
            String label = mCursor.getString(1);
            String start = mCursor.getString(2);
            String end = mCursor.getString(3);
            double deep = mCursor.getDouble(4);
            double light = mCursor.getDouble(5);
            double awake = mCursor.getDouble(6);
            double asleep = mCursor.getDouble(7);
            if (asleep > max) {
                max = asleep;
            }
            Object[] values = new Object[] { label, start, end, deep, light, awake, asleep };
            data.put(position, values);
        } while (mCursor.moveToNext());

        // Calculate the max available offset
        int size = data.size() - 1;
        float maxOffset = mBarWidth * size;

        //swap data
        synchronized (mLock) {
            mData = data;
            mMaxValue = max;
            mMaxOffset = maxOffset;
            mLastOffset = -1.f;
            mCurrentPosition = size;
            mCurrentOffset = computeOffsetForPosition(mCurrentPosition);
            setupTickLabels();
        }
    }
}

From source file:com.blestep.sportsbracelet.view.TimelineChartView.java

private void processData() {
    if (mCursor != null && !mCursor.isClosed() && mCursor.moveToFirst()) {
        // Load the cursor to memory
        double max = 0d;
        final SparseArray<Object[]> data = new SparseArray<>(mCursor.getCount());

        do {/*from www. j  a  v a 2s  .co m*/
            int position = mCursor.getInt(0);
            String label = mCursor.getString(1);
            double stepCount = mCursor.getDouble(2);
            double stepDuration = mCursor.getDouble(3);
            double stepDistance = mCursor.getDouble(4);
            double stepCalorie = mCursor.getDouble(5);
            if (stepCount > max) {
                max = stepCount;
            }
            Object[] values = new Object[] { label, stepCount, stepDuration, stepDistance, stepCalorie };
            data.put(position, values);
        } while (mCursor.moveToNext());

        // Calculate the max available offset
        int size = data.size() - 1;
        float maxOffset = mBarWidth * size;

        //swap data
        synchronized (mLock) {
            mData = data;
            mMaxValue = max;
            mMaxOffset = maxOffset;
            mLastOffset = -1.f;
            mCurrentPosition = size;
            mCurrentOffset = computeOffsetForPosition(mCurrentPosition);
            setupTickLabels();
        }
    }
}

From source file:com.staggeredgrid.library.StaggeredGridView.java

/***
 * Our mColumnTops and mColumnBottoms need to be re-built up to the
 * mSyncPosition - the following layout request will then
 * layout the that position and then fillUp and fillDown appropriately.
 *///from  ww w  .ja  va2 s.  c om
private void onColumnSync() {
    // re-calc tops for new column count!
    int syncPosition = Math.min(mSyncPosition, getCount() - 1);

    SparseArray<Double> positionHeightRatios = new SparseArray<Double>(syncPosition);
    for (int pos = 0; pos < syncPosition; pos++) {
        // check for weirdness
        final GridItemRecord rec = mPositionData.get(pos);
        if (rec == null)
            break;

        Log.d(TAG, "onColumnSync:" + pos + " ratio:" + rec.heightRatio);
        positionHeightRatios.append(pos, rec.heightRatio);
    }

    mPositionData.clear();

    // re-calc our relative position while at the same time
    // rebuilding our GridItemRecord collection

    if (DBG)
        Log.d(TAG, "onColumnSync column width:" + mColumnWidth);

    for (int pos = 0; pos < syncPosition; pos++) {
        //Check for weirdness again
        final Double heightRatio = positionHeightRatios.get(pos);
        if (heightRatio == null) {
            break;
        }

        final GridItemRecord rec = getOrCreateRecord(pos);
        final int height = (int) (mColumnWidth * heightRatio);
        rec.heightRatio = heightRatio;

        int top;
        int bottom;
        // check for headers
        if (isHeaderOrFooter(pos)) {
            // the next top is the bottom for that column
            top = getLowestPositionedBottom();
            bottom = top + height;

            for (int i = 0; i < mColumnCount; i++) {
                mColumnTops[i] = top;
                mColumnBottoms[i] = bottom;
            }
        } else {
            // what's the next column down ?
            final int column = getHighestPositionedBottomColumn();
            // the next top is the bottom for that column
            top = mColumnBottoms[column];
            bottom = top + height + getChildTopMargin(pos) + getChildBottomMargin();

            mColumnTops[column] = top;
            mColumnBottoms[column] = bottom;

            rec.column = column;
        }

        if (DBG)
            Log.d(TAG, "onColumnSync position:" + pos + " top:" + top + " bottom:" + bottom + " height:"
                    + height + " heightRatio:" + heightRatio);
    }

    // our sync position will be displayed in this column
    final int syncColumn = getHighestPositionedBottomColumn();
    setPositionColumn(syncPosition, syncColumn);

    // we want to offset from height of the sync position
    // minus the offset
    int syncToBottom = mColumnBottoms[syncColumn];
    int offset = -syncToBottom + mSpecificTop;
    // offset all columns by
    offsetAllColumnsTopAndBottom(offset);

    // sync the distance to top
    mDistanceToTop = -syncToBottom;

    // stash our bottoms in our tops - though these will be copied back to the bottoms
    System.arraycopy(mColumnBottoms, 0, mColumnTops, 0, mColumnCount);
}

From source file:android.app.Activity.java

/**
 * Restore the state of any saved managed dialogs.
 *
 * @param savedInstanceState The bundle to restore from.
 *///from  w w w . j a v  a2s . c  o m
private void restoreManagedDialogs(Bundle savedInstanceState) {
    final Bundle b = savedInstanceState.getBundle(SAVED_DIALOGS_TAG);
    if (b == null) {
        return;
    }

    final int[] ids = b.getIntArray(SAVED_DIALOG_IDS_KEY);
    final int numDialogs = ids.length;
    mManagedDialogs = new SparseArray<ManagedDialog>(numDialogs);
    for (int i = 0; i < numDialogs; i++) {
        final Integer dialogId = ids[i];
        Bundle dialogState = b.getBundle(savedDialogKeyFor(dialogId));
        if (dialogState != null) {
            // Calling onRestoreInstanceState() below will invoke dispatchOnCreate
            // so tell createDialog() not to do it, otherwise we get an exception
            final ManagedDialog md = new ManagedDialog();
            md.mArgs = b.getBundle(savedDialogArgsKeyFor(dialogId));
            md.mDialog = createDialog(dialogId, dialogState, md.mArgs);
            if (md.mDialog != null) {
                mManagedDialogs.put(dialogId, md);
                onPrepareDialog(dialogId, md.mDialog, md.mArgs);
                md.mDialog.onRestoreInstanceState(dialogState);
            }
        }
    }
}

From source file:com.android.messaging.mmslib.pdu.PduPersister.java

/**
 * Persist a PDU object to specific location in the storage.
 *
 * @param pdu             The PDU object to be stored.
 * @param uri             Where to store the given PDU object.
 * @param subId           Subscription id associated with this message.
 * @param subPhoneNumber TODO//from w w  w . j a  va  2  s  .  c  o  m
 * @param preOpenedFiles  if not null, a map of preopened InputStreams for the parts.
 * @return A Uri which can be used to access the stored PDU.
 */
public Uri persist(final GenericPdu pdu, final Uri uri, final int subId, final String subPhoneNumber,
        final Map<Uri, InputStream> preOpenedFiles) throws MmsException {
    if (uri == null) {
        throw new MmsException("Uri may not be null.");
    }
    long msgId = -1;
    try {
        msgId = ContentUris.parseId(uri);
    } catch (final NumberFormatException e) {
        // the uri ends with "inbox" or something else like that
    }
    final boolean existingUri = msgId != -1;

    if (!existingUri && MESSAGE_BOX_MAP.get(uri) == null) {
        throw new MmsException("Bad destination, must be one of " + "content://mms/inbox, content://mms/sent, "
                + "content://mms/drafts, content://mms/outbox, " + "content://mms/temp.");
    }
    synchronized (PDU_CACHE_INSTANCE) {
        // If the cache item is getting updated, wait until it's done updating before
        // purging it.
        if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
            if (LOCAL_LOGV) {
                LogUtil.v(TAG, "persist: " + uri + " blocked by isUpdating()");
            }
            try {
                PDU_CACHE_INSTANCE.wait();
            } catch (final InterruptedException e) {
                Log.e(TAG, "persist1: ", e);
            }
        }
    }
    PDU_CACHE_INSTANCE.purge(uri);

    final PduHeaders header = pdu.getPduHeaders();
    PduBody body = null;
    ContentValues values = new ContentValues();

    // Mark new messages as seen in the telephony database so that we don't have to
    // do a global "set all messages as seen" since that occasionally seems to be
    // problematic (i.e. very slow).  See bug 18189471.
    values.put(Mms.SEEN, 1);

    //Set<Entry<Integer, String>> set;

    for (int i = ENCODED_STRING_COLUMN_NAME_MAP.size(); --i >= 0;) {
        final int field = ENCODED_STRING_COLUMN_NAME_MAP.keyAt(i);
        final EncodedStringValue encodedString = header.getEncodedStringValue(field);
        if (encodedString != null) {
            final String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field);
            values.put(ENCODED_STRING_COLUMN_NAME_MAP.valueAt(i), toIsoString(encodedString.getTextString()));
            values.put(charsetColumn, encodedString.getCharacterSet());
        }
    }

    for (int i = TEXT_STRING_COLUMN_NAME_MAP.size(); --i >= 0;) {
        final byte[] text = header.getTextString(TEXT_STRING_COLUMN_NAME_MAP.keyAt(i));
        if (text != null) {
            values.put(TEXT_STRING_COLUMN_NAME_MAP.valueAt(i), toIsoString(text));
        }
    }

    for (int i = OCTET_COLUMN_NAME_MAP.size(); --i >= 0;) {
        final int b = header.getOctet(OCTET_COLUMN_NAME_MAP.keyAt(i));
        if (b != 0) {
            values.put(OCTET_COLUMN_NAME_MAP.valueAt(i), b);
        }
    }

    for (int i = LONG_COLUMN_NAME_MAP.size(); --i >= 0;) {
        final long l = header.getLongInteger(LONG_COLUMN_NAME_MAP.keyAt(i));
        if (l != -1L) {
            values.put(LONG_COLUMN_NAME_MAP.valueAt(i), l);
        }
    }

    final SparseArray<EncodedStringValue[]> addressMap = new SparseArray<EncodedStringValue[]>(
            ADDRESS_FIELDS.length);
    // Save address information.
    for (final int addrType : ADDRESS_FIELDS) {
        EncodedStringValue[] array = null;
        if (addrType == PduHeaders.FROM) {
            final EncodedStringValue v = header.getEncodedStringValue(addrType);
            if (v != null) {
                array = new EncodedStringValue[1];
                array[0] = v;
            }
        } else {
            array = header.getEncodedStringValues(addrType);
        }
        addressMap.put(addrType, array);
    }

    final HashSet<String> recipients = new HashSet<String>();
    final int msgType = pdu.getMessageType();
    // Here we only allocate thread ID for M-Notification.ind,
    // M-Retrieve.conf and M-Send.req.
    // Some of other PDU types may be allocated a thread ID outside
    // this scope.
    if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND)
            || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
            || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
        switch (msgType) {
        case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
        case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
            loadRecipients(PduHeaders.FROM, recipients, addressMap);

            // For received messages (whether group MMS is enabled or not) we want to
            // associate this message with the thread composed of all the recipients
            // EXCLUDING our own number. This includes the person who sent the
            // message (the FROM field above) in addition to the other people the message
            // was addressed TO (or CC fields to address group messaging compatibility
            // issues with devices that place numbers in this field). Typically our own
            // number is in the TO/CC field so we have to remove it in loadRecipients.
            checkAndLoadToCcRecipients(recipients, addressMap, subPhoneNumber);
            break;
        case PduHeaders.MESSAGE_TYPE_SEND_REQ:
            loadRecipients(PduHeaders.TO, recipients, addressMap);
            break;
        }
        long threadId = -1L;
        if (!recipients.isEmpty()) {
            // Given all the recipients associated with this message, find (or create) the
            // correct thread.
            threadId = MmsSmsUtils.Threads.getOrCreateThreadId(mContext, recipients);
        } else {
            LogUtil.w(TAG, "PduPersister.persist No recipients; persisting PDU to thread: " + threadId);
        }
        values.put(Mms.THREAD_ID, threadId);
    }

    // Save parts first to avoid inconsistent message is loaded
    // while saving the parts.
    final long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.

    // Figure out if this PDU is a text-only message
    boolean textOnly = true;

    // Get body if the PDU is a RetrieveConf or SendReq.
    if (pdu instanceof MultimediaMessagePdu) {
        body = ((MultimediaMessagePdu) pdu).getBody();
        // Start saving parts if necessary.
        if (body != null) {
            final int partsNum = body.getPartsNum();
            if (LOCAL_LOGV) {
                LogUtil.v(TAG, "PduPersister.persist partsNum: " + partsNum);
            }
            if (partsNum > 2) {
                // For a text-only message there will be two parts: 1-the SMIL, 2-the text.
                // Down a few lines below we're checking to make sure we've only got SMIL or
                // text. We also have to check then we don't have more than two parts.
                // Otherwise, a slideshow with two text slides would be marked as textOnly.
                textOnly = false;
            }
            for (int i = 0; i < partsNum; i++) {
                final PduPart part = body.getPart(i);
                persistPart(part, dummyId, preOpenedFiles);

                // If we've got anything besides text/plain or SMIL part, then we've got
                // an mms message with some other type of attachment.
                final String contentType = getPartContentType(part);
                if (LOCAL_LOGV) {
                    LogUtil.v(TAG, "PduPersister.persist part: " + i + " contentType: " + contentType);
                }
                if (contentType != null && !ContentType.APP_SMIL.equals(contentType)
                        && !ContentType.TEXT_PLAIN.equals(contentType)) {
                    textOnly = false;
                }
            }
        }
    }
    // Record whether this mms message is a simple plain text or not. This is a hint for the
    // UI.
    if (OsUtil.isAtLeastJB_MR1()) {
        values.put(Mms.TEXT_ONLY, textOnly ? 1 : 0);
    }

    if (OsUtil.isAtLeastL_MR1()) {
        values.put(Mms.SUBSCRIPTION_ID, subId);
    } else {
        Assert.equals(ParticipantData.DEFAULT_SELF_SUB_ID, subId);
    }

    Uri res = null;
    if (existingUri) {
        res = uri;
        SqliteWrapper.update(mContext, mContentResolver, res, values, null, null);
    } else {
        res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
        if (res == null) {
            throw new MmsException("persist() failed: return null.");
        }
        // Get the real ID of the PDU and update all parts which were
        // saved with the dummy ID.
        msgId = ContentUris.parseId(res);
    }

    values = new ContentValues(1);
    values.put(Part.MSG_ID, msgId);
    SqliteWrapper.update(mContext, mContentResolver, Uri.parse("content://mms/" + dummyId + "/part"), values,
            null, null);
    // We should return the longest URI of the persisted PDU, for
    // example, if input URI is "content://mms/inbox" and the _ID of
    // persisted PDU is '8', we should return "content://mms/inbox/8"
    // instead of "content://mms/8".
    // TODO: Should the MmsProvider be responsible for this???
    if (!existingUri) {
        res = Uri.parse(uri + "/" + msgId);
    }

    // Save address information.
    for (final int addrType : ADDRESS_FIELDS) {
        final EncodedStringValue[] array = addressMap.get(addrType);
        if (array != null) {
            persistAddress(msgId, addrType, array);
        }
    }

    return res;
}