1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-12-25 17:18:50 -05:00

Added option to have attachments store on sd card

(this is still a rough implementation)
This commit is contained in:
Bao-Long Nguyen-Trong 2010-02-06 15:22:59 +00:00
parent 49b223e20c
commit 55dac7ee4e
10 changed files with 286 additions and 63 deletions

View File

@ -655,4 +655,10 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="remote_control_label">K-9 Mail remote control</string> <string name="remote_control_label">K-9 Mail remote control</string>
<string name="remote_control_desc">Allows this application to control K-9 Mail activities and settings.</string> <string name="remote_control_desc">Allows this application to control K-9 Mail activities and settings.</string>
<string name="account_setup_store_attachment_on_sd_card_label">Attachments On SD Cards</string>
<string name="account_setup_store_attachment_on_sd_card_summary">Whether or not attachments are store on the SD card</string>
<string name="account_setup_store_attachment_on_sd_card_error">No SD card found</string>
<string name="sd_card_error">No SD card found</string>
</resources> </resources>

View File

@ -30,9 +30,16 @@
android:key="account_default" android:key="account_default"
android:title="@string/account_settings_default_label" android:title="@string/account_settings_default_label"
android:summary="@string/account_settings_default_summary" /> android:summary="@string/account_settings_default_summary" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/account_settings_message_lists"> <CheckBoxPreference
android:key="account_setup_store_attachment_on_sd_card"
android:title="@string/account_setup_store_attachment_on_sd_card_label"
android:summary="@string/account_setup_store_attachment_on_sd_card_summary" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/account_settings_message_lists">
<ListPreference <ListPreference
android:key="account_display_count" android:key="account_display_count"
@ -40,9 +47,9 @@
android:entries="@array/account_settings_display_count_entries" android:entries="@array/account_settings_display_count_entries"
android:entryValues="@array/account_settings_display_count_values" android:entryValues="@array/account_settings_display_count_values"
android:dialogTitle="@string/account_settings_mail_display_count_label" /> android:dialogTitle="@string/account_settings_mail_display_count_label" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/account_settings_message_view"> <PreferenceCategory android:title="@string/account_settings_message_view">
<ListPreference <ListPreference
android:key="hide_buttons_enum" android:key="hide_buttons_enum"
@ -51,9 +58,9 @@
android:entryValues="@array/account_settings_hide_buttons_values" android:entryValues="@array/account_settings_hide_buttons_values"
android:dialogTitle="@string/account_settings_hide_buttons_label" /> android:dialogTitle="@string/account_settings_hide_buttons_label" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/account_settings_sync"> <PreferenceCategory android:title="@string/account_settings_sync">
<ListPreference <ListPreference
android:key="account_check_frequency" android:key="account_check_frequency"
android:title="@string/account_settings_mail_check_frequency_label" android:title="@string/account_settings_mail_check_frequency_label"
@ -102,7 +109,7 @@
android:summary="@string/account_settings_incoming_summary" android:summary="@string/account_settings_incoming_summary"
/> />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/account_settings_folders"> <PreferenceCategory android:title="@string/account_settings_folders">

View File

