从第一步我们发现,在第一步修改之后,在短彩绘画界面中中文附件名的附件已无法显示,经过打印堆栈我们发现还是中文乱码在作祟。下面我们接着进行分析,这次我们从UI层往逻辑处理层进行分析。首先我们找到保存附件操作的页面和相关的代码:

短彩会话界面ComposeMessageActivity.java类中的MsgListMenuClickListener子类,onMenuItemClick()方法:<TAG 1-1>

/**

     * Context menu handlers for the message list view.

     */

    private final class MsgListMenuClickListener implements MenuItem.OnMenuItemClickListener {

        private MessageItem mMsgItem;



        public MsgListMenuClickListener(MessageItem msgItem) {

            mMsgItem = msgItem;

        }



        @Override

        public boolean onMenuItemClick(MenuItem item) {

            if (mMsgItem == null) {

                return false;

            }



            switch (item.getItemId()) {

                case MENU_EDIT_MESSAGE:

                    editMessageItem(mMsgItem);

                    drawBottomPanel();

                    return true;



                case MENU_COPY_MESSAGE_TEXT:

                    copyToClipboard(mMsgItem.mBody);

                    return true;



                case MENU_FORWARD_MESSAGE:

                    if (mMsgItem.isMms() && !isAllowForwardMessage(mMsgItem)) {

                        Toast.makeText(ComposeMessageActivity.this,

                                R.string.forward_size_over, Toast.LENGTH_SHORT).show();

                        return false;

                    }

                    forwardMessage(mMsgItem);

                    return true;



                case MENU_RESEND:

                    resendMessage(mMsgItem);

                    return true;



                case MENU_VIEW_SLIDESHOW:

                    MessageUtils.viewMmsMessageAttachment(ComposeMessageActivity.this,

                            ContentUris.withAppendedId(Mms.CONTENT_URI, mMsgItem.mMsgId), null,

                            getAsyncDialog());

                    return true;



                case MENU_VIEW_MESSAGE_DETAILS:

                    return showMessageDetails(mMsgItem);



                case MENU_DELETE_MESSAGE: {

                    DeleteMessageListener l = new DeleteMessageListener(mMsgItem);

                    confirmDeleteDialog(l, mMsgItem.mLocked);

                    return true;

                }

                case MENU_DELIVERY_REPORT:

                    showDeliveryReport(mMsgItem.mMsgId, mMsgItem.mType);

                    return true;



                case MENU_COPY_TO_SDCARD: {

                    int resId = copyMedia(mMsgItem.mMsgId) ? R.string.copy_to_sdcard_success :

                        R.string.copy_to_sdcard_fail;

                    Toast.makeText(ComposeMessageActivity.this, resId, Toast.LENGTH_SHORT).show();

                    return true;

                }




                case MENU_SAVE_RINGTONE: {

                    int resId = getDrmMimeSavedStringRsrc(mMsgItem.mIsDrmRingtoneWithRights,

                            saveRingtone(mMsgItem.mMsgId));

                    Toast.makeText(ComposeMessageActivity.this, resId, Toast.LENGTH_SHORT).show();

                    return true;

                }



                case MENU_LOCK_MESSAGE: {

                    lockMessage(mMsgItem, true);

                    return true;

                }



                case MENU_UNLOCK_MESSAGE: {

                    lockMessage(mMsgItem, false);

                    return true;

                }



                case MENU_COPY_EXTRACT_URL:

                    String copyedUrl = item.getIntent().getStringExtra("copyurl");

                    copyToClipboard(copyedUrl);

                    return true;



                case MENU_COPY_TO_SIM: {

                    if (MessageUtils.getActivatedIccCardCount() > 1) {

                        showCopySelectDialog(mMsgItem);

                    } else if (MessageUtils.isMultiSimEnabledMms()) {

                        new Thread(new CopyToSimThread(mMsgItem,

                                MessageUtils.isIccCardActivated(MessageUtils.SUB1) ?

                                MessageUtils.SUB1 : MessageUtils.SUB2)).start();

                    } else {

                        new Thread(new CopyToSimThread(mMsgItem)).start();

                    }

                    return true;

                }

                case MENU_SELECT_COPY_MESSAGE_TEXT:

                    AdapterView.AdapterContextMenuInfo info;

                    try {

                         info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();

                    } catch (ClassCastException exception) {

                        Log.e(TAG, "Bad menuInfo.", exception);

                        return false;

                    }



                    final Cursor cursor = (Cursor) mMsgListAdapter.getItem(info.position);

                    if (mMsgItem.isSms()) {

                        showSmsMessageContent(cursor);

                    } else {

                        MessageUtils.viewMmsMessageAttachment(ComposeMessageActivity.this,

                                ContentUris.withAppendedId(Mms.CONTENT_URI, mMsgItem.mMsgId), null,

                                getAsyncDialog());

                    }

                    return true;

                default:

                    return false;

            }

        }

    }

