replicant-packages_apps_Email/imap2/src/com/android/imap2/AttachmentLoader.java

198 lines
7.0 KiB
Java

/* Copyright (C) 2012 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.imap2;
import android.content.Context;
import android.os.RemoteException;
import com.android.emailcommon.provider.EmailContent.Attachment;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.service.EmailServiceStatus;
import com.android.emailsync.PartRequest;
import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.imap2.Imap2SyncService.Connection;
import com.android.mail.providers.UIProvider;
import java.io.Closeable;
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;
/**
* Handle IMAP2 attachment loading
*/
public class AttachmentLoader {
static private final int CHUNK_SIZE = 16*1024;
private final Context mContext;
private final Attachment mAttachment;
private final long mAttachmentId;
private final long mMessageId;
private final Message mMessage;
private final Imap2SyncService mService;
public AttachmentLoader(Imap2SyncService service, PartRequest req) {
mService = service;
mContext = service.mContext;
mAttachment = req.mAttachment;
mAttachmentId = mAttachment.mId;
mMessageId = mAttachment.mMessageKey;
mMessage = Message.restoreMessageWithId(mContext, mMessageId);
}
private void doStatusCallback(int status) {
try {
Imap2SyncManager.callback().loadAttachmentStatus(mMessageId, mAttachmentId, status, 0);
} catch (RemoteException e) {
// No danger if the client is no longer around
}
}
private void doProgressCallback(int progress) {
try {
Imap2SyncManager.callback().loadAttachmentStatus(mMessageId, mAttachmentId,
EmailServiceStatus.IN_PROGRESS, progress);
} catch (RemoteException e) {
// No danger if the client is no longer around
}
}
/**
* Close, ignoring errors (as during cleanup)
* @param c a Closeable
*/
private void close(Closeable c) {
try {
c.close();
} catch (IOException e) {
}
}
/**
* Save away the contentUri for this Attachment and notify listeners
* @throws IOException
*/
private void finishLoadAttachment(File file, OutputStream os) throws IOException {
InputStream in = null;
try {
in = new FileInputStream(file);
AttachmentUtilities.saveAttachment(mContext, in, mAttachment);
doStatusCallback(EmailServiceStatus.SUCCESS);
} catch (FileNotFoundException e) {
// Not bloody likely, as we just created it successfully
throw new IOException("Attachment file not found?");
} finally {
close(in);
}
}
private void readPart (ImapInputStream in, String tag, OutputStream out) throws IOException {
String res = in.readLine();
int bstart = res.indexOf("body[");
if (bstart < 0)
bstart = res.indexOf("BODY[");
if (bstart < 0)
return;
int bend = res.indexOf(']', bstart);
if (bend < 0)
return;
int br = res.indexOf('{');
if (br > 0) {
Parser p = new Parser(res, br + 1);
int expectedLength = p.parseInteger();
int remainingLength = expectedLength;
int totalRead = 0;
byte[] buf = new byte[CHUNK_SIZE];
int lastCallbackPct = -1;
int lastCallbackTotalRead = 0;
while (remainingLength > 0) {
int rdlen = (remainingLength > CHUNK_SIZE ? CHUNK_SIZE : remainingLength);
int bytesRead = in.read(buf, 0, rdlen);
totalRead += bytesRead;
out.write(buf, 0, bytesRead);
remainingLength -= bytesRead;
int pct = (totalRead * 100) / expectedLength;
// Callback only if we've read at least 1% more and have read more than CHUNK_SIZE
// We don't want to spam the Email app
if ((pct > lastCallbackPct) && (totalRead > (lastCallbackTotalRead + CHUNK_SIZE))) {
// Report progress back to the UI
doProgressCallback(pct);
lastCallbackTotalRead = totalRead;
lastCallbackPct = pct;
}
}
out.close();
String line = in.readLine();
if (!line.endsWith(")")) {
mService.errorLog("Bad part?");
throw new IOException();
}
line = in.readLine();
if (!line.startsWith(tag)) {
mService.userLog("Bad part?");
throw new IOException();
}
}
}
/**
* Loads an attachment, based on the PartRequest passed in the constructor
* @throws IOException
*/
public void loadAttachment(Connection conn) throws IOException {
if (mMessage == null) {
doStatusCallback(EmailServiceStatus.MESSAGE_NOT_FOUND);
return;
}
if (mAttachment.mUiState == UIProvider.AttachmentState.SAVED) {
return;
}
// Say we've started loading the attachment
doProgressCallback(0);
try {
OutputStream os = null;
File tmpFile = null;
try {
tmpFile = File.createTempFile("imap2_", "tmp", mContext.getCacheDir());
os = new FileOutputStream(tmpFile);
String tag = mService.writeCommand(conn.writer, "uid fetch " + mMessage.mServerId +
" body[" + mAttachment.mLocation + ']');
readPart(conn.reader, tag, os);
finishLoadAttachment(tmpFile, os);
return;
} catch (FileNotFoundException e) {
mService.errorLog("Can't get attachment; write file not found?");
doStatusCallback(EmailServiceStatus.ATTACHMENT_NOT_FOUND);
} finally {
close(os);
if (tmpFile != null) {
tmpFile.delete();
}
}
} catch (IOException e) {
// Report the error, but also report back to the service
doStatusCallback(EmailServiceStatus.CONNECTION_ERROR);
throw e;
} finally {
}
}
}