com.android.mms.ui.MessageUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.android.mms.ui.MessageUtils.java

Source

/*
* Copyright (C) 2014 MediaTek Inc.
* Modification based on code covered by the mentioned copyright
* and/or permission notice(s).
*/
/*
 * Copyright (C) 2008 Esmertec AG.
 * Copyright (C) 2008 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.mms.ui;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.AlertDialog;
import android.app.FragmentTransaction;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SqliteWrapper;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.PorterDuff.Mode;
import android.location.Country;
import android.location.CountryDetector;
import android.media.CamcorderProfile;
import android.media.RingtoneManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Telephony;
import android.provider.Telephony.Mms;
import android.provider.Telephony.Sms;
import android.support.v4.content.FileProvider;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.text.style.StyleSpan;
import android.text.style.URLSpan;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
import android.widget.TextView;
import android.widget.Toast;

import com.android.i18n.phonenumbers.AsYouTypeFormatter;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.internal.telephony.ISms;
import com.android.internal.telephony.PhoneConstants;
import com.android.mms.LogTag;
import com.android.mms.MmsApp;
import com.android.mms.MmsConfig;
import com.android.mms.TempFileProvider;
import com.android.mms.data.Contact;
import com.android.mms.data.Conversation;
import com.android.mms.data.WorkingMessage;
import com.android.mms.model.CarrierContentRestriction;
import com.android.mms.model.MediaModel;
import com.android.mms.model.SlideModel;
import com.android.mms.model.SlideshowModel;
import com.android.mms.R;
import com.android.mms.transaction.MessagingNotification;
import com.android.mms.ui.DeliveryReportActivity.MmsReportStatus;
import com.android.mms.util.FeatureOption;
import com.android.mms.util.MmsContentType;
import com.android.mms.util.MmsLog;
import com.android.mms.util.MuteCache;
import com.google.android.mms.MmsException;
import com.google.android.mms.pdu.CharacterSets;
import com.google.android.mms.pdu.EncodedStringValue;
import com.google.android.mms.pdu.MultimediaMessagePdu;
import com.google.android.mms.pdu.NotificationInd;
import com.google.android.mms.pdu.PduBody;
import com.google.android.mms.pdu.PduHeaders;
import com.google.android.mms.pdu.PduPart;
import com.google.android.mms.pdu.PduPersister;
import com.google.android.mms.pdu.RetrieveConf;
import com.google.android.mms.pdu.SendReq;

import com.mediatek.cb.cbmsg.CbMessageListActivity;
import com.mediatek.cb.cbmsg.CbMessagingNotification;
import com.mediatek.internal.telephony.ITelephonyEx;
import com.mediatek.ipmsg.util.IpMessageUtils;
import com.mediatek.mms.ext.IOpMessageUtilsExt;
import com.mediatek.mms.util.DrmUtilsEx;
import com.mediatek.omadrm.OmaDrmStore;
import com.mediatek.opmsg.util.OpMessageUtils;
import com.mediatek.setting.NotificationPreferenceActivity;
import com.mediatek.storage.StorageManagerEx;
import com.mediatek.telephony.TelephonyManagerEx;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import android.telephony.SmsMessage;

/**
 * An utility class for managing messages.
 */
public class MessageUtils {
    interface ResizeImageResultCallback {
        void onResizeResult(PduPart part, boolean append);
    }

    private static final String TAG = LogTag.TAG;

    private static String sLocalNumber;
    private static String[] sNoSubjectStrings;

    // Cache of both groups of space-separated ids to their full
    // comma-separated display names, as well as individual ids to
    // display names.
    // TODO: is it possible for canonical address ID keys to be
    // re-used?  SQLite does reuse IDs on NULL id_ insert, but does
    // anything ever delete from the mmssms.db canonical_addresses
    // table?  Nothing that I could find.
    private static final Map<String, String> sRecipientAddress = new ConcurrentHashMap<String, String>(
            20 /* initial capacity */);

    /**
     * MMS address parsing data structures
     */
    // allowable phone number separators
    private static final char[] NUMERIC_CHARS_SUGAR = { '-', '.', ',', '(', ')', ' ', '/', '\\', '*', '#', '+' };

    public static final Uri THREAD_SETTINGS_URI = Uri.parse("content://mms-sms/thread_settings/");

    private static HashMap numericSugarMap = new HashMap(NUMERIC_CHARS_SUGAR.length);

    static {
        for (int i = 0; i < NUMERIC_CHARS_SUGAR.length; i++) {
            numericSugarMap.put(NUMERIC_CHARS_SUGAR[i], NUMERIC_CHARS_SUGAR[i]);
        }
    }

    /// M: Code analyze 006, For fix bug ALPS00234739, draft can't be saved
    // after share the edited picture to the same ricipient.Remove old Mms draft
    // in conversation list instead of compose view. @{
    private static Thread mRemoveOldMmsThread;
    /// @}
    /// M: Code analyze 011, For fix bug ALPS00246535, prompt
    // "Please input SD card" after back from attachment list screen. @{
    public static final String SDCARD_1 = "/mnt/sdcard";
    public static final String SDCARD_2 = "/mnt/sdcard2";
    /// @}
    /// M: Code analyze 021, For fix bug ALPS00279524, The "JE" about "MMS"
    // pops up after we launch "Messaging" again. @{
    public static final int NUMBER_OF_RESIZE_ATTEMPTS = 4;
    /// @}
    private static SlideModel slide = null;
    private static String sLocalNumber2;

    private static final String DB_PATH = "/data/data/com.android.providers.telephony/databases/mmssms.db";
    /// M: for showing notification when view mms with video player in full screen model.
    private static final String EXTRA_FULLSCREEN_NOTIFICATION = "mediatek.intent.extra.FULLSCREEN_NOTIFICATION";
    /// M: Action for contact selection intent
    public static final String ACTION_CONTACT_SELECTION = "android.intent.action.contacts.list.PICKMULTIPHONEANDEMAILS";

    public static IOpMessageUtilsExt sOpMessageUtilsExt;

    /// M: fix bug ALPS01523754.set google+ pic as wallpaper.@{
    private static final String TEMP_WALLPAPER = "tempWallpaper";
    /// @}
    public static final int SHOW_INVITE_PANEL = 1;
    public static final int UPDATE_SENDBUTTON = 2;

    public static final int[] SubBackgroundLightRes = new int[] { com.mediatek.internal.R.drawable.sim_light_blue,
            com.mediatek.internal.R.drawable.sim_light_orange, com.mediatek.internal.R.drawable.sim_light_green,
            com.mediatek.internal.R.drawable.sim_light_purple };

    public static final String MMS_SHARED_FILES_FOLDER_NAME = "voicemails";
    public static final String MMS_SHARED_FILE_PROVIDER_AUTHORITIES = "com.android.mms.files";
    private static final String TEMP_FILE_TAG = "MmsTemp";

    private MessageUtils() {
        // Forbidden being instantiated.
    }

    public static void init(Context context) {
        sOpMessageUtilsExt = OpMessageUtils.getOpMessagePlugin().getOpMessageUtilsExt();
        sOpMessageUtilsExt.setExtendedAudioType(CarrierContentRestriction.getSupportedAudioTypes());
    }

    /** M: google jb.mr1 patch
     * cleanseMmsSubject will take a subject that's says, "<Subject: no subject>", and return
     * a null string. Otherwise it will return the original subject string.
     * @param context a regular context so the function can grab string resources
     * @param subject the raw subject
     * @return
     */
    public static String cleanseMmsSubject(Context context, String subject) {
        if (TextUtils.isEmpty(subject)) {
            return subject;
        }
        if (sNoSubjectStrings == null) {
            sNoSubjectStrings = context.getResources().getStringArray(R.array.empty_subject_strings);

        }
        final int len = sNoSubjectStrings.length;
        for (int i = 0; i < len; i++) {
            if (subject.equalsIgnoreCase(sNoSubjectStrings[i])) {
                return null;
            }
        }
        return subject;
    }