上述代码<TAG 1-1>中,调用copyMedia()方法<TAG 1-2>代码如下:

private boolean copyMedia(long msgId) {

        boolean result = true;

        PduBody body = null;

        try {

            body = SlideshowModel.getPduBody(this,

                        ContentUris.withAppendedId(Mms.CONTENT_URI, msgId));

        } catch (MmsException e) {

            Log.e(TAG, "copyMedia can't load pdu body: " + msgId);

        }

        if (body == null) {

            return false;

        }



        int partNum = body.getPartsNum();

        for(int i = 0; i < partNum; i++) {

            PduPart part = body.getPart(i);



            // all parts have to be successful for a valid result.

            result &= copyPart(part, Long.toHexString(msgId));

        }

        return result;

   

上述代码<TAG 1-2>中,调用copyPart()方法<TAG 1-3>代码如下:

private boolean copyPart(PduPart part, String fallback) {

        Uri uri = part.getDataUri();

        String type = new String(part.getContentType());

        boolean isDrm = DrmUtils.isDrmType(type);

        if (isDrm) {

            type = MmsApp.getApplication().getDrmManagerClient()

                    .getOriginalMimeType(part.getDataUri());

        }

        if (!ContentType.isImageType(type)

                && !ContentType.isVideoType(type)

                && !ContentType.isAudioType(type)

                && !(ContentType.TEXT_VCARD.toLowerCase().equals(type.toLowerCase()))

                && !(ContentType.AUDIO_OGG.toLowerCase().equals(type.toLowerCase()))) {

            return true;    // we only save pictures, videos, and sounds. Skip the text parts,

                            // the app (smil) parts, and other type that we can't handle.

                            // Return true to pretend that we successfully saved the part so

                            // the whole save process will be counted a success.

        }

        InputStream input = null;

        FileOutputStream fout = null;

        try {

            input = mContentResolver.openInputStream(uri);

            if (input instanceof FileInputStream) {

                FileInputStream fin = (FileInputStream) input;



                byte[] location = part.getName();

                if (location == null) {

                    location = part.getFilename();

                }

                if (location == null) {

                    location = part.getContentLocation();

                }



                String fileName;

                if (location == null) {

                    // Use fallback name.

                    fileName = fallback;

                } else {

                    // For locally captured videos, fileName can end up being something like this:

                    //      /mnt/sdcard/Android/data/com.android.mms/cache/.temp1.3gp

                    fileName = new String(location);

                }

                File originalFile = new File(fileName);

                fileName = originalFile.getName();  // Strip the full path of where the "part" is

                                                    // stored down to just the leaf filename.

                                                    Log.d("bill","utf--"+(new String(location,"gb2312")+"--dd--"+(new String(location))));

                                                    Log.d("bill","fileName--"+fileName);

                // Depending on the location, there may be an

                // extension already on the name or not. If we've got audio, put the attachment

                // in the Ringtones directory.

                String dir = Environment.getExternalStorageDirectory() + "/"

                                + (ContentType.isAudioType(type) ? Environment.DIRECTORY_RINGTONES :

                                    Environment.DIRECTORY_DOWNLOADS)  + "/";

                String extension;

                int index;

                if ((index = fileName.lastIndexOf('.')) == -1) {

                    extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(type);

                } else {

                    extension = fileName.substring(index + 1, fileName.length());

                    fileName = fileName.substring(0, index);

                }

                if (isDrm) {

                    extension += DrmUtils.getConvertExtension(type);

                }



                // Remove leading periods. The gallery ignores files starting with a period.

                fileName = fileName.replaceAll("^\\.", "");



                File file = getUniqueDestination(dir + fileName, extension);



                // make sure the path is valid and directories created for this file.

                File parentFile = file.getParentFile();

                if (!parentFile.exists() && !parentFile.mkdirs()) {

                    Log.e(TAG, "[MMS] copyPart: mkdirs for " + parentFile.getPath() + " failed!");

                    return false;

                }



                fout = new FileOutputStream(file);



                byte[] buffer = new byte[8000];

                int size = 0;

                while ((size=fin.read(buffer)) != -1) {

                    fout.write(buffer, 0, size);

                }



                // Notify other applications listening to scanner events

                // that a media file has been added to the sd card

                sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,

                        Uri.fromFile(file)));

            }

        } catch (IOException e) {

            // Ignore

            Log.e("bill", "IOException caught while opening or reading stream", e);

            return false;

        } finally {

            if (null != input) {

                try {

                    input.close();

                } catch (IOException e) {

                    // Ignore

                    Log.e(TAG, "IOException caught while closing stream", e);

                    return false;

                }

            }

            if (null != fout) {

                try {

                    fout.close();

                } catch (IOException e) {

                    // Ignore

                    Log.e(TAG, "IOException caught while closing stream", e);

                    return false;

                }

            }

        }

        return true;

    }

