解决Android4.3版本下,手机短彩接收中文文件名附件,中文名字的附件无法保存(第二步:解决从从数据库中读取附件文件名,并在长按后保存附件时,中文乱码导致的无法保存附件)
从第一步我们发现,在第一步修改之后,在短彩绘画界面中中文附件名的附件已无法显示,经过打印堆栈我们发现还是中文乱码在作祟。下面我们接着进行分析,这次我们从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版本下,手机短彩接收中文文件名附件,中文名字的附件无法保存(第二步:解决从从数据库中读取附件文件名,并在长按后保存附件时,中文乱码导致的无法保存附件)的更多相关文章
- 解决vue低版本安卓手机兼容性问题
低版本的安卓手机可能会白屏,是由新特性不支持引起的 解决代码es6新特性兼容问题 1,npm 安装 npm install babel-polyfill npm install es6-promise ...
- 模板列传值到子窗体中,子窗体中多选gridview中checkbox保存数据多项到数据库中
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> &l ...
- 关于从JSP页面插入数据到数据库中乱码问题的解决
问题描述:最近我在写一个j2ee的留言板系统模块,遇到了一个非常让我头大的问题,当我从JSP页面输入数据后,通过hibernate中的业务逻辑类HQL语句把这个数据插入到本地的mysql数据库中,可是 ...
- 编写SqlHelper使用,在将ExecuteReader方法封装进而读取数据库中的数据时会产生Additional information: 阅读器关闭时尝试调用 Read 无效问题,解决方法与解释
在自学杨中科老师的视频教学时,拓展编写SqlHelper使用,在将ExecuteReader方法封装进而读取数据库中的数据时 会产生Additional information: 阅读器关闭时尝试调用 ...
- 使用有序GUID:提升其在各数据库中作为主键时的性能
原文出处:https://www.codeproject.com/articles/388157/guids-as-fast-primary-keys-under-multiple-database ...
- 【Android Developers Training】 26. 在SQL数据库中保存数据
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- mybatis查询mysql 数据库中 BLOB字段,结果出现乱码
起因 mybatis-plus 通过Mapper 查询数据,映射出来的BLOB字段中的yml数据中文是乱码的 --- DefaultValue: '' Formula: '' HintContent: ...
- 配置NHibernate将枚举保存为Oracle数据库中的字符串
假设有这样一个枚举: /// <summary> /// 字典项类型 /// </summary> public enum DicItemType { [EnumDescrip ...
- Oracle数据库中的数据出错的解决办法
http://www.jcwcn.com/article/database/oracle/ 今天上班犯了一个严重的错误:把我们系统所使用的Oracle数据库中的数据给改掉了!当发现自己改错时,顿时冒了 ...
随机推荐
- 【TCP/IP详解 卷一:协议】第十一章 UDP 用户数据报协议
11.1 引言 UDP 是一个简单的 面向数据报 的运输层协议:进程的每个 输出操作 都正好产生一个 UDP数据报,并且组装成一份待发送的IP数据报. 这与 TCP 不一样,它是 面向流字符 的协议, ...
- os.environ() 说明
我们想要用Python获得一些有关系统的各种信息的时候就不得不想到os的environ,那这里面都具体包含了那些内容呢? 一.简介 对于官方的解释,environ是一个字符串所对应环境的映像对象.这是 ...
- Cocos2d-x学习笔记(十)CC_CALLBACK回调函数相关宏
这里加入一个插曲,是关于Cocos2d-x回调函数的.首先,让我们Cocos支持的回调函数宏有哪些,以及其原型: // new callbacks based on C++11 #define CC_ ...
- JS 字符串 作为变量名
function initCKEditor(querySelector,content_val,myEditor) { ClassicEditor.create(document.querySelec ...
- Blue_Flke团队项目设计完善&编码测试
任务1:文档<软件设计方案说明书>github地址:https://github.com/13993013291/ruanjianguigexuqiu 任务2:项目集成开发环境:eclip ...
- Spark与Flink大数据处理引擎对比分析!
大数据技术正飞速地发展着,催生出一代又一代快速便捷的大数据处理引擎,无论是Hadoop.Storm,还是后来的Spark.Flink.然而,毕竟没有哪一个框架可以完全支持所有的应用场景,也就说明不可能 ...
- Windows 2012 R2 创建AD域
创建复数的域控制器,容错的同时(一台AD故障),且能提高用户的登录效率. 为了实现负载平衡,域配置前,两台Ad域的DNS应该按如下设置,同时,也为了避免在AD02上,选择“将域控制器添加到现有域”时出 ...
- Codeforces A - Bear and Prime 100(交互题)
A - Bear and Prime 100 思路:任何一个合数都可以写成2个以上质数的乘积.在2-100中,除了4,9,25,49外都可以写成两个以上不同质数的乘积. 所以打一个质数加这四个数的表: ...
- unity自义定摇杆
写在前面,摇杆控制人物的移动,摄像机跟随人物移动,且滑动屏幕可以控制摄像机观察人物的角度. 需要考虑的问题 1.摇杆滑动角度的计算. 2.摇杆控制效果程度的计算(即:摇杆距离中心位置越远人物的移动速度 ...
- C# DataTable列名不区分大小写
一直很纠结的就是DataTable的列名如何才能规范,从Oracle取出的DataTable都是大写,最后尝试了一下,原来C#的DataTable列名并不区分大小写,具体例子如下: DataTable ...