    public static Bitmap getCircularBitmap(Bitmap bitmap) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = bitmap.getWidth() / 2;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);
        return output;
    }

    // check if the sub is active.
    public static boolean isSmsSubIdActive(Context context, int subId) {
        List<SubscriptionInfo> subinfoList = SubscriptionManager.from(context).getActiveSubscriptionInfoList();
        for (SubscriptionInfo subInfo : subinfoList) {
            if (subInfo.getSubscriptionId() == subId) {
                return true;
            }
        }
        return false;
    }

    // / M: Code analyze 027, new feature, to improve the performance of Mms. @{
    public static String getMessageDetails(Context context, MessageItem msgItem) {
        if (msgItem == null) {
            return null;
        }

        if ("mms".equals(msgItem.mType)) {
            int type = msgItem.mMessageType;
            switch (type) {
            case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
                return getNotificationIndDetails(context, msgItem);
            case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
            case PduHeaders.MESSAGE_TYPE_SEND_REQ:
                return getMultimediaMessageDetails(context, msgItem);
            default:
                Log.w(TAG, "No details could be retrieved.");
                return "";
            }
        } else {
            return getTextMessageDetails(context, msgItem);
        }
    }
    /// @}

    // / M: Code analyze 027, new feature, to improve the performance of Mms. @{
    private static String getNotificationIndDetails(Context context, MessageItem msgItem) {
        /// @}
        StringBuilder details = new StringBuilder();
        Resources res = context.getResources();

        // / M: Code analyze 027, new feature, to improve the performance of
        // Mms. @{
        Uri uri = ContentUris.withAppendedId(Mms.CONTENT_URI, msgItem.mMsgId);
        /// @}
        NotificationInd nInd;

        try {
            // / M: Code analyze 027, new feature, to improve the performance of
            // Mms. @{
            nInd = (NotificationInd) PduPersister.getPduPersister(context).load(uri);
            /// @}
        } catch (MmsException e) {
            Log.e(TAG, "Failed to load the message: " + uri, e);
            return context.getResources().getString(R.string.cannot_get_details);
        }

        // Message Type: Mms Notification.
        details.append(res.getString(R.string.message_type_label));
        details.append(res.getString(R.string.multimedia_notification));

        /// M: Code analyze 026, new feature, show the service center number.
        // @{
        details.append('\n');
        details.append(res.getString(R.string.service_center_label));
        details.append(!TextUtils.isEmpty(msgItem.mServiceCenter) ? msgItem.mServiceCenter : "");
        /// @}

        // From: ***
        String from = extractEncStr(context, nInd.getFrom());
        details.append('\n');
        details.append(res.getString(R.string.from_label));
        details.append(!TextUtils.isEmpty(from) ? from : res.getString(R.string.hidden_sender_address));

        // Date: ***
        details.append('\n');
        details.append(res.getString(R.string.expire_on,
                MessageUtils.formatTimeStampString(context, nInd.getExpiry() * 1000L, true)));

        // Subject: ***
        details.append('\n');
        details.append(res.getString(R.string.subject_label));

        EncodedStringValue subject = nInd.getSubject();
        if (subject != null) {
            details.append(subject.getString());
        }

        // Message class: Personal/Advertisement/Infomational/Auto
        details.append('\n');
        details.append(res.getString(R.string.message_class_label));
        details.append(new String(nInd.getMessageClass()));

        // Message size: *** KB
        details.append('\n');
        details.append(res.getString(R.string.message_size_label));
        details.append(String.valueOf((nInd.getMessageSize() + 1023) / 1024));
        details.append(context.getString(R.string.kilobyte));

        return details.toString();
    }
    /// @}

    /// M: @{
    // / M: Code analyze 027, new feature, to improve the performance of Mms. @{
    private static String getMultimediaMessageDetails(Context context, MessageItem msgItem) {
        if (msgItem.mMessageType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) {
            return getNotificationIndDetails(context, msgItem);
        }

        StringBuilder details = new StringBuilder();
        Resources res = context.getResources();

        // / M: Code analyze 027, new feature, to improve the performance of
        // Mms. @{
        Uri uri = ContentUris.withAppendedId(Mms.CONTENT_URI, msgItem.mMsgId);
        /// @}
        MultimediaMessagePdu msg;
        try {
            msg = (MultimediaMessagePdu) PduPersister.getPduPersister(context).load(uri);
        } catch (MmsException e) {
            Log.e(TAG, "Failed to load the message: " + uri, e);
            return context.getResources().getString(R.string.cannot_get_details);
        }

        // Message Type: Text message.
        initializeMsgDetails(context, details, res, msg);

        // SentDate: ***
        if (msg.getDateSent() > 0 && msgItem.mBoxId == Mms.MESSAGE_BOX_INBOX) {
            details.append('\n');
            details.append(res.getString(R.string.sent_label));
            String dateStr = formatDateAndTimeStampString(context, 0, msg.getDateSent() * 1000L, true);
            details.append(dateStr);
        }

        // Date: ***
        details.append('\n');
        // / M: Code analyze 027, new feature, to improve the performance of
        // Mms. @{
        int msgBox = msgItem.mBoxId;
        /// @}
        if (msgBox == Mms.MESSAGE_BOX_DRAFTS) {
            details.append(res.getString(R.string.saved_label));
        } else if (msgBox == Mms.MESSAGE_BOX_INBOX) {
            details.append(res.getString(R.string.received_label));
        } else {
            details.append(res.getString(R.string.sent_label));
        }
        String dateStr = formatDateAndTimeStampString(context, 0, msg.getDate() * 1000L, true);

        details.append(dateStr);

        // Subject: ***
        details.append('\n');
        details.append(res.getString(R.string.subject_label));

        // / M: Code analyze 027, new feature, to improve the performance of
        // Mms. @{
        int size = msgItem.mMessageSize;
        /// @}
        EncodedStringValue subject = msg.getSubject();
        if (subject != null) {
            String subStr = subject.getString();
            // Message size should include size of subject.
            // Modify ALPS00427926. Because message size has
            // include PDU header, so it should not add this size here
            //size += subStr.length();
            details.append(subStr);
        }

        // Priority: High/Normal/Low
        return formatDetails(details, context, msg, size, res);
    }

    private static void initializeMsgDetails(Context context, StringBuilder details, Resources res,
            MultimediaMessagePdu msg) {
        details.append(res.getString(R.string.message_type_label));
        details.append(res.getString(R.string.multimedia_message));

        if (msg instanceof RetrieveConf) {
            // From: ***
            String from = extractEncStr(context, ((RetrieveConf) msg).getFrom());
            details.append('\n');
            details.append(res.getString(R.string.from_label));
            details.append(!TextUtils.isEmpty(from) ? from : res.getString(R.string.hidden_sender_address));
        }

        // To: ***
        details.append('\n');
        details.append(res.getString(R.string.to_address_label));
        EncodedStringValue[] to = msg.getTo();
        if (to != null) {
            details.append(EncodedStringValue.concat(to));
        } else {
            Log.w(TAG, "recipient list is empty!");
        }

        // Bcc: ***
        if (msg instanceof SendReq) {
            EncodedStringValue[] values = ((SendReq) msg).getBcc();
            if ((values != null) && (values.length > 0)) {
                details.append('\n');
                details.append(res.getString(R.string.bcc_label));
                details.append(EncodedStringValue.concat(values));
            }
        }
    }

    /// @}

    // / M: Code analyze 027, new feature, to improve the performance of Mms. @{
    private static String getTextMessageDetails(Context context, MessageItem msgItem) {
        StringBuilder details = new StringBuilder();
        Resources res = context.getResources();

        // Message Type: Text message.
        details.append(res.getString(R.string.message_type_label));
        String type = IpMessageUtils.getIpMessagePlugin(context).getIpUtils()
                .getIpTextMessageType(msgItem.mIpMessageItem);
        /// M: modify for ipmessage
        if (type != null) {
            details.append(type);
        } else {
            details.append(res.getString(R.string.text_message));
        }

        // Address: ***
        details.append('\n');
        // / M: Code analyze 027, new feature, to improve the performance of
        // Mms. @{
        int smsType = msgItem.mBoxId;
        /// @}
        if (Sms.isOutgoingFolder(smsType)) {
            details.append(res.getString(R.string.to_address_label));
        } else {
            details.append(res.getString(R.string.from_label));
        }
        // / M: Code analyze 027, new feature, to improve the performance of
        // Mms. @{

        details.append(msgItem.mAddress);

        /// @}

        // / @}

        // SentDate: ***
        if (msgItem.mSmsSentDate > 0 && smsType == Sms.MESSAGE_TYPE_INBOX) {
            details.append('\n');
            details.append(res.getString(R.string.sent_label));
            String dateStr = formatDateAndTimeStampString(context, 0, msgItem.mSmsSentDate, true);
            details.append(dateStr);
        }

        // / M: Code analyze 027, new feature, to improve the performance of
        // Mms. @{
        if (msgItem.mSmsDate > 0L) {
            details.append('\n');
            if (smsType == Sms.MESSAGE_TYPE_DRAFT) {
                details.append(res.getString(R.string.saved_label));
            } else if (smsType == Sms.MESSAGE_TYPE_INBOX) {
                details.append(res.getString(R.string.received_label));
            } else {
                details.append(res.getString(R.string.sent_label));
            }
            String dateStr = formatDateAndTimeStampString(context, 0, msgItem.mSmsDate, true);
            details.append(dateStr);
        }
        /// @}

        /// M: Code analyze 028, new feature, add service center in Sms details. @{
        if (smsType == Sms.MESSAGE_TYPE_INBOX) {
            details.append('\n');
            details.append(res.getString(R.string.service_center_label));
            details.append(msgItem.mServiceCenter == null ? "" : msgItem.mServiceCenter);
        }
        /// @}

        // Delivered: ***
        if (smsType == Sms.MESSAGE_TYPE_SENT) {
            // For sent messages with delivery reports, we stick the delivery time in the
            // date_sent column (see MessageStatusReceiver).
            /// M: long dateDelivered = cursor.getLong(MessageListAdapter.COLUMN_SMS_DATE_SENT);
            // / M: Code analyze 027, new feature, to improve the performance of
            // Mms. @{
            long dateDelivered = -1L;
            if (dateDelivered > 0) {
                details.append('\n');
                details.append(res.getString(R.string.delivered_label));
                details.append(MessageUtils.formatTimeStampString(context, dateDelivered, true));
            }
        }

        // Error code: ***
        int errorCode = msgItem.mErrorCode;
        /// @}
        if (errorCode != 0) {
            details.append('\n').append(res.getString(R.string.error_code_label)).append(errorCode);
        }

        return details.toString();
    }
    /// @}

    static private String getPriorityDescription(Context context, int PriorityValue) {
        Resources res = context.getResources();
        switch (PriorityValue) {
        case PduHeaders.PRIORITY_HIGH:
            return res.getString(R.string.priority_high);
        case PduHeaders.PRIORITY_LOW:
            return res.getString(R.string.priority_low);
        case PduHeaders.PRIORITY_NORMAL:
        default:
            return res.getString(R.string.priority_normal);
        }
    }

    public static int getAttachmentType(SlideshowModel model, MultimediaMessagePdu mmp) {
        if (model == null || mmp == null) {
            return MessageItem.ATTACHMENT_TYPE_NOT_LOADED;
        }

        int numberOfSlides = model.size();
        if (numberOfSlides > 1) {
            return WorkingMessage.SLIDESHOW;
        } else if (numberOfSlides == 1) {
            // Only one slide in the slide-show.
            SlideModel slide = model.get(0);
            if (slide.hasVideo()) {
                return WorkingMessage.VIDEO;
            }

            if (slide.hasAudio() && slide.hasImage()) {
                return WorkingMessage.SLIDESHOW;
            }

            if (slide.hasAudio()) {
                return WorkingMessage.AUDIO;
            }

            if (slide.hasImage()) {
                return WorkingMessage.IMAGE;
            }

            /// M: Code analyze 004, For fix bug ALPS00242740, can't see the
            // select all/cut/copy icon. @{
            if (model.sizeOfFilesAttach() > 0) {
                return WorkingMessage.ATTACHMENT;
            }
            /// @}

            if (slide.hasText()) {
                return WorkingMessage.TEXT;
            }

            String subject = mmp.getSubject() != null ? mmp.getSubject().getString() : null;
            if (!TextUtils.isEmpty(subject)) {
                return WorkingMessage.TEXT;
            }
        }

        if (model.sizeOfFilesAttach() > 0) {
            return WorkingMessage.ATTACHMENT;
        }

        return MessageItem.ATTACHMENT_TYPE_NOT_LOADED;
    }

    public static String formatTimeStampString(Context context, long when) {
        return formatTimeStampString(context, when, false);
    }

    public static String formatDateAndTimeStampString(Context context, long msgDate, long msgDateSent,
            boolean fullFormat) {
        String dateAndTime = "";
        if (msgDate != 0) {
            dateAndTime = formatTimeStampString(context, msgDate, fullFormat);
        }
        if (msgDateSent != 0) {
            dateAndTime = formatTimeStampString(context, msgDateSent, fullFormat);
        }
        return sOpMessageUtilsExt.formatDateAndTimeStampString(dateAndTime, context, msgDate, msgDateSent,
                fullFormat);
    }

    public static String formatTimeStampString(Context context, long when, boolean fullFormat) {
        Time then = new Time();
        then.set(when);
        Time now = new Time();
        now.setToNow();

        // Basic settings for formatDateTime() we want for all cases.
        int format_flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT |
        /// M: Fix ALPS00419488 to show 12:00, so mark DateUtils.FORMAT_ABBREV_ALL
        //DateUtils.FORMAT_ABBREV_ALL |
                DateUtils.FORMAT_CAP_AMPM;

        // If the message is from a different year, show the date and year.
        if (then.year != now.year) {
            format_flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
        } else if (then.yearDay != now.yearDay) {
            // If it is from a different day than today, show only the date.
            format_flags |= DateUtils.FORMAT_SHOW_DATE;
        } else {
            // Otherwise, if the message is from today, show the time.
            format_flags |= DateUtils.FORMAT_SHOW_TIME;
        }

        // If the caller has asked for full details, make sure to show the date
        // and time no matter what we've determined above (but still make showing
        // the year only happen if it is a different year from today).
        if (fullFormat) {
            format_flags |= (DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
        }

        String dateTime = sOpMessageUtilsExt.formatTimeStampString(context, when, format_flags);
        if (dateTime != null) {
            return dateTime;
        }
        return DateUtils.formatDateTime(context, when, format_flags);
    }

    public static void selectAudio(Activity activity, int requestCode) {
        // / M: Code analyze 027, new feature, to improve the performance of
        // Mms. @{
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType(MmsContentType.AUDIO_UNSPECIFIED);
        String[] mimeTypess = new String[] { MmsContentType.AUDIO_UNSPECIFIED, MmsContentType.AUDIO_OGG,
                "application/x-ogg" };
        intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypess);
        if (FeatureOption.MTK_DRM_APP) {
            intent.putExtra(OmaDrmStore.DrmIntentExtra.EXTRA_DRM_LEVEL, OmaDrmStore.DrmIntentExtra.LEVEL_SD);
        }
        /// @}
        activity.startActivityForResult(intent, requestCode);
    }

    public static void recordSound(Activity activity, int requestCode, long sizeLimit) {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType(MmsContentType.AUDIO_AMR);
        intent.setClassName("com.android.soundrecorder", "com.android.soundrecorder.SoundRecorder");
        intent.putExtra(android.provider.MediaStore.Audio.Media.EXTRA_MAX_BYTES, sizeLimit);
        activity.startActivityForResult(intent, requestCode);
    }

    public static void recordVideo(Activity activity, int requestCode, long sizeLimit) {
        // The video recorder can sometimes return a file that's larger than the max we
        // say we can handle. Try to handle that overshoot by specifying an 85% limit.
        /// M: media recoder can handle this issue,so mark it.
        //        sizeLimit *= .85F;

        int durationLimit = getVideoCaptureDurationLimit();

        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
            log("recordVideo: durationLimit: " + durationLimit + " sizeLimit: " + sizeLimit);
        }

        Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
        intent.putExtra("android.intent.extra.sizeLimit", sizeLimit);
        intent.putExtra("android.intent.extra.durationLimit", durationLimit);
        /// M: Code analyze 009, For fix bug ALPS00241707, You can not add
        // capture video to Messaging after you preview it in Gallery. @{
        intent.putExtra(MediaStore.EXTRA_OUTPUT, TempFileProvider.SCRAP_VIDEO_URI);
        /// M: fix bug ALPS01043585
        intent.putExtra("CanShare", false);
        activity.startActivityForResult(intent, requestCode);
    }

    public static void capturePicture(Activity activity, int requestCode) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, TempFileProvider.SCRAP_CONTENT_URI);
        activity.startActivityForResult(intent, requestCode);
    }

    public static int getVideoCaptureDurationLimit() {
        CamcorderProfile camcorder = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
        return camcorder == null ? 0 : camcorder.duration;
    }

    public static void selectVideo(Context context, int requestCode) {
        selectMediaByType(context, requestCode, MmsContentType.VIDEO_UNSPECIFIED, true);
    }

    public static void selectImage(Context context, int requestCode) {
        selectMediaByType(context, requestCode, MmsContentType.IMAGE_UNSPECIFIED, false);
    }

    private static void selectMediaByType(Context context, int requestCode, String contentType,
            boolean localFilesOnly) {
        if (context instanceof Activity) {

            Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT);

            innerIntent.setType(contentType);
            /// M: @{
            if (FeatureOption.MTK_DRM_APP) {
                innerIntent.putExtra(OmaDrmStore.DrmIntentExtra.EXTRA_DRM_LEVEL,
                        OmaDrmStore.DrmIntentExtra.LEVEL_SD);
            }
            /// @}
            if (localFilesOnly) {
                innerIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
            }

            Intent wrapperIntent = Intent.createChooser(innerIntent, null);
            ((Activity) context).startActivityForResult(wrapperIntent, requestCode);
        }
    }

    /*
     * Other APPS cannot access MMS file because of security in N version. Files should be
     * put in FileProvider then pass the Uri to other APPS to read.
     */
    public static File createTempFileExposed(Context context, Uri fromUri, String fileName) {
        MmsLog.i(TAG, "createTempFileExposed, file name: " + fileName + " Uri: " + fromUri);
        File file = null;
        try {
            File baseDir = context.getFilesDir();
            File folder = new File(baseDir.toString() + "/" + MMS_SHARED_FILES_FOLDER_NAME);
            if (!folder.exists()) {
                folder.mkdirs();
                folder.setExecutable(true, false);
            }
            file = new File(folder.toString() + "/" + TEMP_FILE_TAG + fileName);
            InputStream in = null;
            FileOutputStream out = null;
            try {
                in = context.getContentResolver().openInputStream(fromUri);
                out = new FileOutputStream(file);
                byte[] buf = new byte[8096];
                int seg = 0;
                while ((seg = in.read(buf)) != -1) {
                    out.write(buf, 0, seg);
                }
            } finally {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            }
        } catch (FileNotFoundException e) {
            MmsLog.e(TAG, "createTempFileExposed, file not found " + fromUri + ", exception ", e);
        } catch (IOException e) {
            MmsLog.e(TAG, "createTempFileExposed, ioexception " + fromUri + ", exception ", e);
        }
        return file;
    }

    /*
     * Delete all shared files
     */
    public static void deleteAllSharedFiles(Context context, String tag) {
        String fileDir = context.getFilesDir().toString() + "/" + MMS_SHARED_FILES_FOLDER_NAME;
        File[] allFiles = new File(fileDir).listFiles();
        if (allFiles == null) {
            return;
        }

        for (int i = 0; i < allFiles.length; i++) {
            File file = allFiles[i];
            if (file.getName().contains(tag)) {
                MmsLog.d(TAG, "getPreviewFileUri, delete old preview file: " + file.getPath());
                file.delete();
            }
        }
    }

    public static Uri getPreviewFileUri(Context context, MediaModel model) {
        deleteAllSharedFiles(context, TEMP_FILE_TAG);
        final File tempPreviewFile = createTempFileExposed(context, model.getUri(), model.getSrc());
        if (tempPreviewFile == null || !tempPreviewFile.exists() || tempPreviewFile.length() <= 0) {
            MmsLog.e(TAG, "getPreviewFileUri, file is not exists or empty " + tempPreviewFile);
            return null;
        }
        return FileProvider.getUriForFile(context, MMS_SHARED_FILE_PROVIDER_AUTHORITIES, tempPreviewFile);
    }

    public static void viewSimpleSlideshow(Context context, SlideshowModel slideshow) {
        if (!slideshow.isSimple()) {
            throw new IllegalArgumentException("viewSimpleSlideshow() called on a non-simple slideshow");
        }
        SlideModel slide = slideshow.get(0);
        MediaModel mm = null;
        if (slide.hasImage()) {
            mm = slide.getImage();
        } else if (slide.hasVideo()) {
            mm = slide.getVideo();
        } else if (slide.hasAudio()) {
            mm = slide.getAudio();
        }

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.putExtra("SingleItemOnly", true); // So we don't see "surrounding" images in Gallery
        /// M: CanShare: false, Hide the videopalye's share option menu.
        /// M: CanShare: ture, Show the share option menu.
        /// M: Code analyze 010, For fix bug ALPS00244046, The "JE" pops up
        // after you tap the "messaging" icon. @{
        intent.putExtra("CanShare", false);
        /// @}
        /// M: for showing notification when view mms with video player in full screen model @{
        intent.putExtra(EXTRA_FULLSCREEN_NOTIFICATION, true);
        /// @}

        String contentType = "";
        if (mm != null) {
            contentType = mm.getContentType();
            MmsLog.e(TAG, "viewSimpleSildeshow. Uri:" + mm.getUri());
            MmsLog.e(TAG, "viewSimpleSildeshow. contentType:" + contentType);
            intent.setDataAndType(getPreviewFileUri(context, mm), contentType);
        }
        /// M: Code analyze 013, For fix bug ALPS00250939, Exception/Java(JE)-->com.android.mms.
        try {
            // M: change feature ALPS01751464
            if (mm != null && mm.hasDrmContent()) {
                DrmUtilsEx.showDrmAlertDialog(context);
                return;
            }

            context.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Message msg = Message.obtain(MmsApp.getToastHandler());
            msg.what = MmsApp.MSG_MMS_CAN_NOT_OPEN;
            msg.obj = contentType;
            msg.sendToTarget();
            /// M: after user click view, and the error toast is shown,
            /// we must make it can press again. tricky code
            if (context instanceof ComposeMessageActivity) {
                ((ComposeMessageActivity) context).mClickCanResponse = true;
            }
        }
        /// @}
    }

    /// M: Code analyze 025, For fix bug ALPS00298363, The "JE" pops up
    // after you launch message again. @{
    public static void showErrorDialog(Activity activity, int titleId, int messageId, int mediaTypeIdForTitle,
            int mediaTypeIdForMsg) {
        /** M:
        the original code is replaced by the following code.
        the original code has a bug, JE may happen.
        the case is when the activity is destoried by some reason(ex:change language),
        when dismiss the dialog, it may be throw a JE. see 298363.
        */
        ErrorDialog errDialog = ErrorDialog.newInstance(titleId, messageId, mediaTypeIdForTitle, mediaTypeIdForMsg);
        try {
            errDialog.show(activity.getFragmentManager(), "errDialog");
        } catch (IllegalStateException e) {
            try {
                MmsLog.d(TAG, "showErrorDialog catch IllegalStateException." + e);
                FragmentTransaction transaction = activity.getFragmentManager().beginTransaction();
                transaction.add(errDialog, "errDialog");
                transaction.commitAllowingStateLoss();
            } catch (Exception e2) {
                MmsLog.e(TAG, "showErrorDialog commitAllowingStateLoss catch Exception." + e2);
            }
            return;
        }
    }
    /// @}

    /**
     * The quality parameter which is used to compress JPEG images.
     */
    public static final int IMAGE_COMPRESSION_QUALITY = 95;
    /**
     * The minimum quality parameter which is used to compress JPEG images.
     */
    /// M: Modify for ALPS00778930
    public static final int MINIMUM_IMAGE_COMPRESSION_QUALITY = 10;

    public static final int MAX_COMPRESS_TIMES = 8;

    /**
     * Message overhead that reduces the maximum image byte size.
     * 5000 is a realistic overhead number that allows for user to also include
     * a small MIDI file or a couple pages of text along with the picture.
     */
    public static final int MESSAGE_OVERHEAD = 5000;

    public static void resizeImageAsync(final Context context, final Uri imageUri, final Handler handler,
            final ResizeImageResultCallback cb, final boolean append) {

        // Show a progress toast if the resize hasn't finished
        // within one second.
        // Stash the runnable for showing it away so we can cancel
        // it later if the resize completes ahead of the deadline.
        final Runnable showProgress = new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, R.string.compressing, Toast.LENGTH_SHORT).show();
            }
        };
        // Schedule it for one second from now.
        handler.postDelayed(showProgress, 1000);

        new Thread(new Runnable() {
            @Override
            public void run() {
                final PduPart part;
                try {
                    UriImage image = new UriImage(context, imageUri);
                    int widthLimit = MmsConfig.getMaxImageWidth();
                    int heightLimit = MmsConfig.getMaxImageHeight();
                    // In mms_config.xml, the max width always been declared larger than the max
                    // height. Swap the width and height limits if necessary so scale the picture
                    // as little as possible.
                    if (image.getHeight() > image.getWidth()) {
                        int temp = widthLimit;
                        widthLimit = heightLimit;
                        heightLimit = temp;
                    }

                    // / M: Code analyze 027, new feature, to improve the
                    // performance of Mms. @{
                    /*part = image.getResizedImageAsPart(
                    widthLimit,
                    heightLimit,
                    MmsConfig.getMaxMessageSize() - MESSAGE_OVERHEAD);
                    */
                    part = image.getResizedImageAsPart(widthLimit, heightLimit,
                            MmsConfig.getUserSetMmsSizeLimit(true) - MESSAGE_OVERHEAD);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            cb.onResizeResult(part, append);
                        }
                    });
                } catch (IllegalArgumentException e) {
                    MmsLog.e(TAG, "Unexpected IllegalArgumentException.", e);
                } finally {
                    /// M: Cancel pending show of the progress toast if necessary.
                    handler.removeCallbacks(showProgress);
                }
                /// @}
            }
        }, "MessageUtils.resizeImageAsync").start();
    }

    /// M: Code analyze 003, For fix bug ALPS00113244, It adds a blank slide
    // when share multi folders from Gallery. @{
    public static void resizeImage(final Context context, final Uri imageUri, final Handler handler,
            final ResizeImageResultCallback cb, final boolean append, boolean showToast) {

        /** M: Show a progress toast if the resize hasn't finished
         * within one second.
         * Stash the runnable for showing it away so we can cancel
         * it later if the resize completes ahead of the deadline.
        */
        final Runnable showProgress = new Runnable() {
            public void run() {
                Toast.makeText(context, R.string.compressing, Toast.LENGTH_SHORT).show();
            }
        };
        if (showToast) {
            handler.post(showProgress);
            //          handler.postDelayed(showProgress, 1000);
        }
        final PduPart part;
        try {
            UriImage image = new UriImage(context, imageUri);
            part = image.getResizedImageAsPart(MmsConfig.getMaxImageWidth(), MmsConfig.getMaxImageHeight(),
                    MmsConfig.getUserSetMmsSizeLimit(true) - MESSAGE_OVERHEAD);
            cb.onResizeResult(part, append);
        } catch (IllegalArgumentException e) {
            MmsLog.e(TAG, "Unexpected IllegalArgumentException.", e);
        } finally {
            /// M: Cancel pending show of the progress toast if necessary.
            handler.removeCallbacks(showProgress);
        }
    }

    /// @}
    public static void showDiscardDraftConfirmDialog(Context context, OnClickListener listener) {
        new AlertDialog.Builder(context)
                /// M: Code analyze 008, new feature, Android4.1 has moved this
                // icon and title. @{
                .setIconAttribute(android.R.attr.alertDialogIcon).setTitle(R.string.discard_message)
                /// @}
                .setMessage(R.string.discard_message_reason).setPositiveButton(R.string.yes, listener)
                .setNegativeButton(R.string.no, null).show();
    }

    /// M: we[mtk] do not support reply read report when deleteing without read.
    public static void handleReadReport(final Context context, final Collection<Long> threadIds, final int status,
            final Runnable callback) {
        //        StringBuilder selectionBuilder = new StringBuilder(Mms.MESSAGE_TYPE + " = "
        //                + PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF
        //                + " AND " + Mms.READ + " = 0"
        //                + " AND " + Mms.READ_REPORT + " = " + PduHeaders.VALUE_YES);
        //
        //        String[] selectionArgs = null;
        //        if (threadIds != null) {
        //            String threadIdSelection = null;
        //            StringBuilder buf = new StringBuilder();
        //            selectionArgs = new String[threadIds.size()];
        //            int i = 0;
        //
        //            for (long threadId : threadIds) {
        //                if (i > 0) {
        //                    buf.append(" OR ");
        //                }
        //                buf.append(Mms.THREAD_ID).append("=?");
        //                selectionArgs[i++] = Long.toString(threadId);
        //            }
        //            threadIdSelection = buf.toString();
        //
        //            selectionBuilder.append(" AND (" + threadIdSelection + ")");
        //        }
        //
        //        final Cursor c = SqliteWrapper.query(context, context.getContentResolver(),
        //                        Mms.Inbox.CONTENT_URI, new String[] {Mms._ID, Mms.MESSAGE_ID},
        //                        selectionBuilder.toString(), selectionArgs, null);
        //
        //        if (c == null) {
        //            return;
        //        }
        //
        //        final Map<String, String> map = new HashMap<String, String>();
        //        try {
        //            if (c.getCount() == 0) {
        //                if (callback != null) {
        //                    callback.run();
        //                }
        //                return;
        //            }
        //
        //            while (c.moveToNext()) {
        //                Uri uri = ContentUris.withAppendedId(Mms.CONTENT_URI, c.getLong(0));
        //                map.put(c.getString(1), AddressUtils.getFrom(context, uri));
        //            }
        //        } finally {
        //            c.close();
        //        }
        // we[mtk] do not support reply read report when deleteing without read.
        // the underlayer code[ReadRecTransaction.java] is modified to support another branch.
        if (callback != null) {
            callback.run();
        }
        /// M: Code analyze 016, For fix bug ALPS00274375, remove the entry of
        // sending read report when deleting mms without read. @{
        /** M:
        OnClickListener positiveListener = new OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            for (final Map.Entry<String, String> entry : map.entrySet()) {
                MmsMessageSender.sendReadRec(context, entry.getValue(),
                                             entry.getKey(), status);
            }
            
            if (callback != null) {
                callback.run();
            }
            dialog.dismiss();
        }
        };
            
        OnClickListener negativeListener = new OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            if (callback != null) {
                callback.run();
            }
            dialog.dismiss();
        }
        };
            
        OnCancelListener cancelListener = new OnCancelListener() {
        @Override
        public void onCancel(DialogInterface dialog) {
            if (callback != null) {
                callback.run();
            }
            dialog.dismiss();
        }
        };
            
        confirmReadReportDialog(context, positiveListener,
                                     negativeListener,
                                     cancelListener);
        */
        /// @}
    }

    private static void confirmReadReportDialog(Context context, OnClickListener positiveListener,
            OnClickListener negativeListener, OnCancelListener cancelListener) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setCancelable(true);
        builder.setTitle(R.string.confirm);
        builder.setMessage(R.string.message_send_read_report);
        builder.setPositiveButton(R.string.yes, positiveListener);
        builder.setNegativeButton(R.string.no, negativeListener);
        builder.setOnCancelListener(cancelListener);
        builder.show();
    }

    public static String extractEncStrFromCursor(Cursor cursor, int columnRawBytes, int columnCharset) {
        String rawBytes = cursor.getString(columnRawBytes);
        int charset = cursor.getInt(columnCharset);

        if (TextUtils.isEmpty(rawBytes)) {
            return "";
        } else if (charset == CharacterSets.ANY_CHARSET) {
            return rawBytes;
        } else {
            return new EncodedStringValue(charset, PduPersister.getBytes(rawBytes)).getString();
        }
    }

    private static String extractEncStr(Context context, EncodedStringValue value) {
        if (value != null) {
            return value.getString();
        } else {
            Log.d(TAG, "extractEncStr EncodedStringValue is null");
            return "";
        }
    }

    public static ArrayList<String> extractUris(URLSpan[] spans) {
        int size = spans.length;
        ArrayList<String> accumulator = new ArrayList<String>();

        for (int i = 0; i < size; i++) {
            // / M: Code analyze 027, new feature, to improve the performance of
            // Mms. @{
            if (!accumulator.contains(spans[i].getURL())) {
                accumulator.add(spans[i].getURL());
            }
            /// @
        }
        return accumulator;
    }

    /**
     * Play/view the message attachments.
     * TOOD: We need to save the draft before launching another activity to view the attachments.
     *       This is hacky though since we will do saveDraft twice and slow down the UI.
     *       We should pass the slideshow in intent extra to the view activity instead of
     *       asking it to read attachments from database.
     * @param activity
     * @param msgUri the MMS message URI in database
     * @param slideshow the slideshow to save
     * @param persister the PDU persister for updating the database
     * @param sendReq the SendReq for updating the database
     */
    public static void viewMmsMessageAttachment(Activity activity, Uri msgUri, SlideshowModel slideshow,
            AsyncDialog asyncDialog) {
        viewMmsMessageAttachment(activity, msgUri, slideshow, 0, asyncDialog);
    }

    public static void viewMmsMessageAttachment(final Activity activity, final Uri msgUri,
            final SlideshowModel slideshow, final int requestCode, AsyncDialog asyncDialog) {
        /// M: Code analyze 002, For fix bug ALPS00112553, system-server JE
        // happens and MS reboot when tap play in MMS. @{
        final boolean isSimple = (slideshow == null) ? false : slideshow.isSimple();

        if (isSimple) {
            SlideModel slideTemp = slideshow.get(0);
            // In attachment-editor mode, we only ever have one slide.
            /// M: fix bug ALPS00393187, play in gellay when simple slide only has picture or video
            if (slideTemp != null && !slideTemp.hasAudio()
                    && (!slideTemp.hasText() || slideTemp.getText().getText().length() == 0)) {
                MessageUtils.viewSimpleSlideshow(activity, slideshow);
                return;
            }
        }
        /// @}
        // M: change feature ALPS01751464
        if (isSimple) {
            SlideModel slideOne = slideshow.get(0);
            if (slideOne != null && slideOne.hasAudio()) {
                MediaModel model = slideOne.getAudio();
                if (model != null && model.hasDrmContent()) {
                    DrmUtilsEx.showDrmAlertDialog(activity);
                    return;
                }
            }
        }

        // The user wants to view the slideshow. We have to persist the slideshow parts
        // in a background task. If the task takes longer than a half second, a progress dialog
        // is displayed. Once the PDU persisting is done, another runnable on the UI thread get
        // executed to start the SlideshowActivity.
        asyncDialog.runAsync(new Runnable() {
            @Override
            public void run() {
                // If a slideshow was provided, save it to disk first.
                if (slideshow != null) {
                    PduPersister persister = PduPersister.getPduPersister(activity);
                    try {
                        PduBody pb = slideshow.toPduBody();
                        MessageUtils.updatePartsIfNeeded(slideshow, persister, msgUri, pb, null);
                        //persister.updateParts(msgUri, pb, null);
                        slideshow.sync(pb);
                    } catch (MmsException e) {
                        Log.e(TAG, "Unable to save message for preview");
                        return;
                    }
                    slide = slideshow.get(0);
                }
            }
        }, new Runnable() {
            @Override
            public void run() {
                // Launch the slideshow activity to play/view.
                Intent intent;
                if ((isSimple && slide.hasAudio()) || (requestCode == AttachmentEditor.MSG_PLAY_AUDIO)) {
                    intent = new Intent(activity.getApplicationContext(), SlideshowActivity.class);
                } else {
                    intent = new Intent(activity.getApplicationContext(), MmsPlayerActivity.class);
                }
                intent.setData(msgUri);
                if (requestCode > 0) {
                    activity.startActivityForResult(intent, requestCode);
                } else {
                    activity.startActivity(intent);
                }
                //                    // Once the above background thread is complete, this runnable is run
                //                    // on the UI thread to launch the slideshow activity.
                //                    launchSlideshowActivity(activity, msgUri, requestCode);

            }
        }, R.string.building_slideshow_title);

    }

    public static void launchSlideshowActivity(Context context, Uri msgUri, int requestCode) {
        // Launch the slideshow activity to play/view.
        Intent intent = new Intent(context, SlideshowActivity.class);
        intent.setData(msgUri);
        if (requestCode > 0 && context instanceof Activity) {
            ((Activity) context).startActivityForResult(intent, requestCode);
        } else {
            context.startActivity(intent);
        }
    }

    /**
     * Debugging
     */
    public static void writeHprofDataToFile() {
        String filename = Environment.getExternalStorageDirectory() + "/mms_oom_hprof_data";
        try {
            android.os.Debug.dumpHprofData(filename);
            Log.i(TAG, "##### written hprof data to " + filename);
        } catch (IOException ex) {
            Log.e(TAG, "writeHprofDataToFile: caught " + ex);
        }
    }

    // An alias (or commonly called "nickname") is:
    // Nickname must begin with a letter.
    // Only letters a-z, numbers 0-9, or . are allowed in Nickname field.
    public static boolean isAlias(String string) {
        if (!MmsConfig.isAliasEnabled()) {
            return false;
        }

        int len = string == null ? 0 : string.length();

        if (len < MmsConfig.getAliasMinChars() || len > MmsConfig.getAliasMaxChars()) {
            return false;
        }

        if (!Character.isLetter(string.charAt(0))) { // Nickname begins with a letter
            return false;
        }
        for (int i = 1; i < len; i++) {
            char c = string.charAt(i);
            if (!(Character.isLetterOrDigit(c) || c == '.')) {
                return false;
            }
        }

        return true;
    }

    /**
     * Given a phone number, return the string without syntactic sugar, meaning parens,
     * spaces, slashes, dots, dashes, etc. If the input string contains non-numeric
     * non-punctuation characters, return null.
     */
    private static String parsePhoneNumberForMms(String address) {
        StringBuilder builder = new StringBuilder();
        int len = address.length();

        for (int i = 0; i < len; i++) {
            char c = address.charAt(i);

            // accept the first '+' in the address
            if (c == '+' && builder.length() == 0) {
                builder.append(c);
                continue;
            }

            if (Character.isDigit(c)) {
                builder.append(c);
                continue;
            }

            if (numericSugarMap.get(c) == null) {
                return null;
            }
        }
        return builder.toString();
    }

    /**
     * Returns true if the address passed in is a valid MMS address.
     */
    public static boolean isValidMmsAddress(String address) {
        String retVal = parseMmsAddress(address);
        /// M: @{
        //return (retVal != null);
        return (retVal != null && !retVal.equals(""));
        /// @}
    }

    /**
     * parse the input address to be a valid MMS address.
     * - if the address is an email address, leave it as is.
     * - if the address can be parsed into a valid MMS phone number, return the parsed number.
     * - if the address is a compliant alias address, leave it as is.
     */
    public static String parseMmsAddress(String address) {
        // if it's a valid Email address, use that.
        if (Mms.isEmailAddress(address)) {
            return address;
        }

        // if we are able to parse the address to a MMS compliant phone number, take that.
        String retVal = parsePhoneNumberForMms(address);
        if (retVal != null) {
            return retVal;
        }

        // if it's an alias compliant address, use that.
        if (isAlias(address)) {
            return address;
        }

        // it's not a valid MMS address, return null
        return null;
    }

    private static void log(String msg) {
        Log.d(TAG, "[MsgUtils] " + msg);
    }

    /// M: @{
    public static Uri saveBitmapAsPart(Context context, Uri messageUri, Bitmap bitmap) throws MmsException {

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        bitmap.compress(CompressFormat.JPEG, IMAGE_COMPRESSION_QUALITY, os);

        PduPart part = new PduPart();

        part.setContentType("image/jpeg".getBytes());
        String contentId = "Image" + System.currentTimeMillis();
        part.setContentLocation((contentId + ".jpg").getBytes());
        part.setContentId(contentId.getBytes());
        part.setData(os.toByteArray());

        Uri retVal = PduPersister.getPduPersister(context).persistPart(part, ContentUris.parseId(messageUri), null);

        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
            log("saveBitmapAsPart: persisted part with uri=" + retVal);
        }

        return retVal;
    }

    public static String getLocalNumber() {
        return MmsApp.getApplication().getTelephonyManager().getLine1Number();
    }

    public static String getLocalNumber(int subId) {
        return MmsApp.getApplication().getTelephonyManager().getLine1Number(subId);
    }

    public static boolean isLocalNumber(String number) {
        if (number == null) {
            return false;
        }

        // we don't use Mms.isEmailAddress() because it is too strict for comparing addresses like
        // "foo+caf_=6505551212=tmomail.net@gmail.com",
        // which is the 'from' address from a forwarded email
        // message from Gmail. We don't want to treat "foo+caf
        // =6505551212=tmomail.net@gmail.com" and
        // "6505551212" to be the same.
        if (number.indexOf('@') >= 0) {
            return false;
        }

        List<SubscriptionInfo> subInfoList;
        subInfoList = SubscriptionManager.from(MmsApp.getApplication()).getActiveSubscriptionInfoList();
        if (subInfoList == null || subInfoList.size() == 0) {
            MmsLog.d(TAG, "isLocalNumber SIM not insert");
            return false;
        }
        for (SubscriptionInfo subInfoRecord : subInfoList) {
            // modify BUG_ID:JWYYL-16 chenweihua 20141216 (start)
            /*
            if (PhoneNumberUtils.compare(number, getLocalNumber(subInfoRecord.getSubscriptionId()))) {
               return true;
            }
            */
            if (android.os.SystemProperties.getInt("ro.rgk_brazil_number_match", 0) == 1) {
                if (PhoneNumberUtils.compareWithAreaCode(number,
                        getLocalNumber(subInfoRecord.getSubscriptionId()))) {
                    return true;
                }
            } else {
                if (PhoneNumberUtils.compare(number, getLocalNumber(subInfoRecord.getSubscriptionId()))) {
                    return true;
                }
            }
            // modify BUG_ID:JWYYL-16 chenweihua 20141216 (end)
        }
        return false;
    }

    public static void viewMmsMessageAttachmentMini(Context context, Uri msgUri, SlideshowModel slideshow) {
        if (msgUri == null) {
            return;
        }
        boolean isSimple = (slideshow == null) ? false : slideshow.isSimple();
        if (slideshow != null) {
            /// M: If a slideshow was provided, save it to disk first.
            PduPersister persister = PduPersister.getPduPersister(context);
            try {
                PduBody pb = slideshow.toPduBody();
                MessageUtils.updatePartsIfNeeded(slideshow, persister, msgUri, pb, null);
                //persister.updateParts(msgUri, pb, null);
                slideshow.sync(pb);
            } catch (MmsException e) {
                MmsLog.e(TAG, "Unable to save message for preview");
                return;
            }
        }

        /// M: Launch the slideshow activity to play/view.
        Intent intent;
        if (isSimple && (slideshow != null) && slideshow.get(0).hasAudio()) {
            intent = new Intent(context, SlideshowActivity.class);
        } else {
            intent = new Intent(context, MmsPlayerActivity.class);
        }
        intent.setData(msgUri);
        context.startActivity(intent);
    }

    /// M:wappush: add this function to handle the url string,
    /// if it does not contain the http or https schema, then add http schema manually.
    public static String checkAndModifyUrl(String url) {
        if (url == null) {
            return null;
        }

        Uri uri = Uri.parse(url);
        if (uri.getScheme() != null) {
            return url;
        }

        return "http://" + url;
    }

    public static void selectRingtone(Context context, int requestCode) {
        if (context instanceof Activity) {
            Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_INCLUDE_DRM, false);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_MORE_RINGTONES, false);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, context.getString(R.string.select_audio));
            if (FeatureOption.MTK_DRM_APP) {
                intent.putExtra(OmaDrmStore.DrmIntentExtra.EXTRA_DRM_LEVEL, OmaDrmStore.DrmIntentExtra.LEVEL_SD);
            }
            ((Activity) context).startActivityForResult(intent, requestCode);
        }
    }

    public static void setSubIconAndLabel(int subId, String subName, TextView subView) {
        Log.i(TAG, "setSubIconAndLabel subId=" + subId);
        int textColor = 0;
        if (subView == null) {
            return;
        }
        int activeSubCount = SubscriptionManager.from(MmsApp.getApplication()).getActiveSubscriptionInfoCount();

        if (subName == null && activeSubCount > 1) {
            SubscriptionInfo subInfo = SubscriptionManager.from(MmsApp.getApplication())
                    .getActiveSubscriptionInfo(subId);
            Log.d(TAG, "subInfo=" + subInfo);
            if (null != subInfo) {
                if ((subInfo.getSimSlotIndex() == SubscriptionManager.SIM_NOT_INSERTED)
                        || (subInfo.getSimSlotIndex() == SubscriptionManager.INVALID_SUBSCRIPTION_ID)) {
                    Log.i(TAG, "current not insert sim card");
                } else {
                    subName = subInfo.getDisplayName().toString();
                    textColor = subInfo.getIconTint();
                }
            } else {
                Log.i(TAG, "subInfo is null ");
            }
        }

        if (subName == null || activeSubCount <= 1) {
            subView.setVisibility(View.GONE);
        } else {
            subView.setVisibility(View.VISIBLE);
            subView.setTextColor(textColor);
            subView.setText(subName);
        }
    }

    public static void addNumberOrEmailtoContact(final String numberOrEmail, final int REQUEST_CODE,
            final Activity activity) {
        if (!TextUtils.isEmpty(numberOrEmail)) {
            String message = activity.getResources().getString(R.string.add_contact_dialog_message, numberOrEmail);
            AlertDialog.Builder builder = new AlertDialog.Builder(activity).setTitle(numberOrEmail)
                    .setMessage(message);
            AlertDialog dialog = builder.create();
            dialog.setButton(AlertDialog.BUTTON_POSITIVE,
                    activity.getResources().getString(R.string.add_contact_dialog_existing),
                    new DialogInterface.OnClickListener() {

                        public void onClick(DialogInterface dialog, int which) {
                            // TODO Auto-generated method stub
                            Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
                            intent.setType(Contacts.CONTENT_ITEM_TYPE);
                            if (Mms.isEmailAddress(numberOrEmail)) {
                                intent.putExtra(ContactsContract.Intents.Insert.EMAIL, numberOrEmail);
                            } else {
                                intent.putExtra(ContactsContract.Intents.Insert.PHONE, numberOrEmail);
                            }
                            if (REQUEST_CODE > 0) {
                                activity.startActivityForResult(intent, REQUEST_CODE);
                            } else {
                                activity.startActivity(intent);
                            }
                        }
                    });

            dialog.setButton(AlertDialog.BUTTON_NEGATIVE,
                    activity.getResources().getString(R.string.add_contact_dialog_new),
                    new DialogInterface.OnClickListener() {

                        public void onClick(DialogInterface dialog, int which) {
                            // TODO Auto-generated method stub
                            final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
                            if (Mms.isEmailAddress(numberOrEmail)) {
                                intent.putExtra(ContactsContract.Intents.Insert.EMAIL, numberOrEmail);
                            } else {
                                intent.putExtra(ContactsContract.Intents.Insert.PHONE, numberOrEmail);
                            }
                            if (REQUEST_CODE > 0) {
                                activity.startActivityForResult(intent, REQUEST_CODE);
                            } else {
                                activity.startActivity(intent);
                            }
                        }
                    });
            dialog.show();
        }
    }

    /** M:
     * Return the current storage status.
     */
    public static String getStorageStatus(Context context) {
        /// M: we need count only
        final String[] PROJECTION = new String[] { BaseColumns._ID, Mms.MESSAGE_SIZE };
        final ContentResolver cr = context.getContentResolver();
        final Resources res = context.getResources();
        Cursor cursor = null;

        StringBuilder buffer = new StringBuilder();
        // Mms count
        cursor = cr.query(Mms.CONTENT_URI, PROJECTION, null, null, null);
        int mmsCount = 0;
        if (cursor != null) {
            mmsCount = cursor.getCount();
        }
        buffer.append(res.getString(R.string.storage_dialog_mms, mmsCount));
        buffer.append("\n");
        //Mms size
        long size = 0;
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                do {
                    size += cursor.getInt(1);
                } while (cursor.moveToNext());
            }
            cursor.close();
        }
        buffer.append(res.getString(R.string.storage_dialog_mms_size) + getHumanReadableSize(size));
        buffer.append("\n");
        // Attachment size
        size = getAttachmentSize(context);
        Log.d(TAG, "mms attachment size = " + size);
        final String sizeTag = getHumanReadableSize(size);
        buffer.append(res.getString(R.string.storage_dialog_attachments) + sizeTag);
        buffer.append("\n");
        // Sms count
        cursor = cr.query(Sms.CONTENT_URI, PROJECTION, null, null, null);
        int smsCount = 0;
        if (cursor != null) {
            smsCount = cursor.getCount();
            cursor.close();
        }
        buffer.append(res.getString(R.string.storage_dialog_sms, smsCount));
        buffer.append("\n");
        // Database size
        final long dbsize = getDatabaseSize(context);
        buffer.append(res.getString(R.string.storage_dialog_database) + getHumanReadableSize(dbsize));
        buffer.append("\n");
        // Available space
        final StatFs datafs = new StatFs(Environment.getDataDirectory().getAbsolutePath());
        final long availableSpace = (long) datafs.getAvailableBlocks() * datafs.getBlockSize();
        buffer.append(
                res.getString(R.string.storage_dialog_available_space) + getHumanReadableSize(availableSpace));
        return buffer.toString();
    }

    /// M: Code analyze 017, For fix bug ALPS00275452, the size of Mms
    // attachment is 0B in the settings. @{
    private static long getAttachmentSize(Context context) {
        Uri uri = Uri.parse("content://mms/attachment_size");
        ContentValues insertValues = new ContentValues();
        uri = context.getContentResolver().insert(uri, insertValues);
        String size = uri.getQueryParameter("size");
        return Long.parseLong(size);
    }
    /// @}

    /// M: Get database size, for SELinux enhance, mms can not get the size directly,
    //  should get the size through phone process @{
    private static long getDatabaseSize(Context context) {
        Uri uri = Uri.parse("content://mms-sms/database_size");
        ContentValues insertValues = new ContentValues();
        uri = context.getContentResolver().insert(uri, insertValues);
        String size = uri.getQueryParameter("size");
        return Long.parseLong(size);
    }
    /// @}

    /// M: Code analyze 004, For fix bug ALPS00231349, add new feature vCard
    // support. @{
    public static String getHumanReadableSize(long size) {
        /// @}
        String tag;
        float fsize = (float) size;
        if (size < 1024L) {
            tag = String.valueOf(size) + "B";
        } else if (size < 1024L * 1024L) {
            fsize /= 1024.0f;
            tag = String.format(Locale.ENGLISH, "%.2f", fsize) + "KB";
        } else {
            fsize /= 1024.0f * 1024.0f;
            tag = String.format(Locale.ENGLISH, "%.2f", fsize) + "MB";
        }
        return tag;
    }

    /// M: Code analyze 001, For fix bug ALPS00101270, The address is displayed
    // as contact in the unsent message view,but the add contact icon still
    // displayed in the view. @{
    public static boolean canAddToContacts(Contact contact) {
        // There are some kind of automated messages, like STK messages, that we don't want
        // to add to contacts. These names begin with special characters, like, "*Info".
        final String name = contact.getName();
        if (!TextUtils.isEmpty(contact.getNumber())) {
            char c = contact.getNumber().charAt(0);
            if (isSpecialChar(c)) {
                return false;
            }
        }
        if (!TextUtils.isEmpty(name)) {
            char c = name.charAt(0);
            if (isSpecialChar(c)) {
                return false;
            }
        }
        if (!(Mms.isEmailAddress(name) || (Mms.isPhoneNumber(name) || isPhoneNumber(name))
                || MessageUtils.isLocalNumber(contact.getNumber()))) { // Handle "Me"
            return false;
        }
        return true;
    }

    private static boolean isPhoneNumber(String num) {
        num = num.trim();
        if (TextUtils.isEmpty(num)) {
            return false;
        }
        final char[] digits = num.toCharArray();
        for (char c : digits) {
            if (!Character.isDigit(c)) {
                return false;
            }
        }
        return true;
    }

    private static boolean isSpecialChar(char c) {
        return c == '*' || c == '%' || c == '$';
    }
    /// @}

    /// M: Code analyze 005, For fix bug ALPS00120575, add for cmcc dir ui. @{
    public static void replyMessage(int subId, Context context, String address) {
        Intent intent = new Intent();
        intent.putExtra("address", address);
        intent.putExtra("showinput", true);
        /// M: fix alps01858266. this put sub id here. @{
        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
        /// @}
        intent.setClassName(context, "com.android.mms.ui.ComposeMessageActivity");
        context.startActivity(intent);
    }

    public static void confirmDeleteMessage(final Activity activity, final Uri msgUri) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle(R.string.confirm_dialog_title);
        builder.setIconAttribute(android.R.attr.alertDialogIcon);
        builder.setCancelable(true);
        builder.setMessage(R.string.confirm_delete_message);
        builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                /// M: fix bug ALPS00351620; for requery searchactivity.
                SearchActivity.setNeedRequery();
                SqliteWrapper.delete(activity.getApplicationContext(), activity.getContentResolver(), msgUri, null,
                        null);
                dialog.dismiss();
                activity.finish();
            }
        });
        builder.setNegativeButton(R.string.no, null);
        builder.show();
    }

    public static String getMmsDetail(Context context, Uri uri, int size, int msgBox) {
        Resources res = context.getResources();
        MultimediaMessagePdu msg;
        try {
            msg = (MultimediaMessagePdu) PduPersister.getPduPersister(context).load(uri);
        } catch (MmsException e) {
            Log.e(TAG, "Failed to load the message: " + uri, e);
            return res.getString(R.string.cannot_get_details);
        }

        StringBuilder details = new StringBuilder();
        // Message Type: Text message.
        initializeMsgDetails(context, details, res, msg);

        // SentDate: ***
        if (msg.getDateSent() > 0 && msgBox == Mms.MESSAGE_BOX_INBOX) {
            details.append('\n');
            details.append(res.getString(R.string.sent_label));
            details.append(MessageUtils.formatTimeStampString(context, msg.getDateSent() * 1000L, true));
        }

        // Date: ***
        details.append('\n');
        if (msgBox == Mms.MESSAGE_BOX_DRAFTS) {
            details.append(res.getString(R.string.saved_label));
        } else if (msgBox == Mms.MESSAGE_BOX_INBOX) {
            details.append(res.getString(R.string.received_label));
        } else {
            details.append(res.getString(R.string.sent_label));
        }

        details.append(MessageUtils.formatTimeStampString(context, msg.getDate() * 1000L, true));

        // Subject: ***
        details.append('\n');
        details.append(res.getString(R.string.subject_label));

        EncodedStringValue subject = msg.getSubject();
        if (subject != null) {
            String subStr = subject.getString();
            // Message size already include size of subject.
            //            size += subStr.length();
            details.append(subStr);
        }
        // Priority: High/Normal/Low
        return formatDetails(details, context, msg, size, res);
    }

    private static String formatDetails(StringBuilder details, Context context, MultimediaMessagePdu msg, int size,
            Resources res) {
        details.append('\n');
        details.append(res.getString(R.string.priority_label));
        details.append(getPriorityDescription(context, msg.getPriority()));

        // Message size: *** KB
        details.append('\n');
        details.append(res.getString(R.string.message_size_label));
        details.append((size - 1) / 1024 + 1);
        details.append(res.getString(R.string.kilobyte));

        return details.toString();
    }

    public static void updateNotification(final Context context) {
        new Thread(new Runnable() {
            public void run() {
                MessagingNotification.nonBlockingUpdateSendFailedNotification(context);
                MessagingNotification.updateDownloadFailedNotification(context);
                CbMessagingNotification.updateNewMessageIndicator(context);
            }
        }).start();
    }
    /// @}

    /// M: Code analyze 006, For fix bug ALPS00234739, draft can't be saved
    // after share the edited picture to the same ricipient.Remove old Mms draft
    // in conversation list instead of compose view. @{
    public static void addRemoveOldMmsThread(Runnable r) {
        mRemoveOldMmsThread = new Thread(r);
    }

    public static void asyncDeleteOldMms() {
        if (mRemoveOldMmsThread != null) {
            mRemoveOldMmsThread.start();
            mRemoveOldMmsThread = null;
        }
    }

    /// @}

    /// M: Code analyze 015, new feature, Unread message number of Mms, Phone,
    // Email and Calendar display in Launcher. @{
    private static int getUnreadMessageNumber(Context context) {
        Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
                Uri.parse("content://mms-sms/unread_count"), null, null, null, null);
        if (cursor != null) {
            try {
                if (cursor.moveToFirst()) {
                    int count = cursor.getInt(0);
                    MmsLog.d(MmsApp.TXN_TAG, "unread message count: " + count);
                    return count;

                }
            } finally {
                cursor.close();
            }
        } else {
            MmsLog.d(MmsApp.TXN_TAG, "can not get unread message count.");
        }
        return 0;
    }

    /// @}

    /// M: Code analyze 012, For fix bug ALPS00245433, "Suggested" does not
    // show in select Sub dialog if te recipient number associates with Sub
    // cards. @{
    public static String detectCountry() {
        try {
            CountryDetector detector = (CountryDetector) MmsApp.getApplication()
                    .getSystemService(Context.COUNTRY_DETECTOR);
            final Country country = detector.detectCountry();
            if (country != null) {
                return country.getCountryIso();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /// @}

    /// M: Code analyze 023, For fix bug ALPS00284546, The video can not be
    // added and there is no prompt. @{
    public static String formatNumber(String number, Context context) {
        String countryCode = detectCountry();
        AsYouTypeFormatter mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
        char[] cha = number.toCharArray();
        int ii = cha.length;
        for (int num = 0; num < ii; num++) {
            number = mFormatter.inputDigit(cha[num]);
        }
        return number;
    }

    /// @}

    /// M: Code analyze 014, new feature, add new feature vCalendar @{
    public static boolean isVCalendarAvailable(Context context) {
        final Intent intent = new Intent("android.intent.action.CALENDARCHOICE");
        intent.setType("text/x-vcalendar");
        final PackageManager packageManager = context.getPackageManager();
        List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }
    /// @}

    /** M: Code analyze 019, For fix bug ALPS00278013, The name of the image is
     * changed to number and the suffix also disappeared when received in the email.
     * Get an unique name . It is different with the existed names.
     *
     * @param names
     * @param fileName
     * @return
     */
    public static String getUniqueName(String names[], String fileName) {
        if (names == null || names.length == 0) {
            return fileName;
        }
        int mIndex = 0;
        String tempName = "";
        String finalName = fileName;
        String extendion = "";
        String fileNamePrefix = "";
        int fileCount = 0;
        while (mIndex < names.length) {
            tempName = names[mIndex];
            if (tempName != null && tempName.equals(finalName)) {
                fileCount++;
                int tempInt = fileName.lastIndexOf(".");
                if (tempInt == -1) {
                    extendion = "";
                    fileNamePrefix = fileName;
                    finalName = fileNamePrefix + "(" + fileCount + ")" + extendion;
                } else {
                    extendion = fileName.substring(tempInt, fileName.length());
                    fileNamePrefix = fileName.substring(0, tempInt);
                    finalName = fileNamePrefix + "(" + fileCount + ")" + extendion;
                }
                mIndex = 0;
            } else {
                mIndex++;
            }
        }
        return finalName;
    }

    /** M: Code analyze 021, For fix bug ALPS00279524, The "JE" about "MMS"
     * pops up after we launch "Messaging" again.
     *
     * @param bitmap
     * @param maxWidth
     * @param maxHeight
     * @return
     */
    public static Bitmap getResizedBitmap(Bitmap bitmap, int maxWidth, int maxHeight) {
        if (bitmap == null) {
            return bitmap;
        }
        int originWidth = bitmap.getWidth();
        int originHeight = bitmap.getHeight();

        if (originWidth < maxWidth && originHeight < maxHeight) {
            return bitmap;
        }

        int width = originWidth;
        int height = originHeight;

        if (originWidth > maxWidth) {
            width = maxWidth;
            double i = originWidth * 1.0 / maxWidth;
            height = (int) Math.floor(originHeight / i);
            Bitmap mBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);
            return mBitmap;
        }

        if (originHeight > maxHeight) {
            height = maxHeight;
            double i = originHeight * 1.0 / maxHeight;
            width = (int) Math.floor(originWidth / i);
            Bitmap mBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);
            return mBitmap;
        }

        return bitmap;
    }

    /** M: Code analyze 022, For fix bug ALPS00281094, Can not send and receive Sms.
     * Return true iff the network portion of <code>address</code> is,
     * as far as we can tell on the device, suitable for use as an SMS
     * destination address.
     */
    public static boolean isWellFormedSmsAddress(String address) {
        // MTK-START [mtk04070][120104][ALPS00109412]Solve
        // "can't send MMS with MSISDN in international format"
        // Merge from ALPS00089029
        if (!isDialable(address)) {
            return false;
        }
        // MTK-END [mtk04070][120104][ALPS00109412]Solve
        // "can't send MMS with MSISDN in international format"

        String networkPortion = PhoneNumberUtils.extractNetworkPortion(address);

        return (!(networkPortion.equals("+") || TextUtils.isEmpty(networkPortion))) && isDialable(networkPortion);
    }

    private static boolean isDialable(String address) {
        for (int i = 0, count = address.length(); i < count; i++) {
            if (!isDialable(address.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    /** M: True if c is ISO-LATIN characters 0-9, *, # , +, WILD  */
    private static boolean isDialable(char c) {
        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N' || c == '(' || c == ')';
    }

    /// @}

    /// M: Code analyze 024, For fix bug ALPS00296775, attachments in slideshow
    // can be saved. Network service Provider will change the file name whick
    // contains unusual characters. @{
    public static String getContentType(String contentType, String fileName) {
        String finalContentType = "";
        if (contentType == null) {
            return contentType;
        }
        if (contentType.equalsIgnoreCase("application/oct-stream")
                || contentType.equalsIgnoreCase("application/octet-stream")) {
            if (fileName != null) {
                String suffix = fileName.contains(".")
                        ? fileName.substring(fileName.lastIndexOf("."), fileName.length())
                        : "";
                /// M: fix bug ALPS00427229, Ignore suffix Case
                if (suffix.equals("")) {
                    return contentType;
                } else if (suffix.equalsIgnoreCase(".bmp")) {
                    finalContentType = MmsContentType.IMAGE_BMP;
                } else if (suffix.equalsIgnoreCase(".jpg")) {
                    finalContentType = MmsContentType.IMAGE_JPG;
                } else if (suffix.equalsIgnoreCase(".wbmp")) {
                    finalContentType = MmsContentType.IMAGE_WBMP;
                } else if (suffix.equalsIgnoreCase(".gif")) {
                    finalContentType = MmsContentType.IMAGE_GIF;
                } else if (suffix.equalsIgnoreCase(".png")) {
                    finalContentType = MmsContentType.IMAGE_PNG;
                } else if (suffix.equalsIgnoreCase(".jpeg")) {
                    finalContentType = MmsContentType.IMAGE_JPEG;
                } else if (suffix.equalsIgnoreCase(".vcs")) {
                    finalContentType = MmsContentType.TEXT_VCALENDAR;
                } else if (suffix.equalsIgnoreCase(".vcf")) {
                    finalContentType = MmsContentType.TEXT_VCARD;
                } else if (suffix.equalsIgnoreCase(".imy")) {
                    finalContentType = MmsContentType.AUDIO_IMELODY;
                    // M: fix bug ALPS00355917
                } else if (suffix.equalsIgnoreCase(".ogg")) {
                    finalContentType = MmsContentType.AUDIO_OGG;
                } else if (suffix.equalsIgnoreCase(".aac")) {
                    finalContentType = MmsContentType.AUDIO_AAC;
                } else if (suffix.equalsIgnoreCase(".mp2")) {
                    finalContentType = MmsContentType.AUDIO_MPEG;
                    /// M: fix bug ALPS00444328, 3gp audio contentType will be modified
                    /// when CMCC send to CU
                } else if (suffix.equalsIgnoreCase(".3gp")) {
                    finalContentType = MmsContentType.AUDIO_3GPP;
                } else {
                    String extension = fileName.contains(".")
                            ? fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length())
                            : "";
                    finalContentType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
                    if (finalContentType == null) {
                        return contentType;
                    }
                }
                return finalContentType;
            }
        }
        return contentType;
    }

    /// @}
    public static float getPreferenceValueFloat(Context context, String key, float defaultValue) {
        SharedPreferences sp = context.getSharedPreferences("com.android.mms_preferences",
                Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
        return sp.getFloat(key, defaultValue);
    }

    /// M: Code analyze 025, For fix bug ALPS00298363, The "JE" pops up after
    // you launch message again. @{
    public static long getAvailableBytesInFileSystemAtGivenRoot(String rootFilePath) {
        StatFs stat = new StatFs(rootFilePath);
        //        final long totalBlocks = stat.getBlockCount();
        // put a bit of margin (in case creating the file grows the system by a few blocks)
        final long availableBlocks = stat.getAvailableBlocks() - 128;

        // long mTotalSize = totalBlocks * stat.getBlockSize();
        long mAvailSize = availableBlocks * stat.getBlockSize();

        Log.i(TAG, "getAvailableBytesInFileSystemAtGivenRoot(): "
                + "available space (in bytes) in filesystem rooted at: " + rootFilePath + " is: " + mAvailSize);
        return mAvailSize;
    }
    /// @}

    /** M: 4.1 has removed this function
    public static void viewMmsMessageAttachment(Context context, WorkingMessage msg,
        int requestCode) {
    SlideshowModel slideshow = msg.getSlideshow();
    if (slideshow == null) {
        throw new IllegalStateException("msg.getSlideshow() == null");
    }
        
    SlideModel slide = slideshow.get(0);
    if (slideshow.isSimple() && slide!=null && !slide.hasAudio()) {
        MessageUtils.viewSimpleSlideshow(context, slideshow);
    } else {
        Uri uri = msg.saveAsMms(false);
        if (uri != null) {
            // Pass null for the slideshow paramater, otherwise viewMmsMessageAttachment
            // will persist the slideshow to disk again (we just did that above in saveAsMms)
            viewMmsMessageAttachment(context, uri, null, requestCode);
        }
    }
    }*/

    /** M: Move this functiom from compose activity to this class.*/
    public static boolean isRestrictedType(Context context, long msgId) {
        PduBody body = PduBodyCache.getPduBody(context, ContentUris.withAppendedId(Mms.CONTENT_URI, msgId));
        if (body == null) {
            return false;
        }

        int partNum = body.getPartsNum();
        for (int i = 0; i < partNum; i++) {
            PduPart part = body.getPart(i);
            int width = 0;
            int height = 0;
            String type = new String(part.getContentType());

            int mediaTypeStringId;
            if (MmsContentType.isVideoType(type)) {
                mediaTypeStringId = R.string.type_video;
            } else if (MmsContentType.isAudioType(type) || "application/ogg".equalsIgnoreCase(type)) {
                mediaTypeStringId = R.string.type_audio;
            } else if (MmsContentType.isImageType(type)) {
                mediaTypeStringId = R.string.type_picture;
                InputStream input = null;
                try {
                    input = context.getContentResolver().openInputStream(part.getDataUri());
                    BitmapFactory.Options opt = new BitmapFactory.Options();
                    opt.inJustDecodeBounds = true;
                    BitmapFactory.decodeStream(input, null, opt);
                    width = opt.outWidth;
                    height = opt.outHeight;
                } catch (FileNotFoundException e) {
                    // Ignore
                    MmsLog.e(TAG, "FileNotFoundException caught while opening stream", e);
                } finally {
                    if (null != input) {
                        try {
                            input.close();
                        } catch (IOException e) {
                            // Ignore
                            MmsLog.e(TAG, "IOException caught while closing stream", e);
                        }
                    }
                }
            } else {
                continue;
            }
            if (!MmsContentType.isUnrestrictedType(type) || width > MmsConfig.getMaxRestrictedImageWidth()
                    || height > MmsConfig.getMaxRestrictedImageHeight()) {
                if (WorkingMessage.sCreationMode == WorkingMessage.RESTRICTED_TYPE) {
                    Resources res = context.getResources();
                    MessageUtils.showErrorDialog((Activity) context, R.string.unsupported_media_format,
                            R.string.select_different_media, mediaTypeStringId, mediaTypeStringId);
                }
                return true;
            }
        }
        return false;
    }

    /// M: new feature, SD card exist or not
    public static boolean existingSD(Context context, boolean isExternal) {
        StorageManager mStorageMamatger;
        mStorageMamatger = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        StorageVolume[] volumes = mStorageMamatger.getVolumeList();
        if (volumes == null) {
            return false;
        }
        String mountPoint = null;
        for (int i = 0; i < volumes.length; i++) {
            if (isExternal) {
                if (volumes[i].isRemovable()) {
                    mountPoint = volumes[i].getPath();
                    break;
                }
            } else {
                if (!volumes[i].isRemovable()) {
                    mountPoint = volumes[i].getPath();
                    break;
                }
            }
        }
        if (mountPoint == null) {
            return false;
        }
        String volumeState = mStorageMamatger.getVolumeState(mountPoint);
        return (volumeState != null && volumeState.equals(Environment.MEDIA_MOUNTED));
    }
    /// @}

    public static Intent createIntentByThreadId(Context context, long threadId, int type) {
        Intent intent = null;
        if (Telephony.Threads.CELL_BROADCAST_THREAD == type) {
            intent = CbMessageListActivity.createIntent(context, threadId);
            intent.putExtra("bFromLaunch", true);
        } else {
            intent = new Intent(context, ComposeMessageActivity.class);
            intent.putExtra("finish", true);
            if (threadId > 0) {
                intent.setAction(Intent.ACTION_SENDTO);
                intent.setData(Uri.parse("smsto:" + TextUtils.join(",",
                        Conversation.get(context, threadId, true).getRecipients().getNumbers())));
            }
        }
        return intent;
    }
    /// @}

    /**
     * M: copy file from srcFile to destFile.
     *
     * @param srcFile
     * @param destFile
     * @return
     */
    public static boolean copyFile(String srcFile, String destFile) {
        InputStream is = null;
        FileOutputStream os = null;
        try {
            File inFile = new File(srcFile);
            if (!inFile.exists()) {
                return false;
            }
            is = new FileInputStream(inFile);
            File outFile = new File(destFile);
            if (!outFile.exists()) {
                outFile.createNewFile();
            }
            os = new FileOutputStream(outFile);
            byte[] buffer = new byte[2048];
            for (int len = 0; (len = is.read(buffer)) != -1;) {
                os.write(buffer, 0, len);
            }
            return true;
        } catch (FileNotFoundException e) {
            return false;
        } catch (IOException e) {
            return false;
        } finally {
            try {
                if (null != is) {
                    is.close();
                }
                if (null != os) {
                    os.close();
                }
            } catch (IOException e) {

            }
        }
    }

    /**
     * M:
     * @param path
     * @return
     */
    public static String getFileName(String path) {
        if (path == null || path.equals("")) {
            return path;
        }
        int index = path.lastIndexOf(File.separator);
        if (index > 0) {
            path = path.substring(index + 1, path.length());
        }
        return path;
    }

    /*
     * this method is similar with formatTimeStampString, except that it can show Now/Yesterday
     * if the time is within a minute
     * obviously you need to refresh to update this String after some seconds.
     */
    public static String formatTimeStampStringExtend(Context context, long when) {
        Time then = new Time();
        then.set(when);
        Time now = new Time();
        now.setToNow();

        // Basic settings for formatDateTime() we want for all cases.
        int format_flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_ABBREV_ALL
                | DateUtils.FORMAT_CAP_AMPM;

        // If the message is from a different year, show the date and year.
        if (then.year != now.year) {
            format_flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
        } else if (then.yearDay != now.yearDay) {
            // If it is from a different day than today, show only the date.
            if ((now.yearDay - then.yearDay) == 1) {
                return context.getString(R.string.str_ipmsg_yesterday);
            } else {
                format_flags |= DateUtils.FORMAT_SHOW_DATE;
            }
        } else if ((now.toMillis(false) - then.toMillis(false)) < 60000) {
            return context.getString(R.string.time_now);
        } else {
            // Otherwise, if the message is from today, show the time.
            format_flags |= DateUtils.FORMAT_SHOW_TIME;
        }
        sOpMessageUtilsExt.formatTimeStampStringExtend(context, when, format_flags);
        return DateUtils.formatDateTime(context, when, format_flags);
    }

    private static List<String> getHomes(Context context) {
        MmsLog.d(TAG, "DialogModeActivity.getHomes");

        List<String> names = new ArrayList<String>();
        PackageManager packageManager = context.getPackageManager();
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
                PackageManager.MATCH_DEFAULT_ONLY);

        for (ResolveInfo ri : resolveInfo) {
            names.add(ri.activityInfo.packageName);
            MmsLog.d(TAG, "package name=" + ri.activityInfo.packageName + " class name=" + ri.activityInfo.name);
        }
        return names;
    }

    public static boolean isHome(Context context) {
        List<String> homePackageNames = getHomes(context);
        String packageName = "";
        String className = "";
        boolean ret = false;

        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningTaskInfo> rti = activityManager.getRunningTasks(2);

        if (rti != null && rti.size() > 0) {
            packageName = rti.get(0).topActivity.getPackageName();
            className = rti.get(0).topActivity.getClassName();
        }

        MmsLog.d(TAG, "package0= " + packageName + " class0=" + className);

        ret = homePackageNames.contains(packageName);
        if (!ret) {
            if ("com.mediatek.mms.ui.DialogModeActivity".equals(className)) {
                ret = true;
            }
        }

        /// M: fix bug ALPS00687923, check RunningAppProcessInfo IMPORTANCE_FOREGROUND @{
        if (!ret) {
            List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
            if (appProcesses == null || appProcesses.size() == 0) {
                MmsLog.d(TAG, "appProcesses == null || appProcesses.size() == 0");
                ret = false;
            } else {
                for (RunningAppProcessInfo appProcess : appProcesses) {
                    if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                            && appProcess.processName.equals("com.android.launcher")) {
                        MmsLog.d(TAG, "IMPORTANCE_FOREGROUND == com.android.launcher");
                        ret = true;
                    }
                }
            }
        }
        /// @}

        return ret;
    }

    public static boolean checkNeedNotify(Context context, long threadId, Cursor cursor) {
        if (threadId < 0) {
            MmsLog.w(TAG, "illegal threadId:" + threadId);
            return false;
        }
        boolean appNotificationEnabled = true;
        long appMute = 0;
        long appMuteStart = 0;
        boolean threadNotificationEnabled = true;
        long threadMute = 0;
        long threadMuteStart = 0;
        if (!checkAppSettingsNeedNotify(context)) {
            return false;
        }
        if (threadId == 0) {
            return true;
        }
        /// M: check thread settings
        Uri threadSettingsUri = ContentUris.withAppendedId(THREAD_SETTINGS_URI, (int) threadId);
        if (cursor != null) {
            threadNotificationEnabled = cursor.getInt(Conversation.NOTIFICATION_ENABLE) == 0 ? false : true;
            threadMute = cursor.getLong(Conversation.MUTE);
            threadMuteStart = cursor.getLong(Conversation.MUTE_START);
            MmsLog.d(TAG, "before check: threadNotificationEnabled = " + threadNotificationEnabled
                    + ", \tthreadMute = " + threadMute + ", \tthreadMuteStart = " + threadMuteStart);
        } else {
            /// M: fix bug ALPS00415754, add some useful log
            MmsLog.d(TAG, "before query threadSettingsUri in checkNeedNotify()");
            Cursor c = context.getContentResolver().query(threadSettingsUri,
                    new String[] { Telephony.ThreadSettings.NOTIFICATION_ENABLE, Telephony.ThreadSettings.MUTE,
                            Telephony.ThreadSettings.MUTE_START, Telephony.ThreadSettings.RINGTONE,
                            Telephony.ThreadSettings.VIBRATE },
                    null, null, null);
            MmsLog.d(TAG, "after query threadSettingsUri in checkNeedNotify()");

            if (c == null) {
                MmsLog.d(TAG, "cursor is null.");
                return true;
            }
            try {
                if (c.getCount() == 0) {
                    MmsLog.d(TAG, "cursor count is 0");
                } else {
                    c.moveToFirst();
                    threadNotificationEnabled = c.getInt(0) == 0 ? false : true;
                    threadMute = c.getLong(1);
                    threadMuteStart = c.getLong(2);

                    MmsLog.d(TAG, "before check: threadNotificationEnabled = " + threadNotificationEnabled
                            + ", \tthreadMute = " + threadMute + ", \tthreadMuteStart = " + threadMuteStart);
                }
            } finally {
                if (c != null) {
                    c.close();
                }
            }
        }

        if (!threadNotificationEnabled) {
            MmsLog.d(TAG, "thread notification is disabled!");
            return false;
        }

        MmsLog.d(TAG, "\t threadMute:" + threadMute + ", threadMute*3600=" + threadMute * 3600
                + "\t threadMuteStart" + threadMuteStart / 1000);

        threadMute = checkThreadMuteTimeout(threadMuteStart, threadMute, threadSettingsUri, context);

        if (threadMute > 0) {
            MmsLog.d(TAG, "thread mute is set!");
            return false;
        }

        return true;
    }

    private static long checkThreadMuteTimeout(long threadMuteStart, long threadMute, Uri threadSettingsUri,
            Context context) {
        long mute = threadMute;
        if (threadMuteStart > 0 && threadMute > 0) {
            long currentTime = (System.currentTimeMillis() / 1000);
            MmsLog.d(TAG, "\t currentTime" + currentTime);
            if ((threadMute * 3600 + threadMuteStart / 1000) <= currentTime) {
                MmsLog.d(TAG, "thread mute timeout, reset to default.");
                threadMute = 0;
                threadMuteStart = 0;
                final Uri threadSettings = threadSettingsUri;
                final Context ct = context;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        ContentValues contentValues = new ContentValues(2);
                        contentValues.put(Telephony.ThreadSettings.MUTE, 0);
                        contentValues.put(Telephony.ThreadSettings.MUTE_START, 0);
                        ct.getContentResolver().update(threadSettings, contentValues, null, null);
                        sOpMessageUtilsExt.checkThreadMuteTimeout(threadSettings, MuteCache.getInstance());
                    }
                }, "reset-mute-Thread").start();
                return 0;
            }
        }
        return mute;
    }

    public static boolean checkNeedNotifyForFolderMode(Context context, long threadId, long threadMute,
            long threadMuteStart, boolean threadNotificationEnabled) {
        if (threadId < 0) {
            MmsLog.w(TAG, "illegal threadId:" + threadId);
            return false;
        }
        if (!checkAppSettingsNeedNotify(context)) {
            return false;
        }

        if (threadId == 0) {
            return true;
        }
        /// M: check thread settings
        if (!threadNotificationEnabled) {
            MmsLog.d(TAG, "thread notification is disabled!");
            return false;
        }

        MmsLog.d(TAG, "\t threadMute:" + threadMute + ", threadMute*3600=" + threadMute * 3600
                + "\t threadMuteStart" + threadMuteStart / 1000);

        Uri threadSettingsUri = ContentUris.withAppendedId(THREAD_SETTINGS_URI, (int) threadId);
        threadMute = checkThreadMuteTimeout(threadMuteStart, threadMute, threadSettingsUri, context);
        if (threadMute > 0) {
            MmsLog.d(TAG, "thread mute is set!");
            return false;
        }

        return true;
    }

    public static boolean checkAppSettingsNeedNotify(Context context) {
        boolean appNotificationEnabled = true;
        long appMute = 0;
        long appMuteStart = 0;

        /// M: check app settings
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        appNotificationEnabled = prefs.getBoolean(NotificationPreferenceActivity.NOTIFICATION_ENABLED, true);
        if (!appNotificationEnabled) {
            MmsLog.d(TAG, "app notification set disabled!");
            return false;
        }

        String muteStr = prefs.getString(NotificationPreferenceActivity.NOTIFICATION_MUTE, Integer.toString(0));
        appMute = Integer.parseInt(muteStr);
        appMuteStart = prefs.getLong(NotificationPreferenceActivity.MUTE_START, 0);
        MmsLog.d(TAG, "\t appmute:" + appMute + ", appMute*3600=" + appMute * 3600 + "\t appMuteStart"
                + appMuteStart / 1000);
        if (appMuteStart > 0 && appMute > 0) {
            long currentTime = (System.currentTimeMillis() / 1000);
            MmsLog.d(TAG, "\t currentTime" + currentTime);
            if ((appMute * 3600 + appMuteStart / 1000) <= currentTime) {
                MmsLog.d(TAG, "thread mute timeout, reset to default.");
                appMute = 0;
                appMuteStart = 0;
                SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
                editor.putLong(NotificationPreferenceActivity.MUTE_START, 0);
                editor.putString(NotificationPreferenceActivity.NOTIFICATION_MUTE, String.valueOf(appMute));
                editor.apply();
            }
        }

        if (appMute > 0) {
            return false;
        }
        return true;
    }

    /**
     * M: read app drawable resource and create a new file in /data/data/com.android.mms/files path.
     * @param context
     * @param fileName
     * @return
     */
    public static boolean createFileForResource(Context context, String fileName, int fileResourceId) {
        OutputStream os = null;
        InputStream ins = null;
        try {
            os = context.openFileOutput(fileName, Context.MODE_PRIVATE);
            ins = context.getResources().openRawResource(fileResourceId);
            byte[] buffer = new byte[2048];
            for (int len = 0; (len = ins.read(buffer)) != -1;) {
                os.write(buffer, 0, len);
            }
            return true;
        } catch (FileNotFoundException e) {
            MmsLog.e(TAG, "create file failed.", e);
            return false;
        } catch (IOException e) {
            MmsLog.e(TAG, "create file failed.", e);
            return false;
        } finally {
            try {
                if (null != ins) {
                    ins.close();
                }
                if (null != os) {
                    os.close();
                }
            } catch (IOException e) {
                MmsLog.e(TAG, "createFileForResource:" + e.toString());
            }
        }
    }

    public static int getStatusResourceId(Context context, MessageItem msgItem) {
        if (msgItem == null) {
            MmsLog.e(TAG, "getStatusResourceId(): MsgItem is null!", new Exception());
            return 0;
        }
        if (msgItem.isSubMsg()) {
            MmsLog.d(TAG, "getStatusResourceId(): Sub message.");
            return 0;
        }
        MmsLog.d(TAG,
                "getStatusResourceId(): isMms = " + msgItem.isMms() + ", SENTBOX = "
                        + (msgItem.mBoxId == Mms.MESSAGE_BOX_SENT) + ", has read report = " + msgItem.mHasReadReport
                        + ", has delivery report = " + msgItem.mHasDeliveryReport + " MsgId = " + msgItem.mMsgId);
        if (msgItem.isMms()) {
            if (msgItem.mHasReadReport) {
                return R.drawable.im_meg_status_read;
            }
            if (msgItem.mHasDeliveryReport) {
                return R.drawable.im_meg_status_reach;
            }

            switch (msgItem.mBoxId) {
            case Mms.MESSAGE_BOX_SENT:
                return R.drawable.im_meg_status_out;

            case Mms.MESSAGE_BOX_OUTBOX:
            case Mms.MESSAGE_BOX_FAILED:
                MmsLog.d(MmsApp.TXN_TAG, "mms is sending, uri = " + msgItem.mMessageUri);
                return R.drawable.im_meg_status_sending;

            case Mms.MESSAGE_BOX_INBOX:
            case Mms.MESSAGE_BOX_DRAFTS:
            case Mms.MESSAGE_BOX_ALL:
            default:
                return 0;
            }
        } else if (msgItem.isSms()) {
            switch (msgItem.mBoxId) {
            case Sms.MESSAGE_TYPE_QUEUED:
            case Sms.MESSAGE_TYPE_OUTBOX:
                return R.drawable.im_meg_status_sending;

            case Sms.MESSAGE_TYPE_SENT:
                return R.drawable.im_meg_status_out;

            case Sms.MESSAGE_TYPE_FAILED:
                ///M: this status has been handled by common flow
            case Sms.MESSAGE_TYPE_INBOX:
            case Sms.MESSAGE_TYPE_DRAFT:
            case Sms.MESSAGE_TYPE_ALL:
            default:
                return 0;
            }
        }
        return 0;
    }

    public static List<MmsReportStatus> getMmsReportStatus(Context context, long messageId) {
        MmsLog.d(TAG, "getMmsReportStatus(): messageId = " + messageId);
        Uri uri = Uri.withAppendedPath(Mms.REPORT_STATUS_URI, String.valueOf(messageId));
        Cursor c = SqliteWrapper.query(context, context.getContentResolver(), uri,
                new String[] { Mms.Addr.ADDRESS, "delivery_status", "read_status" }, null, null, null);
        if (c == null) {
            return null;
        }
        try {
            List<MmsReportStatus> mmsReportStatusList = new ArrayList<MmsReportStatus>();
            while (c.moveToNext()) {
                int columnDeliveryStatus = 1;
                int columnReadStatus = 2;
                mmsReportStatusList
                        .add(new MmsReportStatus(c.getInt(columnDeliveryStatus), c.getInt(columnReadStatus)));
            }
            return mmsReportStatusList;
        } finally {
            c.close();
        }
    }

    /**
     * M: For EVDO: check the sim is whether UIM or not.
     * @param subId the sim's sub id.
     * @return true: UIM; false: not UIM.
     */
    public static boolean isUSimType(int subId) {
        String phoneType = TelephonyManagerEx.getDefault().getIccCardType(subId);
        if (phoneType == null) {
            Log.d(TAG, "[isUIMType]: phoneType = null");
            return false;
        }
        Log.d(TAG, "[isUIMType]: phoneType = " + phoneType);
        return phoneType.equalsIgnoreCase("CSIM") || phoneType.equalsIgnoreCase("UIM")
                || phoneType.equalsIgnoreCase("RUIM");
    }

    /**
     * M: for check CSIM in gsm mode or not.
     * @param subId the CSIM's sub id.
     * @return true: in gsm; false: no.
     */
    public static boolean isCSIMInGsmMode(int subId) {
        if (isUSimType(subId)) {
            TelephonyManagerEx tmEx = TelephonyManagerEx.getDefault();
            int vnt = tmEx.getPhoneType(SubscriptionManager.getSlotId(subId));
            Log.d(TAG, "[isCSIMInGsmMode]:[NO_PHONE = 0; GSM_PHONE = 1; CDMA_PHONE = 2;]; phoneType:" + vnt);
            if (vnt == TelephonyManager.PHONE_TYPE_GSM) {
                return true;
            }
        }
        return false;
    }

    public static CharSequence formatMsgContent(String subject, String body, String displayAddress) {
        StringBuilder buf = new StringBuilder(
                displayAddress == null ? "" : displayAddress.replace('\n', ' ').replace('\r', ' '));
        buf.append(':').append(' ');

        int offset = buf.length();
        if (!TextUtils.isEmpty(subject)) {
            subject = subject.replace('\n', ' ').replace('\r', ' ');
            buf.append(subject);
            buf.append(' ');
        }

        if (!TextUtils.isEmpty(body)) {
            body = body.replace('\n', ' ').replace('\r', ' ');
            buf.append(body);
        }

        SpannableString spanText = new SpannableString(buf.toString());
        spanText.setSpan(new StyleSpan(Typeface.BOLD), 0, offset, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        return spanText;
    }

    public static boolean haveEmailContact(String emailAddress, Context context) {
        Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
                Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(emailAddress)),
                new String[] { Contacts.DISPLAY_NAME }, null, null, null);

        if (cursor != null) {
            try {
                String name;
                while (cursor.moveToNext()) {
                    name = cursor.getString(0);
                    if (!TextUtils.isEmpty(name)) {
                        return true;
                    }
                }
            } finally {
                cursor.close();
            }
        }
        return false;
    }

    public static CharSequence getVisualTextName(Context context, String enumName, int choiceNameResId,
            int choiceValueResId) {
        CharSequence[] visualNames = context.getResources().getTextArray(choiceNameResId);

        visualNames = sOpMessageUtilsExt.getVisualTextName(visualNames, context,
                choiceNameResId == R.array.pref_sms_save_location_choices);

        CharSequence[] enumNames = context.getResources().getTextArray(choiceValueResId);
        // Sanity check
        if (visualNames.length != enumNames.length) {
            return "";
        }
        for (int i = 0; i < enumNames.length; i++) {
            if (enumNames[i].equals(enumName)) {
                return visualNames[i];
            }
        }
        return "";
    }

    public static File getStorageFile(String filename, Context context) {
        String dir = "";
        String path = StorageManagerEx.getDefaultPath();
        if (path == null) {
            MmsLog.e(TAG, "default path is null");
            return null;
        }
        dir = path + "/" + Environment.DIRECTORY_DOWNLOADS + "/";
        MmsLog.i(TAG, "copyPart,  file full path is " + dir + filename);
        File file = getUniqueDestination(dir + filename);

        // make sure the path is valid and directories created for this file.
        File parentFile = file.getParentFile();
        if (!parentFile.exists() && !parentFile.mkdirs()) {
            MmsLog.e(TAG, "[MMS] copyPart: mkdirs for " + parentFile.getPath() + " failed!");
            return null;
        }
        return file;
    }

    public static String getMainCardDisplayName() {
        String mainSubDisplayName = "";
        Context ct = MmsApp.getApplication();
        int mainSubId = (int) Settings.System.getLong(ct.getContentResolver(), Settings.System.SMS_SIM_SETTING,
                Settings.System.DEFAULT_SIM_NOT_SET);
        if (mainSubId != Settings.System.DEFAULT_SIM_SETTING_ALWAYS_ASK
                && mainSubId != Settings.System.DEFAULT_SIM_NOT_SET) {
            SubscriptionInfo info = SubscriptionManager.from(MmsApp.getApplication())
                    .getActiveSubscriptionInfo(mainSubId);
            mainSubDisplayName = info.getDisplayName().toString();
        } else {
            SubscriptionInfo info = SubscriptionManager.from(MmsApp.getApplication())
                    .getActiveSubscriptionInfo(mainSubId);
            if (info != null && info.getSubscriptionId() > 0) {
                mainSubDisplayName = info.getDisplayName().toString();
            } else {
                info = SubscriptionManager.from(MmsApp.getApplication()).getActiveSubscriptionInfo(mainSubId);
                if (info != null && info.getSubscriptionId() > 0) {
                    mainSubDisplayName = info.getDisplayName().toString();
                } else {
                    MmsLog.e(TAG, "error to get main sub display name");
                }
            }
        }
        return mainSubDisplayName;
    }

    //Modify ALPS00445952, if this attachment didn't include extension(index < 0),
    //fileName.substring(0, index) will throw exception, and save attachment fail.
    //So we should deal with this situation.

    public static File getUniqueDestination(String fileName) {
        File file;
        final int index = fileName.indexOf(".");
        if (index > 0) {
            final String extension = fileName.substring(index + 1, fileName.length());
            final String base = fileName.substring(0, index);
            file = new File(base + "." + extension);
            for (int i = 2; file.exists(); i++) {
                file = new File(base + "_" + i + "." + extension);
            }
        } else {
            file = new File(fileName);
            for (int i = 2; file.exists(); i++) {
                file = new File(fileName + "_" + i);
            }
        }
        return file;
    }

    /* remove NotificationPlus notiPlus
    public static void handleNewNotification(Context context, int messageCount) {
    Intent clickIntent = new Intent(Intent.ACTION_MAIN);
    //clickIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    //        | Intent.FLAG_ACTIVITY_SINGLE_TOP
    //        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    clickIntent.setClassName("com.android.mms", "com.android.mms.ui.BootActivity");
    // Make a startActivity() PendingIntent for the notification.
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, clickIntent,
            PendingIntent.FLAG_UPDATE_CURRENT);
        
    EncapsulatedNotificationPlus notiPlus =
            new EncapsulatedNotificationPlus.EncapsulatedBuilder(context)
            .setTitle(context.getString(R.string.new_message))
            .setMessage(context.getResources().getQuantityString(
                R.plurals.notification_multiple, messageCount, messageCount))
            .setPositiveButton(context.getString(R.string.view), pendingIntent)
            .create();
    EncapsulatedNotificationManagerPlus.notify(1, notiPlus);
    }
    */

    public static void setMmsLimitSize(Context context) {
        Context otherAppContext = null;
        SharedPreferences sp = null;
        try {
            otherAppContext = context.createPackageContext("com.android.mms", Context.CONTEXT_IGNORE_SECURITY);

        } catch (Exception e) {
            MmsLog.e(TAG, "ConversationList NotFoundContext");
        }
        if (otherAppContext != null) {
            sp = otherAppContext.getSharedPreferences("com.android.mms_preferences", Context.MODE_PRIVATE);
        }
        String mSizeLimitTemp = null;
        int mMmsSizeLimit = 0;
        if (sp != null) {
            //Modify JWYYL-1510 chenshu 20150121 start-->
            //mSizeLimitTemp = sp.getString("pref_key_mms_size_limit", "300");
            if (context.getResources().getBoolean(R.bool.support_MMS_max_size_limit)) {
                mSizeLimitTemp = sp.getString("pref_key_mms_size_limit",
                        String.valueOf(context.getResources().getInteger(R.integer.mms_custom_size)));
            } else {
                mSizeLimitTemp = sp.getString("pref_key_mms_size_limit", "300");
            }
            //Modify JWYYL-1510 chenshu 20150121 end-->
        }
        if (mSizeLimitTemp != null && 0 == mSizeLimitTemp.compareTo("100")) {
            mMmsSizeLimit = 100;
        } else if (mSizeLimitTemp != null && 0 == mSizeLimitTemp.compareTo("200")) {
            mMmsSizeLimit = 200;
            //Add JWYYL-1510 chenshu 20150121 start-->
        } else if (mSizeLimitTemp != null && 0 == mSizeLimitTemp.compareTo("600")) {
            mMmsSizeLimit = 600;
        } else if (mSizeLimitTemp != null && 0 == mSizeLimitTemp.compareTo("900")) {
            mMmsSizeLimit = 900;
        } else if (mSizeLimitTemp != null && 0 == mSizeLimitTemp.compareTo("1024")) {
            mMmsSizeLimit = 1024;
            //Add JWYYL-1510 chenshu 20150121 end-->
        } else {
            //Modify JWYYL-1510 chenshu 20150121 start-->
            //mMmsSizeLimit = 300;
            if (context.getResources().getBoolean(R.bool.support_MMS_max_size_limit)) {
                mMmsSizeLimit = context.getResources().getInteger(R.integer.mms_custom_size);
                MmsLog.e("chenshu", "mMmsSizeLimit:" + mMmsSizeLimit);
            } else {
                mMmsSizeLimit = 300;
            }
            //Modify JWYYL-1510 chenshu 20150121 end-->
        }

        MmsLog.e("chenshu", "mMmsSizeLimit:" + mMmsSizeLimit);
        MmsConfig.setUserSetMmsSizeLimit(mMmsSizeLimit);
    }

    /// M: fix bug ALPS604911, change MmsContentType when share multi-file from FileManager @{
    public static String getContentType(Uri uri) {
        String path = uri.getPath();

        MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
        String extension = MimeTypeMap.getFileExtensionFromUrl(path).toLowerCase();
        if (TextUtils.isEmpty(extension)) {
            int dotPos = path.lastIndexOf('.');
            if (0 <= dotPos) {
                extension = path.substring(dotPos + 1);
                extension = extension.toLowerCase();
            }
        }

        String type = mimeTypeMap.getMimeTypeFromExtension(extension);
        return type;
    }
    /// @}

    /// M: fix bug ALPS01379798, support multi-file share from KK Download @{
    public static String queryContentType(Context context, Uri uri) {
        String type = context.getContentResolver().getType(uri);
        return type;
    }
    /// @}

    /*
     * M: If image can display return true, else return false.
     */
    public static boolean checkImageOK(Context context, Uri imageUri) {
        try {
            Bitmap bitmap = null;
            InputStream mInputStream = null;
            try {
                mInputStream = context.getContentResolver().openInputStream(imageUri);
                if (mInputStream != null) {
                    bitmap = BitmapFactory.decodeStream(mInputStream);
                }
                if (bitmap == null) {
                    return false;
                } else {
                    return true;
                }
            } catch (FileNotFoundException e) {
                bitmap = null;
            } finally {
                if (mInputStream != null) {
                    mInputStream.close();
                }
            }
        } catch (java.lang.OutOfMemoryError e) {
            Log.e(TAG, "checkImageOK(Uri): out of memory: ", e);
        } catch (IOException e) {
            Log.e(TAG, "checkImageOK(Uri): IOException: ", e);
        } catch (Exception e) {
            Log.e(TAG, "checkImageOK(Uri): Exception: ", e);
        }
        return false;
    }

    /*
     * M: If image can not display, show default broken image.
     */
    public static Bitmap getDefaultBrokenImage(Context context) {
        try {
            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
                    R.drawable.ic_missing_thumbnail_picture);
            return bitmap;
        } catch (java.lang.OutOfMemoryError e) {
            Log.e(TAG, "getDefaultBrokenImage: out of memory: ", e);
        }
        return null;
    }

    public static void updatePartsIfNeeded(SlideshowModel slideshow, PduPersister persister, Uri uri, PduBody pb,
            HashMap<Uri, InputStream> preOpenedFiles) throws MmsException {
        if (!slideshow.needUpdate()) {
            return;
        }
        persister.updateParts(uri, pb, preOpenedFiles);
        slideshow.resetUpdateState();
    }

    public static int calculateWallpaperSize(Context context, int height, int width) {
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        int currentMaxHeight = windowManager.getDefaultDisplay().getHeight();
        int currentMaxWidth = windowManager.getDefaultDisplay().getWidth();
        MmsLog.d(TAG, "CurrentMaxHeight = " + currentMaxHeight + " CurrentMaxWidth = " + currentMaxWidth);
        int ratio = 1;
        while ((height / ratio) > currentMaxHeight || (width / ratio) > currentMaxWidth) {
            ratio *= 2;
        }
        return ratio;
    }

    /// M: fix bug ALPS01523754.set google+ pic as wallpaper.@{
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    public static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }

    public static boolean isSimMessageAccessable(Context context, int... subId) {
        // First, forbid to access SIM message if this is not default MMS.
        boolean isSmsEnable = MmsConfig.isSmsEnabled(context);
        if (!isSmsEnable) {
            MmsLog.d(TAG, "isSimMessageAccessable Sms not enabled");
            return false;
        }

        // Second, check airplane mode
        boolean airplaneOn = Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
                0) == 1;
        if (airplaneOn) {
            MmsLog.d(TAG, "isSimMessageAccessable airplane is On");
            return false;
        }

        // Third, check whether has inserted SIM
        List<SubscriptionInfo> subInfoList = SubscriptionManager.from(MmsApp.getApplication())
                .getActiveSubscriptionInfoList();
        if (subInfoList == null || subInfoList.size() == 0) {
            MmsLog.d(TAG, "isSimMessageAccessable SIM not insert");
            return false;
        }

        // Forth, check sms ready
        ISms mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms"));
        if (mSmsManager == null) {
            MmsLog.d(TAG, "isSimMessageAccessable mSmsManager is null");
            return false;
        }
        boolean isSimReady = false;
        try {
            if (subId.length == 1) {
                isSimReady = mSmsManager.isSmsReadyForSubscriber(subId[0]);
            } else {
                for (SubscriptionInfo subInfoRecord : subInfoList) {
                    isSimReady = mSmsManager.isSmsReadyForSubscriber(subInfoRecord.getSubscriptionId());
                    if (isSimReady) {
                        break;
                    }
                }
            }
        } catch (RemoteException e) {
            MmsLog.e(TAG, "isSimMessageAccessable failed to get sms state");
            isSimReady = false;
        }

        MmsLog.d(TAG, "isSimMessageAccessable" + isSimReady);
        return isSimReady;
    }

    public static String getTempWallpaper(Context context, Uri uri) {
        InputStream is = null;
        FileOutputStream fos = null;
        String tempPath = TempFileProvider.getScrapPath(context, TEMP_WALLPAPER);
        try {
            is = context.getContentResolver().openInputStream(uri);
            File tempFile = new File(tempPath);
            if (is != null) {
                if (tempFile.exists()) {
                    tempFile.delete();
                }
                File parentFile = tempFile.getParentFile();
                if (!parentFile.exists() && !parentFile.mkdirs()) {
                    MmsLog.w(TAG, "getTempWallpaper parentFile.mkdirs fail");
                    return null;
                }
                tempFile.createNewFile();
            }
            fos = new FileOutputStream(tempFile);
            byte[] buffer = new byte[8192];
            for (int len = 0; (len = is.read(buffer)) != -1;) {
                fos.write(buffer, 0, len);
            }
            return tempPath;
        } catch (FileNotFoundException e) {
            MmsLog.e(TAG, "getTempWallpaper FileNotFoundException", e);
        } catch (IOException e) {
            MmsLog.d(TAG, "getTempWallpaper IOException", e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    MmsLog.e(TAG, "getTempWallpaper IOException while closing: " + fos, e);
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    MmsLog.e(TAG, "getTempWallpaper IOException while closing: " + is, e);
                }
            }
        }
        return null;
    }
    /// @}

    /**
     * @return Whether use Sim Simulator Toll or not.
     */
    public static boolean isUseSubSimulator() {
        String enableNowSMS = SystemProperties.get("net.ENABLE_NOWSMS");
        String enableSimulator = SystemProperties.get("net.Enable_Simulator_Tool");
        if (enableNowSMS.equals("true") && enableSimulator.equals("true")) {
            return true;
        }
        return false;
    }

    public static boolean shouldShowTimeDivider(long curTime, long nextTime) {
        Date curDate = new Date(curTime);
        Date nextDate = new Date(nextTime);
        Date cur = new Date(curDate.getYear(), curDate.getMonth(), curDate.getDate(), 0, 0, 0);
        Date next = new Date(nextDate.getYear(), nextDate.getMonth(), nextDate.getDate(), 0, 0, 0);
        return (cur.getTime() != next.getTime());
    }

    public static String getTimeDividerString(Context context, long when) {
        Time then = new Time();
        then.set(when);
        Time now = new Time();
        now.setToNow();

        // Basic settings for formatDateTime() we want for all cases.
        int formatFlags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_ABBREV_ALL
                | DateUtils.FORMAT_CAP_AMPM;

        // If the message is from a different year, show the date and year.
        if (then.year != now.year) {
            formatFlags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
        } else if (then.yearDay != now.yearDay) {
            // If it is from a different day than today, show only the date.
            formatFlags |= DateUtils.FORMAT_SHOW_DATE;
            Date curDate = new Date();
            Date cur = new Date(curDate.getYear(), curDate.getMonth(), curDate.getDate(), 0, 0, 0);
            long oneDay = 24 * 60 * 60 * 1000;
            long elapsedTime = cur.getTime() - when;
            if (elapsedTime < oneDay && elapsedTime > 0) {
                return context.getResources().getString(R.string.str_ipmsg_yesterday);
            }
        } else {
            return context.getString(R.string.str_ipmsg_today);
        }
        return DateUtils.formatDateTime(context, when, formatFlags);
    }

    public static String getShortTimeString(Context context, long time) {
        int formatFlags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_CAP_AMPM;
        formatFlags |= DateUtils.FORMAT_SHOW_TIME;
        return DateUtils.formatDateTime(context, time, formatFlags);

    }

    public static String unescapeXML(String str) {
        return str.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&quot;", "\"")
                .replaceAll("&apos;", "'").replaceAll("&amp;", "&");
    }

    public static boolean allowSafeDraft(final Activity activity, boolean deviceStorageIsFull, boolean isNofityUser,
            int toastType) {
        return sOpMessageUtilsExt.allowSafeDraft(activity, deviceStorageIsFull, isNofityUser, toastType);
    }

    ///M: WFC: utility api @ {
    public static boolean isSimPresent(Context context) {
        int[] subs = SubscriptionManager.from(context).getActiveSubscriptionIdList();
        if (subs.length == 0) {
            MmsLog.d(TAG, "Sim not present");
            return false;
        } else {
            MmsLog.d(TAG, "Sim present");
            return true;
        }
    }

    public static boolean isWifiConnected(Context context) {
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo wifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        MmsLog.d(TAG, "wifi state:" + wifiManager.getWifiState() + "wifi connected:" + wifi.isConnected());
        if (wifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED && wifi.isConnected()) {
            MmsLog.d(TAG, "Wifi connected");
            return true;
        }
        MmsLog.d(TAG, "Wifi off or not connected");
        return false;
    }

    public static int getCellularState(int selectedSubId, Context context) {
        MmsLog.d(TAG, "getCellularState() subid: " + selectedSubId);
        ITelephonyEx telephonyEx = ITelephonyEx.Stub
                .asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE_EX));
        Bundle bundle = null;
        try {
            bundle = telephonyEx.getServiceState(selectedSubId);
        } catch (RemoteException e) {
            e.printStackTrace();
            return ServiceState.STATE_OUT_OF_SERVICE;
        }
        if (bundle != null) {
            return ServiceState.newFromBundle(bundle).getState();
        } else {
            return ServiceState.STATE_OUT_OF_SERVICE;
        }
    }
    /// @}

    /**
     * check whether the card inserted into the slot is a CDMA card
     */
    public static boolean isCdmaCard(int slotId) {
        boolean isCdma = false;
        TelephonyManagerEx telephonyMgrEx = TelephonyManagerEx.getDefault();
        String[] types = telephonyMgrEx.getSupportCardType(slotId);

        if (types == null) {
            Log.d(TAG, "isCdmaCard, types = null");
            return isCdma;
        }

        for (String type : types) {
            if ("RUIM".equals(type) || "CSIM".equals(type)) {
                isCdma = true;
            } else if ("SIM".equals(type) && telephonyMgrEx.isCt3gDualMode(slotId)) {
                isCdma = true;
            }
        }

        Log.d(TAG, "isCdmaCard, slotId = " + slotId + ", types = " + types + ", isCdma = " + isCdma);
        return isCdma;
    }

    //Add, Mms-SmsEncoding7Bit Start
    public static final String findSpanishChar(CharSequence s) {
        int sLength = s.length();
        StringBuilder sPs = new StringBuilder();
        for (int i = 0; i < sLength; i++) {
            char tmpC = s.charAt(i);
            int tmpi = (int) tmpC;
            //   Log.e("chenshu","MessageUtils->>findSpanishChar->>tmpi->>"+Integer.toHexString(tmpi));
            //   Log.d("chenshu","MessageUtils->>findSpanishChar->>calculateLength->>"+s.subSequence(i,i+1));

            int[] params = SmsMessage.calculateLength(s.subSequence(i, i + 1), false);
            //            /* SmsMessage.calculateLength returns an int[4] with:
            //             *   int[0] being the number of SMS's required,
            //             *   int[1] the number of code units used,
            //             *   int[2] is the number of code units remaining until the next message.
            //             *   int[3] is the encoding type that should be used for the message.
            //             */
            int remainingInCurrentMessage = params[2];
            //   Log.d("chenshu","MessageUtils->>findSpanishChar->>remainingInCurrentMessage->>"+remainingInCurrentMessage);
            if (remainingInCurrentMessage > 70) {
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>Default 7-bit char");
                sPs.append(tmpC);
                continue;
            }

            switch (tmpi) {
            case 0x00C0:
            case 0x00C1:
            case 0x00C2:
            case 0x00C3:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to A");
                sPs.append('A');
                break;
            case 0x00C8:
            case 0x00CA:
            case 0x00CB:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to E");
                sPs.append('E');
                break;
            case 0x00CC:
            case 0x00CD:
            case 0x00CE:
            case 0x00CF:
            case 0x0130:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to I");
                sPs.append('I');
                break;
            case 0x00D2:
            case 0x00D3:
            case 0x00D4:
            case 0x00D5:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to O");
                sPs.append('O');
                break;
            case 0x00D9:
            case 0x00DA:
            case 0x00DB:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to U");
                sPs.append('U');
                break;
            case 0x00E1:
            case 0x00E2:
            case 0x00E3:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to a");
                sPs.append('a');
                break;
            //case 0x00C7:
            case 0x00E7:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to c");
                sPs.append('c');
                break;
            case 0x00EA:
            case 0x00EB:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to e");
                sPs.append('e');
                break;
            case 0x00ED:
            case 0x00EE:
            case 0x00EF:
            case 0x0131:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to i");
                sPs.append('i');
                break;
            case 0x00F3:
            case 0x00F4:
            case 0x00F5:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to o");
                sPs.append('o');
                break;
            case 0x00FA:
            case 0x00FB:
                //   Log.d("chenshu","MessageUtils->>findSpanishChar->>convert to u");
                sPs.append('u');
                break;
            case 0x011E:
                sPs.append('G');
                break;
            case 0x011F:
                sPs.append('g');
                break;
            case 0x015E:
                sPs.append('S');
                break;
            case 0x015F:
                sPs.append('s');
                break;
            default:
                if (tmpC > 127 || tmpC < 0) {
                    //      Log.e("chenshu","MessageUtil->>findSpanishChar->>becaus it was not ascii char->>tmpC->>"+tmpC);
                    return null;
                } else {
                    sPs.append(tmpC);
                }
                break;
            }
            //   Log.e("chenshu","MessageUtil->>findSpanishChar->>tmpC->>"+tmpC);
        }

        if (sPs.toString().endsWith(s.toString())) {
            return null;
        } else {
            //   Log.e("chenshu","MessageUtil->>findSpanishChar->>mRepliceText->>"+sPs.toString());
            return sPs.toString();
        }
    }
    //Add, Mms-SmsEncoding7Bit End
}