上述代码<TAG 1-3>中,调用PduPart.java类中的getName()

public byte[] getName() {

         return (byte[]) mPartHeader.get(P_NAME);

     }

因此我们分析setName()方法:

public void setName(byte[] name) {

     android.util.Log.d("bill",android.util.Log.getStackTraceString(new Throwable()));

         if(null == name) {

             throw new NullPointerException("null content-id");

         }



         mPartHeader.put(P_NAME, name);

     }

通过打印堆栈我们进行代码追溯:

06-03 17:29:02.759 D/bill    ( 1320): java.lang.Throwable

06-03 17:29:02.759 D/bill    ( 1320):     at com.google.android.mms.pdu.PduPart.setName(PduPart.java:342)

06-03 17:29:02.759 D/bill    ( 1320):     at com.google.android.mms.pdu.PduPersister.loadParts(PduPersister.java:440)

06-03 17:29:02.759 D/bill    ( 1320):     at com.google.android.mms.pdu.PduPersister.load(PduPersister.java:632)



分析PduPersister.java类中的loadParts()方法<TAG 1-4>:

private PduPart[] loadParts(long msgId) throws MmsException {

        Cursor c = SqliteWrapper.query(mContext, mContentResolver,

                Uri.parse("content://mms/" + msgId + "/part"),

                PART_PROJECTION, null, null, null);



        PduPart[] parts = null;



        try {

            if ((c == null) || (c.getCount() == 0)) {

                if (LOCAL_LOGV) {

                    Log.v(TAG, "loadParts(" + msgId + "): no part to load.");

                }

                return null;

            }



            int partCount = c.getCount();

            int partIdx = 0;

            parts = new PduPart[partCount];

            while (c.moveToNext()) {

                PduPart part = new PduPart();

                Integer charset = getIntegerFromPartColumn(

                        c, PART_COLUMN_CHARSET);

                if (charset != null) {

                    part.setCharset(charset);

                }



                byte[] contentDisposition = getByteArrayFromPartColumn(

                        c, PART_COLUMN_CONTENT_DISPOSITION);

                if (contentDisposition != null) {

                    part.setContentDisposition(contentDisposition);

                }



                byte[] contentId = getByteArrayFromPartColumn(

                        c, PART_COLUMN_CONTENT_ID);

                if (contentId != null) {

                    part.setContentId(contentId);

                }



                byte[] contentLocation = getByteArrayFromPartColumn(

                        c, PART_COLUMN_CONTENT_LOCATION);

                if (contentLocation != null) {

                    part.setContentLocation(contentLocation);

                }



                byte[] contentType = getByteArrayFromPartColumn(

                        c, PART_COLUMN_CONTENT_TYPE);

                if (contentType != null) {

                    part.setContentType(contentType);

                } else {

                    throw new MmsException("Content-Type must be set.");

                }



                byte[] fileName = getByteArrayFromPartColumn(

                        c, PART_COLUMN_FILENAME);

                if (fileName != null) {

                    part.setFilename(fileName);

                }



                byte[] name = getByteArrayFromPartColumn(

                        c, PART_COLUMN_NAME);

                if (name != null) {

                    part.setName(name);

                }




                // Construct a Uri for this part.

                long partId = c.getLong(PART_COLUMN_ID);

                Uri partURI = Uri.parse("content://mms/part/" + partId);

                part.setDataUri(partURI);



                // For images/audio/video, we won't keep their data in Part

                // because their renderer accept Uri as source.

                String type = toIsoString(contentType);

                if (!ContentType.isImageType(type)

                        && !ContentType.isAudioType(type)

                        && !ContentType.isVideoType(type)) {

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();

                    InputStream is = null;



                    // Store simple string values directly in the database instead of an

                    // external file.  This makes the text searchable and retrieval slightly

                    // faster.

                    if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type)

                            || ContentType.TEXT_HTML.equals(type)) {

                        String text = c.getString(PART_COLUMN_TEXT);

                        byte [] blob = new EncodedStringValue(text != null ? text : "")

                            .getTextString();

                        baos.write(blob, 0, blob.length);

                    } else {



                        try {

                            is = mContentResolver.openInputStream(partURI);



                            byte[] buffer = new byte[256];

                            int len = is.read(buffer);

                            while (len >= 0) {

                                baos.write(buffer, 0, len);

                                len = is.read(buffer);

                            }

                        } catch (IOException e) {

                            Log.e(TAG, "Failed to load part data", e);

                            c.close();

                            throw new MmsException(e);

                        } finally {

                            if (is != null) {

                                try {

                                    is.close();

                                } catch (IOException e) {

                                    Log.e(TAG, "Failed to close stream", e);

                                } // Ignore

                            }

                        }

                    }

                    part.setData(baos.toByteArray());

                }

                parts[partIdx++] = part;

            }

        } finally {

            if (c != null) {

                c.close();

            }

        }



        return parts;

    }