@ -60,10 +60,11 @@ public class Account implements Serializable
boolean mIsSignatureBeforeQuotedText; boolean mIsSignatureBeforeQuotedText;
private String mExpungePolicy = EXPUNGE_IMMEDIATELY; private String mExpungePolicy = EXPUNGE_IMMEDIATELY;
private int mMaxPushFolders; private int mMaxPushFolders;
private boolean mStoreAttachmentsOnSdCard;
List<Identity> identities; List<Identity> identities;
public enum FolderMode public enum FolderMode
{ {
NONE, ALL, FIRST_CLASS, FIRST_AND_SECOND_CLASS, NOT_SECOND_CLASS; NONE, ALL, FIRST_CLASS, FIRST_AND_SECOND_CLASS, NOT_SECOND_CLASS;
} }
@ -274,7 +275,6 @@ public class Account implements Serializable
mFolderPushMode = FolderMode.FIRST_CLASS; mFolderPushMode = FolderMode.FIRST_CLASS;
} }
try try
{ {
mFolderTargetMode = FolderMode.valueOf(preferences.getPreferences().getString(mUuid + ".folderTargetMode", mFolderTargetMode = FolderMode.valueOf(preferences.getPreferences().getString(mUuid + ".folderTargetMode",
@ -286,6 +286,7 @@ public class Account implements Serializable
} }
mIsSignatureBeforeQuotedText = preferences.getPreferences().getBoolean(mUuid + ".signatureBeforeQuotedText", false); mIsSignatureBeforeQuotedText = preferences.getPreferences().getBoolean(mUuid + ".signatureBeforeQuotedText", false);
mStoreAttachmentsOnSdCard = preferences.getPreferences().getBoolean(mUuid + ".storeAttachmentOnSdCard", true);
identities = loadIdentities(preferences.getPreferences()); identities = loadIdentities(preferences.getPreferences());
} }
@ -530,6 +531,7 @@ public class Account implements Serializable
editor.remove(mUuid + ".signatureBeforeQuotedText"); editor.remove(mUuid + ".signatureBeforeQuotedText");
editor.remove(mUuid + ".expungePolicy"); editor.remove(mUuid + ".expungePolicy");
editor.remove(mUuid + ".maxPushFolders"); editor.remove(mUuid + ".maxPushFolders");
editor.remove(mUuid + ".storeAttachmentOnSdCard");
deleteIdentities(preferences.getPreferences(), editor); deleteIdentities(preferences.getPreferences(), editor);
editor.commit(); editor.commit();
} }
@ -602,6 +604,7 @@ public class Account implements Serializable
editor.putBoolean(mUuid + ".signatureBeforeQuotedText", this.mIsSignatureBeforeQuotedText); editor.putBoolean(mUuid + ".signatureBeforeQuotedText", this.mIsSignatureBeforeQuotedText);
editor.putString(mUuid + ".expungePolicy", mExpungePolicy); editor.putString(mUuid + ".expungePolicy", mExpungePolicy);
editor.putInt(mUuid + ".maxPushFolders", mMaxPushFolders); editor.putInt(mUuid + ".maxPushFolders", mMaxPushFolders);
editor.putBoolean(mUuid + ".storeAttachmentOnSdCard", mStoreAttachmentsOnSdCard);
saveIdentities(preferences.getPreferences(), editor); saveIdentities(preferences.getPreferences(), editor);
editor.commit(); editor.commit();
@ -965,4 +968,13 @@ public class Account implements Serializable
mRing = ring; mRing = ring;
} }
public boolean isStoreAttachmentOnSdCard()
{
return mStoreAttachmentsOnSdCard;
}
public void setStoreAttachmentOnSdCard(boolean mStoreAttachmentOnSdCard)
{
this.mStoreAttachmentsOnSdCard = mStoreAttachmentOnSdCard;
}
} }

View File