/**
 * The common code about delete progress dialogs.
 */
/// M: Code analyze 018, For fix bug ALPS00278539, ANR occured when clicking
// the deleting message. @{
class DeleteProgressDialogUtil {
    /**
     * Gets a delete progress dialog.
     * @param context the activity context.
     * @return the delete progress dialog.
     */
    public static NewProgressDialog getProgressDialog(Context context) {
        NewProgressDialog dialog = new NewProgressDialog(context);
        dialog.setCancelable(false);
        dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        dialog.setMessage(context.getString(R.string.deleting));
        dialog.setMax(1); /* default is one complete */
        // ignore the search key, when deleting we do not want the search bar come out.
        dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                return (keyCode == KeyEvent.KEYCODE_SEARCH);
            }
        });
        return dialog;
    }
}

// M: fix bug ALPS00351620
class SearchProgressDialogUtil {
    /**
     * Gets a search progress dialog.
     * @param context the activity context.
     * @return the search progress dialog.
     */
    public static NewProgressDialog getProgressDialog(Context context) {
        NewProgressDialog dialog = new NewProgressDialog(context);
        dialog.setCancelable(false);
        dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        dialog.setMessage(context.getString(R.string.refreshing));
        dialog.setMax(1); /* default is one complete */
        // ignore the search key, when searching we do not want the search bar come out.
        dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                return (keyCode == KeyEvent.KEYCODE_SEARCH);
            }
        });
        return dialog;
    }
}

class NewProgressDialog extends ProgressDialog {
    private boolean mIsDismiss = false;

    public NewProgressDialog(Context context) {
        super(context);
    }

    public void dismiss() {
        if (isDismiss()) {
            super.dismiss();
        }
    }

    public synchronized void setDismiss(boolean isDismiss) {
        this.mIsDismiss = isDismiss;
    }

    public synchronized boolean isDismiss() {
        return mIsDismiss;
    }
}
/// @}