上述代码<TAG 1-4>中,从如下的代码我们可以发现,这里从数据库中取出数据,并对数据进行了编码,从上一篇博客中我们可以了解,我们已经对字节数组进行了处理,也就是说数据不再是单一的ISO_8895-1编码,而是数据的原始编码。这里则不用再对数据进行处理。

private byte[] getByteArrayFromPartColumn(Cursor c, int columnIndex) {

        if (!c.isNull(columnIndex)) {

            return getBytes(c.getString(columnIndex));

        }

        return null;

    }

public static byte[] getBytes(String data) {<TAG 1-5>

        try {

            return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);

        } catch (UnsupportedEncodingException e) {

            // Impossible to reach here!

            Log.e(TAG, "ISO_8859_1 must be supported!", e);

            return new byte[0];

        }

    }

代码<TAG 1-5>修改如下,

public static byte[] getBytes(String data) {<TAG 1-5>

        return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);

}

乱码问题搞定,这附件不能保存的问题,迎刃而解。

解决Android4.3版本下,手机短彩接收中文文件名附件,中文名字的附件无法保存(第二步:解决从从数据库中读取附件文件名,并在长按后保存附件时,中文乱码导致的无法保存附件)的更多相关文章

  1. 解决vue低版本安卓手机兼容性问题

    低版本的安卓手机可能会白屏,是由新特性不支持引起的 解决代码es6新特性兼容问题 1,npm 安装 npm install babel-polyfill npm install es6-promise ...

  2. 模板列传值到子窗体中,子窗体中多选gridview中checkbox保存数据多项到数据库中

    <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> &l ...

  3. 关于从JSP页面插入数据到数据库中乱码问题的解决

    问题描述:最近我在写一个j2ee的留言板系统模块,遇到了一个非常让我头大的问题,当我从JSP页面输入数据后,通过hibernate中的业务逻辑类HQL语句把这个数据插入到本地的mysql数据库中,可是 ...

  4. 编写SqlHelper使用,在将ExecuteReader方法封装进而读取数据库中的数据时会产生Additional information: 阅读器关闭时尝试调用 Read 无效问题,解决方法与解释

    在自学杨中科老师的视频教学时,拓展编写SqlHelper使用,在将ExecuteReader方法封装进而读取数据库中的数据时 会产生Additional information: 阅读器关闭时尝试调用 ...

  5. 使用有序GUID:提升其在各数据库中作为主键时的性能

    原文出处:https://www.codeproject.com/articles/388157/guids-as-fast-primary-keys-under-multiple-database  ...

  6. 【Android Developers Training】 26. 在SQL数据库中保存数据

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. mybatis查询mysql 数据库中 BLOB字段,结果出现乱码

    起因 mybatis-plus 通过Mapper 查询数据,映射出来的BLOB字段中的yml数据中文是乱码的 --- DefaultValue: '' Formula: '' HintContent: ...

  8. 配置NHibernate将枚举保存为Oracle数据库中的字符串

    假设有这样一个枚举: /// <summary> /// 字典项类型 /// </summary> public enum DicItemType { [EnumDescrip ...

  9. Oracle数据库中的数据出错的解决办法

    http://www.jcwcn.com/article/database/oracle/ 今天上班犯了一个严重的错误:把我们系统所使用的Oracle数据库中的数据给改掉了!当发现自己改错时,顿时冒了 ...