@ -31,6 +31,7 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Environment;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.Process; import android.os.Process;
import android.os.PowerManager.WakeLock; import android.os.PowerManager.WakeLock;
@ -46,7 +47,6 @@ import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessageRemovalListener; import com.fsck.k9.mail.MessageRemovalListener;
import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessageRetrievalListener;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Part; import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.PushReceiver; import com.fsck.k9.mail.PushReceiver;
import com.fsck.k9.mail.Pusher; import com.fsck.k9.mail.Pusher;
@ -54,6 +54,7 @@ import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.Folder.FolderType; import com.fsck.k9.mail.Folder.FolderType;
import com.fsck.k9.mail.Folder.OpenMode; import com.fsck.k9.mail.Folder.OpenMode;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBody;
@ -61,6 +62,10 @@ import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.mail.store.LocalStore.LocalFolder; import com.fsck.k9.mail.store.LocalStore.LocalFolder;
import com.fsck.k9.mail.store.LocalStore.LocalMessage; import com.fsck.k9.mail.store.LocalStore.LocalMessage;
import com.fsck.k9.mail.store.LocalStore.PendingCommand; import com.fsck.k9.mail.store.LocalStore.PendingCommand;
import com.fsck.k9.mail.transport.EOLConvertingOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/** /**
* Starts a long running (application) Thread that will run through commands * Starts a long running (application) Thread that will run through commands
@ -3858,13 +3863,19 @@ public class MessagingController implements Runnable
for (final Account account : accounts) for (final Account account : accounts)
{ {
if (account.isStoreAttachmentOnSdCard()
&& !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "SD card not mounted: skipping synchronizing account " + account.getDescription());
continue;
}
final long accountInterval = account.getAutomaticCheckIntervalMinutes() * 60 * 1000; final long accountInterval = account.getAutomaticCheckIntervalMinutes() * 60 * 1000;
if (ignoreLastCheckedTime == false && accountInterval <= 0) if (ignoreLastCheckedTime == false && accountInterval <= 0)
{ {
if (K9.DEBUG) if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Skipping synchronizing account " + account.getDescription()); Log.i(K9.LOG_TAG, "Skipping synchronizing account " + account.getDescription());
continue; continue;
} }
@ -3947,8 +3958,6 @@ public class MessagingController implements Runnable
continue; continue;
} }
if (K9.DEBUG) if (K9.DEBUG)
Log.v(K9.LOG_TAG, "Folder " + folder.getName() + " was last synced @ " + Log.v(K9.LOG_TAG, "Folder " + folder.getName() + " was last synced @ " +
new Date(folder.getLastChecked())); new Date(folder.getLastChecked()));
@ -3967,6 +3976,16 @@ public class MessagingController implements Runnable
{ {
public void run() public void run()
{ {
//Let's be conservative and check the sd card
//in case it was unmounted while we are sync'ing this account
if (account.isStoreAttachmentOnSdCard()
&& !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "SD card not mounted: skipping synchronizing: account " + account.getDescription() + " folder=" + folder.getName());
return;
}
LocalFolder tLocalFolder = null; LocalFolder tLocalFolder = null;
try try
{ {
@ -4010,7 +4029,6 @@ public class MessagingController implements Runnable
{ {
synchronizeMailboxSynchronous(account, folder.getName(), listener); synchronizeMailboxSynchronous(account, folder.getName(), listener);
} }
finally finally
{ {
if (account.isShowOngoing()) if (account.isShowOngoing())
@ -4523,6 +4541,14 @@ public class MessagingController implements Runnable
{ {
public void run() public void run()
{ {
if (account.isStoreAttachmentOnSdCard()
&& !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "SD card not mounted: skipping reception of pushed messages: account=" + account.getDescription() + ", folder=" + remoteFolder.getName() + ", message count=" + messages.size());
return;
}
LocalFolder localFolder = null; LocalFolder localFolder = null;
try try
{ {

View File

@ -9,6 +9,7 @@ import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.view.*; import android.view.*;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
@ -377,6 +378,13 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
private void onCheckMail(Account account) private void onCheckMail(Account account)
{ {
if (account.isStoreAttachmentOnSdCard()
&& !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
Toast.makeText(this, R.string.sd_card_error, Toast.LENGTH_SHORT).show();
return;
}
MessagingController.getInstance(getApplication()).checkMail(this, account, true, true, null); MessagingController.getInstance(getApplication()).checkMail(this, account, true, true, null);
} }

View File

@ -8,6 +8,7 @@ import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.PowerManager.WakeLock; import android.os.PowerManager.WakeLock;
@ -172,6 +173,13 @@ public class FolderList extends K9ListActivity
private void checkMail(FolderInfoHolder folder) private void checkMail(FolderInfoHolder folder)
{ {
if (mAccount.isStoreAttachmentOnSdCard()
&& !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
Toast.makeText(this, R.string.sd_card_error, Toast.LENGTH_SHORT).show();
return;
}
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
final WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Email - UpdateWorker"); final WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Email - UpdateWorker");
wakeLock.setReferenceCounted(false); wakeLock.setReferenceCounted(false);
@ -447,6 +455,13 @@ public class FolderList extends K9ListActivity
private void checkMail(final Account account) private void checkMail(final Account account)
{ {
if (mAccount.isStoreAttachmentOnSdCard()
&& !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
Toast.makeText(this, R.string.sd_card_error, Toast.LENGTH_SHORT).show();
return;
}
MessagingController.getInstance(getApplication()).checkMail(this, account, true, true, mAdapter.mListener); MessagingController.getInstance(getApplication()).checkMail(this, account, true, true, mAdapter.mListener);
} }

View File

@ -10,11 +10,9 @@ import android.content.Intent;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.text.SpannableString;
import android.text.Spannable; import android.text.Spannable;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TextAppearanceSpan; import android.text.style.TextAppearanceSpan;
import android.util.Config; import android.util.Config;
import android.util.Log; import android.util.Log;
@ -974,6 +972,13 @@ public class MessageList
private void checkMail(Account account, String folderName) private void checkMail(Account account, String folderName)
{ {
if (mAccount.isStoreAttachmentOnSdCard()
&& !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
Toast.makeText(this, R.string.sd_card_error, Toast.LENGTH_SHORT).show();
return;
}
mController.synchronizeMailbox(account, folderName, mAdapter.mListener); mController.synchronizeMailbox(account, folderName, mAdapter.mListener);
sendMail(account); sendMail(account);
} }

View File

@ -5,9 +5,12 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.preference.*; import android.preference.*;
import android.preference.Preference.OnPreferenceChangeListener;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.widget.Toast;
import com.fsck.k9.*; import com.fsck.k9.*;
import com.fsck.k9.activity.ChooseFolder; import com.fsck.k9.activity.ChooseFolder;
import com.fsck.k9.activity.ChooseIdentity; import com.fsck.k9.activity.ChooseIdentity;
@ -45,6 +48,7 @@ public class AccountSettings extends K9PreferenceActivity
private static final String PREFERENCE_DELETE_POLICY = "delete_policy"; private static final String PREFERENCE_DELETE_POLICY = "delete_policy";
private static final String PREFERENCE_EXPUNGE_POLICY = "expunge_policy"; private static final String PREFERENCE_EXPUNGE_POLICY = "expunge_policy";
private static final String PREFERENCE_AUTO_EXPAND_FOLDER = "account_setup_auto_expand_folder"; private static final String PREFERENCE_AUTO_EXPAND_FOLDER = "account_setup_auto_expand_folder";
private static final String PREFERENCE_STORE_ATTACHMENTS_ON_SD_CARD = "account_setup_store_attachment_on_sd_card";
private Account mAccount; private Account mAccount;
@ -67,6 +71,7 @@ public class AccountSettings extends K9PreferenceActivity
private ListPreference mDeletePolicy; private ListPreference mDeletePolicy;
private ListPreference mExpungePolicy; private ListPreference mExpungePolicy;
private Preference mAutoExpandFolder; private Preference mAutoExpandFolder;
private CheckBoxPreference mStoreAttachmentsOnSdCard;
public static void actionSettings(Context context, Account account) public static void actionSettings(Context context, Account account)
@ -349,6 +354,28 @@ public class AccountSettings extends K9PreferenceActivity
return true; return true;
} }
}); });
mStoreAttachmentsOnSdCard = (CheckBoxPreference) findPreference(PREFERENCE_STORE_ATTACHMENTS_ON_SD_CARD);
mStoreAttachmentsOnSdCard.setChecked(mAccount.isStoreAttachmentOnSdCard());
mStoreAttachmentsOnSdCard.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue instanceof Boolean)
{
Boolean b = (Boolean)newValue;
if (b.booleanValue()
&& !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
Toast.makeText(
AccountSettings.this,
R.string.account_setup_store_attachment_on_sd_card_error,
Toast.LENGTH_SHORT).show();
return false;
}
}
return true;
}
});
} }
@Override @Override
@ -378,6 +405,7 @@ public class AccountSettings extends K9PreferenceActivity
mAccount.setFolderTargetMode(Account.FolderMode.valueOf(mTargetMode.getValue())); mAccount.setFolderTargetMode(Account.FolderMode.valueOf(mTargetMode.getValue()));
mAccount.setDeletePolicy(Integer.parseInt(mDeletePolicy.getValue())); mAccount.setDeletePolicy(Integer.parseInt(mDeletePolicy.getValue()));
mAccount.setExpungePolicy(mExpungePolicy.getValue()); mAccount.setExpungePolicy(mExpungePolicy.getValue());
mAccount.setStoreAttachmentOnSdCard(mStoreAttachmentsOnSdCard.isChecked());
SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences(); SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
String newRingtone = prefs.getString(PREFERENCE_RINGTONE, null); String newRingtone = prefs.getString(PREFERENCE_RINGTONE, null);

View File

@ -8,8 +8,10 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.net.Uri; import android.net.Uri;
import android.os.Environment;
import android.text.util.Regex; import android.text.util.Regex;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.Preferences; import com.fsck.k9.Preferences;
import com.fsck.k9.Utility; import com.fsck.k9.Utility;
@ -36,9 +38,11 @@ public class LocalStore extends Store implements Serializable
private static final int DB_VERSION = 33; private static final int DB_VERSION = 33;
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN }; private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN };
private Account mAccount;
private String mPath; private String mPath;
private SQLiteDatabase mDb; private SQLiteDatabase mDb;
private File mAttachmentsDir; private File mInternalAttachmentsDir = null;
private File mExternalAttachmentsDir = null;
private Application mApplication; private Application mApplication;
private String uUid = null; private String uUid = null;
@ -65,6 +69,24 @@ public class LocalStore extends Store implements Serializable
public LocalStore(String _uri, Application application) throws MessagingException public LocalStore(String _uri, Application application) throws MessagingException
{ {
mApplication = application; mApplication = application;
//Store should be passed their store
//but let's not break the interface now
for (Account account : Preferences.getPreferences(mApplication).getAccounts())
{
if (_uri.equals(account.getLocalStoreUri()))
{
mAccount = account;
break;
}
}
if (mAccount == null)
{
//Should not happend
throw new IllegalArgumentException("No account found: uri=" + _uri);
}
URI uri = null; URI uri = null;
try try
{ {
@ -94,10 +116,20 @@ public class LocalStore extends Store implements Serializable
parentDir.mkdirs(); parentDir.mkdirs();
} }
mAttachmentsDir = new File(mPath + "_att"); mInternalAttachmentsDir = new File(mPath + "_att");
if (!mAttachmentsDir.exists()) if (!mInternalAttachmentsDir.exists())
{ {
mAttachmentsDir.mkdirs(); mInternalAttachmentsDir.mkdirs();
}
if (useExternalAttachmentDir())
{
String externalAttachmentsPath = "/sdcard" + mPath.substring("//data".length());
mExternalAttachmentsDir = new File(externalAttachmentsPath + "_att");
if (!mExternalAttachmentsDir.exists())
{
mExternalAttachmentsDir.mkdirs();
}
} }
mDb = SQLiteDatabase.openOrCreateDatabase(mPath, null); mDb = SQLiteDatabase.openOrCreateDatabase(mPath, null);
@ -235,7 +267,21 @@ public class LocalStore extends Store implements Serializable
{ {
long attachmentLength = 0; long attachmentLength = 0;
File[] files = mAttachmentsDir.listFiles(); attachmentLength =+ getSize(mInternalAttachmentsDir);
if (useExternalAttachmentDir())
{
attachmentLength =+ getSize(mExternalAttachmentsDir);
}
File dbFile = new File(mPath);
return dbFile.length() + attachmentLength;
}
private long getSize(File attachmentsDir)
{
long attachmentLength = 0;
File[] files = attachmentsDir.listFiles();
for (File file : files) for (File file : files)
{ {
if (file.exists()) if (file.exists())
@ -244,9 +290,7 @@ public class LocalStore extends Store implements Serializable
} }
} }
return attachmentLength;
File dbFile = new File(mPath);
return dbFile.length() + attachmentLength;
} }
public void compact() throws MessagingException public void compact() throws MessagingException
@ -375,24 +419,13 @@ public class LocalStore extends Store implements Serializable
{ {
} }
try
{ delete(mInternalAttachmentsDir);
File[] attachments = mAttachmentsDir.listFiles(); if (useExternalAttachmentDir())
for (File attachment : attachments)
{
if (attachment.exists())
{
attachment.delete();
}
}
if (mAttachmentsDir.exists())
{
mAttachmentsDir.delete();
}
}
catch (Exception e)
{ {
delete(mExternalAttachmentsDir);
} }
try try
{ {
new File(mPath).delete(); new File(mPath).delete();
@ -403,24 +436,53 @@ public class LocalStore extends Store implements Serializable
} }
} }
private void delete(File attachmentsDir) {
try {
File[] attachments = attachmentsDir.listFiles();
for (File attachment : attachments)
{
if (attachment.exists())
{
attachment.delete();
}
}
if (attachmentsDir.exists())
{
attachmentsDir.delete();
}
}
catch (Exception e)
{
Log.w(K9.LOG_TAG, null, e);
}
}
public void pruneCachedAttachments() throws MessagingException public void pruneCachedAttachments() throws MessagingException
{ {
pruneCachedAttachments(false); pruneCachedAttachments(false);
} }
/**
* Deletes all cached attachments for the entire store.
*/
public void pruneCachedAttachments(boolean force) throws MessagingException public void pruneCachedAttachments(boolean force) throws MessagingException
{ {
if (force) if (force)
{ {
ContentValues cv = new ContentValues(); ContentValues cv = new ContentValues();
cv.putNull("content_uri"); cv.putNull("content_uri");
mDb.update("attachments", cv, null, null); mDb.update("attachments", cv, null, null);
} }
File[] files = mAttachmentsDir.listFiles(); pruneCachedAttachments(force, mInternalAttachmentsDir);
if (useExternalAttachmentDir())
{
pruneCachedAttachments(force, mExternalAttachmentsDir);
}
}
/**
* Deletes all cached attachments for the entire store.
*/
private void pruneCachedAttachments(boolean force, File attachmentsDir) throws MessagingException
{
File[] files = attachmentsDir.listFiles();
for (File file : files) for (File file : files)
{ {
if (file.exists()) if (file.exists())
@ -1719,7 +1781,7 @@ public class LocalStore extends Store implements Serializable
* so we copy the data into a cached attachment file. * so we copy the data into a cached attachment file.
*/ */
InputStream in = attachment.getBody().getInputStream(); InputStream in = attachment.getBody().getInputStream();
tempAttachmentFile = File.createTempFile("att", null, mAttachmentsDir); tempAttachmentFile = File.createTempFile("att", null, getAttachmentsDir());
FileOutputStream out = new FileOutputStream(tempAttachmentFile); FileOutputStream out = new FileOutputStream(tempAttachmentFile);
size = IOUtils.copy(in, out); size = IOUtils.copy(in, out);
in.close(); in.close();
@ -1784,7 +1846,7 @@ public class LocalStore extends Store implements Serializable
if (tempAttachmentFile != null) if (tempAttachmentFile != null)
{ {
File attachmentFile = new File(mAttachmentsDir, Long.toString(attachmentId)); File attachmentFile = new File(getAttachmentsDir(), Long.toString(attachmentId));
tempAttachmentFile.renameTo(attachmentFile); tempAttachmentFile.renameTo(attachmentFile);
contentUri = AttachmentProvider.getAttachmentUri( contentUri = AttachmentProvider.getAttachmentUri(
new File(mPath).getName(), new File(mPath).getName(),
@ -1942,11 +2004,20 @@ public class LocalStore extends Store implements Serializable
long attachmentId = attachmentsCursor.getLong(0); long attachmentId = attachmentsCursor.getLong(0);
try try
{ {
File file = new File(mAttachmentsDir, Long.toString(attachmentId)); File file;
file = new File(mInternalAttachmentsDir, Long.toString(attachmentId));
if (file.exists()) if (file.exists())
{ {
file.delete(); file.delete();
} }
file = new File(mExternalAttachmentsDir, Long.toString(attachmentId));
if (file.exists())
{
file.delete();
}
} }
catch (Exception e) catch (Exception e)
{ {
@ -2509,14 +2580,6 @@ public class LocalStore extends Store implements Serializable
{ {
return mApplication.getContentResolver().openInputStream(mUri); return mApplication.getContentResolver().openInputStream(mUri);
} }
catch (FileNotFoundException fnfe)
{
/*
* Since it's completely normal for us to try to serve up attachments that
* have been blown away, we just return an empty stream.
*/
return new ByteArrayInputStream(new byte[0]);
}
catch (IOException ioe) catch (IOException ioe)
{ {
throw new MessagingException("Invalid attachment.", ioe); throw new MessagingException("Invalid attachment.", ioe);
@ -2536,4 +2599,34 @@ public class LocalStore extends Store implements Serializable
return mUri; return mUri;
} }
} }
private File getAttachmentsDir()
{
if (useExternalAttachmentDir())
{
return mExternalAttachmentsDir;
}
else {
return mInternalAttachmentsDir;
}
}
private boolean useExternalAttachmentDir()
{
if (mAccount.isStoreAttachmentOnSdCard()) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
throw new IllegalStateException("SDCard not mounted");
}
else
{
return true;
}
}
else
{
return false;
}
}
} }

View File

@ -9,6 +9,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account; import com.fsck.k9.Account;
@ -156,6 +157,29 @@ public class AttachmentProvider extends ContentProvider
} }
} }
private File getFile(String dbName, String id)
throws FileNotFoundException
{
try
{
File attachmentsDir = getContext().getDatabasePath(dbName + "_att");
File file = new File(attachmentsDir, id);
if (!file.exists())
{
file = new File("/sdcard" + attachmentsDir.getCanonicalPath().substring("/data".length()), id);
if (!file.exists()) {
throw new FileNotFoundException();
}
}
return file;
}
catch (IOException e)
{
Log.w(K9.LOG_TAG, null, e);
throw new FileNotFoundException(e.getMessage());
}
}
@Override @Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
{ {
@ -176,10 +200,9 @@ public class AttachmentProvider extends ContentProvider
String type = getType(attachmentUri); String type = getType(attachmentUri);
try try
{ {
FileInputStream in = new FileInputStream( FileInputStream in = new FileInputStream(getFile(dbName, id));
new File(getContext().getDatabasePath(dbName + "_att"), id));
Bitmap thumbnail = createThumbnail(type, in); Bitmap thumbnail = createThumbnail(type, in);
thumbnail = thumbnail.createScaledBitmap(thumbnail, width, height, true); thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true);
FileOutputStream out = new FileOutputStream(file); FileOutputStream out = new FileOutputStream(file);
thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out); thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out);
out.close(); out.close();
@ -195,7 +218,7 @@ public class AttachmentProvider extends ContentProvider
else else
{ {
return ParcelFileDescriptor.open( return ParcelFileDescriptor.open(
new File(getContext().getDatabasePath(dbName + "_att"), id), getFile(dbName, id),
ParcelFileDescriptor.MODE_READ_ONLY); ParcelFileDescriptor.MODE_READ_ONLY);
} }
} }