Merge "FP2-1515: Fixed Dual Sim SMS Delivery Reports bug" into fp2-dev-v2
diff --git a/src/com/android/mms/model/SlideshowModel.java b/src/com/android/mms/model/SlideshowModel.java
index fbb3bc3..16b6af7 100755
--- a/src/com/android/mms/model/SlideshowModel.java
+++ b/src/com/android/mms/model/SlideshowModel.java
@@ -53,6 +53,7 @@
 import com.android.mms.dom.smil.parser.SmilXmlSerializer;
 import com.android.mms.layout.LayoutManager;
 import com.android.mms.ui.UriImage;
+import com.android.mms.ui.MessageUtils;
 import com.google.android.mms.ContentType;
 import com.google.android.mms.MmsException;
 import com.google.android.mms.pdu.GenericPdu;
@@ -111,7 +112,7 @@
         return createFromPduBody(context, getPduBody(context, uri));
     }
 
-    public static SlideshowModel createFromPduBody(Context context, PduBody pb) throws MmsException {
+public static SlideshowModel createFromPduBody(Context context, PduBody pb) throws MmsException {
         SMILDocument document = SmilHelper.getDocument(pb);
 
         // Create root-layout model.
@@ -148,6 +149,7 @@
         int slidesNum = slideNodes.getLength();
         ArrayList<SlideModel> slides = new ArrayList<SlideModel>(slidesNum);
         int totalMessageSize = 0;
+        boolean isClassCastFailed = false;
 
         for (int i = 0; i < slidesNum; i++) {
             // FIXME: This is NOT compatible with the SMILDocument which is
@@ -160,7 +162,16 @@
             ArrayList<MediaModel> mediaSet = new ArrayList<MediaModel>(mediaNum);
 
             for (int j = 0; j < mediaNum; j++) {
-                SMILMediaElement sme = (SMILMediaElement) mediaNodes.item(j);
+                SMILMediaElement sme = null;
+                try {
+                    sme = (SMILMediaElement) mediaNodes.item(j);
+                } catch (ClassCastException e) {
+                    // The node may not be a SMILMediaElement but a SMILElement or else
+                    // in other mobile phones. So catch the exception and handle it later.
+                    isClassCastFailed = true;
+                    Log.e(TAG, e.getMessage());
+                    continue;
+                }
                 try {
                     MediaModel media = MediaModelFactory.getMediaModel(
                             context, sme, layouts, pb);
@@ -222,6 +233,45 @@
                 }
             }
 
+            // Add vcard when receive from other products without ref target in smil.
+            int partsNum = pb.getPartsNum();
+            if (needAddUnrecognizedPart(slidesNum,
+                    mediaNum, partsNum, isClassCastFailed)) {
+                for (int k = 0; k < partsNum; k++) {
+                    PduPart part = pb.getPart(k);
+                    String contentType = (new String(part.getContentType())).toLowerCase();
+                    if (MessageUtils.OCT_STREAM.equals(contentType)) {
+                        byte[] name = part.getName();
+                        if (name == null) {
+                            name = part.getContentLocation();
+                        }
+                        if (name != null) {
+                            // Convert content type application/oct-stream (which is a vcard) to
+                            // application/X-vcard so that this mms can be recognized.
+                            contentType = MessageUtils.convertToVcardType(new String(name));
+                        }
+                    }
+                    if (ContentType.TEXT_VCARD.toLowerCase().equals(contentType) ||
+                            ContentType.TEXT_VCALENDAR.toLowerCase().equals(contentType)) {
+                        byte[] data = part.getContentLocation();
+                        if (data == null) {
+                            data = part.getName();
+                        }
+                        MediaModel vMedia = null;
+                        if (ContentType.TEXT_VCARD.toLowerCase().equals(contentType)) {
+                            vMedia = new VcardModel(context, contentType,
+                                    new String(data), part.getDataUri());
+                        } else {
+                            vMedia = new VCalModel(context, contentType,
+                                    new String(data), part.getDataUri());
+                        }
+                        mediaSet.add(vMedia);
+                        totalMessageSize += vMedia.getMediaSize();
+                        break;
+                    }
+                }
+            }
+
             SlideModel slide = new SlideModel((int) (par.getDur() * 1000), mediaSet);
             slide.setFill(par.getFill());
             SmilHelper.addParElementEventListeners((EventTarget) par, slide);
@@ -234,6 +284,21 @@
         return slideshow;
     }
 
+   
+   private static boolean needAddUnrecognizedPart(
+             int slidesNum, int mediaNum, int partsNum, boolean castFailed) {
+         if (slidesNum != 1) {
+             return false;
+         }
+
+         if (mediaNum != partsNum || castFailed) {
+             // Some part is not recognized as a media, need add.
+             return true;
+         } else {
+             return false;
+         }
+    }
+
     public PduBody toPduBody() {
         if (mPduBodyCache == null) {
             mDocumentCache = SmilHelper.getDocument(this);
diff --git a/src/com/android/mms/model/VCalModel.java b/src/com/android/mms/model/VCalModel.java
new file mode 100644
index 0000000..1db266d
--- /dev/null
+++ b/src/com/android/mms/model/VCalModel.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2014, CyanogenMod
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+         copyright notice, this list of conditions and the following
+         disclaimer in the documentation and/or other materials provided
+         with the distribution.
+       * Neither the name of The Linux Foundation nor the names of its
+         contributors may be used to endorse or promote products derived
+         from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+   OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.mms.model;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+import com.google.android.mms.ContentType;
+import com.google.android.mms.MmsException;
+import org.w3c.dom.events.Event;
+
+public class VCalModel extends MediaModel {
+    private static final String TAG = MediaModel.TAG;
+
+    public VCalModel(Context context, Uri uri) throws MmsException {
+        this(context, ContentType.TEXT_VCALENDAR, null, uri);
+        initModelFromUri(uri);
+    }
+
+    public VCalModel(Context context, String contentType, String src, Uri uri)
+            throws MmsException {
+        super(context, SmilHelper.ELEMENT_TAG_REF, contentType, src, uri);
+    }
+
+    private void initModelFromUri(Uri uri) throws MmsException {
+        String scheme = uri.getScheme();
+        if (scheme == null) {
+            Log.e(TAG, "The uri's scheme is null.");
+            return;
+        }
+
+        if (scheme.equals("file")) {
+            mSrc = getFileSrc(uri);
+        }
+        initMediaDuration();
+    }
+
+    private String getFileSrc(Uri uri) {
+        String path = Uri.decode(uri.toString());
+        return path.substring(path.lastIndexOf('/') + 1);
+    }
+
+    @Override
+    public void handleEvent(Event evt) {
+    }
+
+    @Override
+    protected boolean isPlayable() {
+        return false;
+    }
+
+    @Override
+    protected void initMediaDuration() throws MmsException {
+        mDuration = 0;
+    }
+
+}
+
diff --git a/src/com/android/mms/model/VcardModel.java b/src/com/android/mms/model/VcardModel.java
new file mode 100644
index 0000000..f62c19c
--- /dev/null
+++ b/src/com/android/mms/model/VcardModel.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+         copyright notice, this list of conditions and the following
+         disclaimer in the documentation and/or other materials provided
+         with the distribution.
+       * Neither the name of The Linux Foundation nor the names of its
+         contributors may be used to endorse or promote products derived
+         from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+   OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.mms.model;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.Telephony.Mms.Part;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.MmsException;
+
+import org.w3c.dom.events.Event;
+
+public class VcardModel extends MediaModel {
+    private static final String TAG = MediaModel.TAG;
+
+    private String mLookupUri = null;
+
+    public VcardModel(Context context, Uri uri) throws MmsException {
+        this(context, ContentType.TEXT_VCARD, null, uri);
+        initModelFromUri(uri);
+    }
+
+    public VcardModel(Context context, String contentType, String src, Uri uri)
+            throws MmsException {
+        super(context, SmilHelper.ELEMENT_TAG_REF, contentType, src, uri);
+        if (!TextUtils.isEmpty(src)) {
+            initLookupUri(uri);
+        }
+    }
+
+    private void initModelFromUri(Uri uri) throws MmsException {
+        String scheme = uri.getScheme();
+        if (scheme == null) {
+            Log.e(TAG, "The uri's scheme is null.");
+            return;
+        }
+
+        if (scheme.equals("file")) {
+            mSrc = uri.getLastPathSegment();
+        } else if (scheme.equals("content")){
+            Cursor c = null;
+            try {
+                c = getContentCursor(uri);
+                mLookupUri = getLookupUri(uri,c);
+                mSrc = getLookupSrc(uri,c);
+            } finally {
+                if (c != null) {
+                    c.close();
+                    c = null;
+                } else {
+                    throw new MmsException("Bad URI: " + uri);
+                }
+            }
+        }
+        initMediaDuration();
+    }
+
+    private Cursor getContentCursor(Uri uri) {
+        if(isMmsUri(uri)) {
+            return mContext.getContentResolver().query(uri, null, null, null, null);
+        }
+        return mContext.getContentResolver()
+                .query(getExtraLookupUri(uri), null, null, null, null);
+    }
+
+    private String getLookupUri(Uri uri, Cursor c) {
+        if(isMmsUri(uri))
+            return getMmsLookupUri(uri,c);
+        return getExtraLookupUri(uri).toString();
+    }
+
+    private String getMmsLookupUri(Uri uri, Cursor c) {
+        if (c != null && c.moveToFirst())
+            return c.getString(c.getColumnIndexOrThrow(Part.CONTENT_DISPOSITION));
+        return "";
+    }
+
+    private Uri getExtraLookupUri(Uri uri) {
+        String lookup = uri.getLastPathSegment();
+        Uri lookupUri =
+            Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookup);
+        return lookupUri;
+    }
+
+    private String getLookupSrc(Uri uri, Cursor c) throws MmsException {
+        if(isMmsUri(uri))
+            return getMmsLookupSrc(c);
+        return getExtraSrc(uri,c);
+    }
+
+    private String getMmsLookupSrc(Cursor c) {
+        if (c != null && c.moveToFirst()) {
+            String path = c.getString(c.getColumnIndexOrThrow(Part._DATA));
+            return path.substring(path.lastIndexOf('/') + 1);
+        }
+        return "";
+    }
+
+    private String getExtraSrc(Uri uri, Cursor c) throws MmsException {
+        if (c != null && c.getCount() == 1 && c.moveToFirst()) {
+            String displayName = c.getString(c.getColumnIndexOrThrow(ContactsContract
+                    .Contacts.DISPLAY_NAME));
+            return displayName + ".vcf";
+        } else {
+            throw new MmsException("Type of media is unknown.");
+        }
+    }
+
+    private void initLookupUri(Uri uri) {
+        if (isMmsUri(uri)) {
+            ContentResolver cr = mContext.getContentResolver();
+            Cursor c = cr.query(uri, null, null, null, null);
+            try {
+                if (c != null && c.moveToFirst()) {
+                    mLookupUri = c.getString(c.getColumnIndexOrThrow(Part.CONTENT_DISPOSITION));
+                }
+            } finally {
+                if (c != null) {
+                    c.close();
+                    c = null;
+                }
+            }
+        }
+    }
+
+    public String getLookupUri() {
+        return mLookupUri;
+    }
+
+    @Override
+    public void handleEvent(Event evt) {
+    }
+
+    @Override
+    protected boolean isPlayable() {
+        return false;
+    }
+
+    @Override
+    protected void initMediaDuration() throws MmsException {
+        mDuration = 0;
+    }
+
+}
+
diff --git a/src/com/android/mms/ui/MessageListItem.java b/src/com/android/mms/ui/MessageListItem.java
index e20ed99..7846876 100755
--- a/src/com/android/mms/ui/MessageListItem.java
+++ b/src/com/android/mms/ui/MessageListItem.java
@@ -334,7 +334,7 @@
         if (!sameItem || haveLoadedPdu) {
             boolean isSelf = Sms.isOutgoingFolder(mMessageItem.mBoxId);
             String addr = isSelf ? null : mMessageItem.mAddress;
-            updateAvatarView(mMessageItem.mAddress, false);
+            updateAvatarView(addr, isSelf);
         }
 
         // Add SIM sms address above body.
diff --git a/src/com/android/mms/ui/MessageUtils.java b/src/com/android/mms/ui/MessageUtils.java
index ba770f6..15671db 100755
--- a/src/com/android/mms/ui/MessageUtils.java
+++ b/src/com/android/mms/ui/MessageUtils.java
@@ -163,6 +163,17 @@
     public static final int PREFER_SMS_STORE_TO_SIM = 1;
     private static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
 
+// Consider oct-strean as the content type of vCard
+    public static final String OCT_STREAM = "application/oct-stream";
+    public static final String VCARD = "vcf";
+
+    // distinguish view vcard from mms but not from contacts.
+    public static final String VIEW_VCARD = "VIEW_VCARD_FROM_MMS";
+    // add for obtain mms data path
+    private static final String MMS_DATA_DIR = "/data/phonedata";
+    private static final String MMS_DATABASE_DIR =
+            "/com.android.providers.telephony/databases/mmssms.db";
+
     // add threshold for low memory
     private static final int THRESHOLD_LOW_MEM_PERCENTAGE = 5;
     // add for obtain mms data path
@@ -524,6 +535,19 @@
         }
     }
 
+    public static String convertToVcardType(String src) {
+        if (!TextUtils.isEmpty(src)) {
+            int index = src.lastIndexOf('.');
+            if (index > 0) {
+                String extension = src.substring(index + 1, src.length());
+                if (extension.toLowerCase().equals(VCARD)) {
+                    return ContentType.TEXT_VCARD.toLowerCase();
+                }
+            }
+        }
+        return "";
+    }
+
     public static int getAttachmentType(SlideshowModel model, MultimediaMessagePdu mmp) {
         if (model == null || mmp == null) {
             return MessageItem.ATTACHMENT_TYPE_NOT_LOADED;