随机推荐

  1. darknet-训练自己的yolov3模型

    目录 Yolo v3的使用方法 安装darknet 训练Pascal VOC格式的数据 修改cfg文件中的voc.data 修改VOC.names 下载预训练卷积层权重 修改cfg/yolov3-vo ...

  2. oadrunner11录制手机app脚本

    oadrunner11录制手机app视频:http://pan.baidu.com/s/1bnc4cHL 注意点: 1.手机和loadrunner安装的电脑必须在同一网段2.视频的www.baidu. ...

  3. Java中关于Arrays.asList()的操作

    我们可以通过Arrays.asList() 产生一个List,但是要记住,我们通过Arrays.asList产生的list是基于一个固定大小的数组的, 仅支持那些不会改变数组大小的操作.所以我们在使用 ...

  4. Educational Codeforces Round 1 E. Chocolate Bar dp

    题目链接:http://codeforces.com/contest/598/problem/E E. Chocolate Bar time limit per test 2 seconds memo ...

  5. 适用于目前环境的bug记录

    问测试,bugtracker.JIRA,你们用起来啊? 难道bugtracker/JIRA只有测试用吗? 截屏忽略,只有测试人员自己提bug,开发不管不顾,解决了也不关闭bug,bug提得太多,还嫌测 ...

  6. 《剑指offer》第二十九题(顺时针打印矩阵)

    // 面试题29:顺时针打印矩阵 // 题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字. #include <iostream> void PrintMatrixInC ...

  7. 时间常用api

    1.常用api 创建 Date 对象  -  年  -  月  -  日   -  小时  -  分  -  秒 -  星期 var now=new Date() var year = now.get ...

  8. Codeforces D - Ithea Plays With Chtholly

    D - Ithea Plays With Chtholly 思路:考虑每个位置最多被替换c/2次 那么折半考虑,如果小于c/2,从左往右替换,大于c/2总右往左替换,只有小于这个数(从左往右)或者大于 ...

  9. robot 批处理文件

    robot自带的ride工具不好用,就像填表格似的写脚本,太拘束.所以一直在用sublime text写robot脚本,但是也有问题:用sublime text写的脚本,只能运行一个文件的case,并 ...

  10. asp.net一般处理程序利用反射定位方法

    asp.net的一般处理程序我想大家用得都不少,经常会如下如下的代码: using System; using System.Collections.Generic; using System.Lin ...