1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-15 22:15:15 -05:00

Added "advanced options" option and more support for local-only messages and folders:

added message upload support.
added advanced option to create local-only folder.
disabled sync & push for local-only folders.
hid expunge, check_mail, and "Load up to x more" for local-only folders.
hid create/rename/delete folder options for webdav (someone give me a webdav account).
added advanced option to change if a folder is local-only or syncable.
add dialog to confirm clearing local messages, and if it should include local-only messages or just synced messages.
added advanced option to automatically upload on move from local-only to syncable.
changed background color in message list and subject color in message view of local-only messages.
changed: keep local-only messages in folders no longer on server instead of deleting folder, and mark folder as local-only.
added automatically fully downloading messages moved/copied to local-only folder.
added advanced options option (if not enabled, those settings will be hidden).
reworked onCreateFolder(), onRenameFolder(), onDeleteFolder().
added Store.isAppendCapable().
added LocalFolder.expunge() [unused].
This commit is contained in:
ashley willis 2012-02-05 02:15:54 -06:00
parent 14a0fdf27d
commit d13aa61de3
26 changed files with 866 additions and 274 deletions

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_root"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp" >
<TextView
android:id="@+id/clear_local_folder_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="@+string/clear_local_folder_text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#FFF" />
<TextView
android:id="@+id/clear_local_only_folder_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="@+string/clear_local_only_folder_text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#FFF" />
<CheckBox
android:id="@+id/clear_local_folder_all"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="@string/clear_local_folder_all"
android:textColor="#FFF"
android:checked="false" />
</LinearLayout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_root"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp" >
<EditText
android:id="@+id/create_folder_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:hint="@string/create_folder_hint" />
<CheckBox
android:id="@+id/create_folder_local"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="@string/create_folder_local"
android:textColor="#FFF"
android:checked="false" />
</LinearLayout>

View File

@ -57,6 +57,10 @@
android:id="@+id/copy"
android:title="@string/copy_action"
/>
<item
android:id="@+id/upload"
android:title="@string/upload"
/>
<item
android:id="@+id/send_alternate"
android:title="@string/send_alternate_action"

View File

@ -93,6 +93,10 @@
android:id="@+id/batch_copy_op"
android:title="@string/batch_copy_op"
/>
<item
android:id="@+id/batch_upload_op"
android:title="@string/batch_upload_op"
/>
<item
android:id="@+id/batch_select_all"
android:title="@string/batch_select_all"

View File

@ -61,8 +61,12 @@
android:id="@+id/copy"
android:title="@string/copy_action"
/>
<item
android:id="@+id/select_text"
android:title="@string/select_text_action"
/>
<item
android:id="@+id/upload"
android:title="@string/upload"
/>
<item
android:id="@+id/select_text"
android:title="@string/select_text_action"
/>
</menu>

View File

@ -76,6 +76,8 @@
<string name="create_folder_action">Create folder</string>
<string name="rename_folder_action">Rename folder</string>
<string name="delete_folder_action">Delete folder</string>
<string name="create_folder_hint">New folder name</string>
<string name="create_folder_local">Local-only</string>
<string name="mark_all_as_read_action">Mark all messages as read</string>
<string name="add_account_action">Add account</string>
<string name="compose_action">Compose</string>
@ -124,6 +126,9 @@
<string name="empty_trash_action">Empty Trash</string>
<string name="expunge_action">Expunge</string>
<string name="clear_local_folder_action">Clear local messages</string>
<string name="clear_local_folder_text">Clear local copies of remote messages?</string>
<string name="clear_local_only_folder_text">Clear local-only messages? These will be permanently lost!</string>
<string name="clear_local_folder_all">Include local-only messages? These will be permanently lost!</string>
<string name="set_sort_action">Choose sort</string>
<string name="reverse_sort_action">Reverse sort</string>
<string name="about_action">About</string>
@ -310,6 +315,7 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="folder_push_active_symbol">(Push)</string>
<string name="from_same_sender">More from this sender</string>
<string name="upload">Upload</string>
<string name="message_copied_toast">Message copied</string>
<string name="message_moved_toast">Message moved</string>
@ -421,6 +427,9 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="account_setup_incoming_save_all_headers_title">Download headers</string>
<string name="account_setup_incoming_save_all_headers_label">Save all message headers locally</string>
<string name="account_setup_auto_upload_on_move_title">Upload on move or copy</string>
<string name="account_setup_auto_upload_on_move_label">Automatically upload local-only message when moved/copied to a syncable folder</string>
<string name="local_storage_provider_external_label">External storage (SD card)</string>
<string name="local_storage_provider_internal_label">Regular internal storage</string>
<string name="local_storage_provider_samsunggalaxy_label">%1$s additional internal storage</string>
@ -681,6 +690,9 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="folder_settings_in_top_group_label">Show in top group</string>
<string name="folder_settings_in_top_group_summary">Show near the top of the folder list</string>
<string name="folder_settings_local_only_label">Local-only folder</string>
<string name="folder_settings_local_only_summary">Set to local-only folder (never synced with server)</string>
<string name="folder_settings_folder_display_mode_label">Folder display class</string>
<string name="folder_settings_folder_display_mode_normal">None</string>
<string name="folder_settings_folder_display_mode_first_class">1st Class</string>
@ -896,6 +908,7 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="batch_spam_op">Move selected to Spam</string>
<string name="batch_move_op">Move selected</string>
<string name="batch_copy_op">Copy selected</string>
<string name="batch_upload_op">Upload selected</string>
<string name="batch_flag_mode">Star mode</string>
<string name="batch_select_mode">Select mode</string>
<string name="batch_plain_mode">Plain mode</string>
@ -919,6 +932,9 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="compact_layouts_title">Compact layouts</string>
<string name="compact_layouts_summary">Adjust layouts to display more on each page</string>
<string name="show_advanced_options_title">Advanced options</string>
<string name="show_advanced_options_summary">Show advanced options</string>
<string name="volume_navigation_title">Volume key navigation</string>
<string name="volume_navigation_summary">Flip through items using the volume controls</string>
<string name="volume_navigation_message">Message view</string>

View File

@ -179,6 +179,12 @@
android:key="account_save_all_headers"
android:summary="@string/account_setup_incoming_save_all_headers_label" />
<CheckBoxPreference
android:persistent="false"
android:title="@string/account_setup_auto_upload_on_move_title"
android:key="account_auto_upload_on_move"
android:summary="@string/account_setup_auto_upload_on_move_label" />
<PreferenceScreen
android:key="incoming"
android:title="@string/account_settings_incoming_label"

View File

@ -61,6 +61,12 @@
android:title="@string/folder_settings_include_in_integrated_inbox_label"
android:summary="@string/folder_settings_include_in_integrated_inbox_summary" />
<CheckBoxPreference
android:persistent="false"
android:key="folder_settings_local_only"
android:title="@string/folder_settings_local_only_label"
android:summary="@string/folder_settings_local_only_summary" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -73,6 +73,11 @@
android:title="@string/compact_layouts_title"
android:summary="@string/compact_layouts_summary" />
<CheckBoxPreference
android:persistent="false"
android:key="show_advanced_options"
android:title="@string/show_advanced_options_title"
android:summary="@string/show_advanced_options_summary" />
</PreferenceCategory>

View File

@ -68,6 +68,7 @@ public class Account implements BaseAccount {
public static final boolean DEFAULT_QUOTED_TEXT_SHOWN = true;
public static final boolean DEFAULT_REPLY_AFTER_QUOTE = false;
public static final boolean DEFAULT_STRIP_SIGNATURE = true;
public static final boolean DEFAULT_SHOW_ADVANCED_OPTIONS = false;
public static final String ACCOUNT_DESCRIPTION_KEY = "description";
public static final String STORE_URI_KEY = "storeUri";
@ -119,6 +120,7 @@ public class Account implements BaseAccount {
private FolderMode mFolderTargetMode;
private int mAccountNumber;
private boolean mSaveAllHeaders;
private boolean mAutoUploadOnMove;
private boolean mPushPollOnConnect;
private boolean mNotifySync;
private ScrollButtons mScrollMessageViewButtons;
@ -206,6 +208,7 @@ public class Account implements BaseAccount {
mAutomaticCheckIntervalMinutes = -1;
mIdleRefreshMinutes = 24;
mSaveAllHeaders = true;
mAutoUploadOnMove = true;
mPushPollOnConnect = true;
mDisplayCount = K9.DEFAULT_VISIBLE_LIMIT;
mAccountNumber = -1;
@ -284,6 +287,7 @@ public class Account implements BaseAccount {
mAutomaticCheckIntervalMinutes = prefs.getInt(mUuid + ".automaticCheckIntervalMinutes", -1);
mIdleRefreshMinutes = prefs.getInt(mUuid + ".idleRefreshMinutes", 24);
mSaveAllHeaders = prefs.getBoolean(mUuid + ".saveAllHeaders", true);
mAutoUploadOnMove = prefs.getBoolean(mUuid + ".autoUploadOnMove", true);
mPushPollOnConnect = prefs.getBoolean(mUuid + ".pushPollOnConnect", true);
mDisplayCount = prefs.getInt(mUuid + ".displayCount", K9.DEFAULT_VISIBLE_LIMIT);
if (mDisplayCount < 0) {
@ -445,6 +449,7 @@ public class Account implements BaseAccount {
editor.remove(mUuid + ".automaticCheckIntervalMinutes");
editor.remove(mUuid + ".pushPollOnConnect");
editor.remove(mUuid + ".saveAllHeaders");
editor.remove(mUuid + ".autoUploadOnMove");
editor.remove(mUuid + ".idleRefreshMinutes");
editor.remove(mUuid + ".lastAutomaticCheckTime");
editor.remove(mUuid + ".latestOldMessageSeenTime");
@ -601,6 +606,7 @@ public class Account implements BaseAccount {
editor.putInt(mUuid + ".automaticCheckIntervalMinutes", mAutomaticCheckIntervalMinutes);
editor.putInt(mUuid + ".idleRefreshMinutes", mIdleRefreshMinutes);
editor.putBoolean(mUuid + ".saveAllHeaders", mSaveAllHeaders);
editor.putBoolean(mUuid + ".autoUploadOnMove", mAutoUploadOnMove);
editor.putBoolean(mUuid + ".pushPollOnConnect", mPushPollOnConnect);
editor.putInt(mUuid + ".displayCount", mDisplayCount);
editor.putLong(mUuid + ".lastAutomaticCheckTime", mLastAutomaticCheckTime);
@ -1305,6 +1311,14 @@ public class Account implements BaseAccount {
mSaveAllHeaders = saveAllHeaders;
}
public synchronized boolean isAutoUploadOnMove() {
return mAutoUploadOnMove;
}
public synchronized void setAutoUploadOnMove(boolean autoUploadOnMove) {
mAutoUploadOnMove = autoUploadOnMove;
}
/**
* Are we storing out localStore on the SD-card instead of the local device
* memory?<br/>

View File

@ -179,7 +179,7 @@ public class K9 extends Application {
private static String mQuietTimeEnds = null;
private static boolean compactLayouts = false;
private static String mAttachmentDefaultPath = "";
private static boolean mShowAdvancedOptions;
private static boolean useGalleryBugWorkaround = false;
private static boolean galleryBuggy;
@ -445,6 +445,7 @@ public class K9 extends Application {
editor.putBoolean("compactLayouts", compactLayouts);
editor.putString("attachmentdefaultpath", mAttachmentDefaultPath);
editor.putBoolean("showAdvancedOptions", mShowAdvancedOptions);
fontSizes.save(editor);
}
@ -575,6 +576,7 @@ public class K9 extends Application {
compactLayouts = sprefs.getBoolean("compactLayouts", false);
mAttachmentDefaultPath = sprefs.getString("attachmentdefaultpath", Environment.getExternalStorageDirectory().toString());
mShowAdvancedOptions = sprefs.getBoolean("showAdvancedOptions", false);
fontSizes.load(sprefs);
try {
@ -1016,4 +1018,14 @@ public class K9 extends Application {
public static void setAttachmentDefaultPath(String attachmentDefaultPath) {
K9.mAttachmentDefaultPath = attachmentDefaultPath;
}
public static boolean isShowAdvancedOptions() {
return mShowAdvancedOptions;
}
public static void setShowAdvancedOptions(boolean showAdvancedOptions) {
mShowAdvancedOptions = showAdvancedOptions;
}
}

View File

@ -15,6 +15,7 @@ import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.ListView;
@ -33,6 +34,8 @@ import com.fsck.k9.mail.store.WebDavStore;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class ChooseFolder extends K9ListActivity {
String mFolder;
@ -183,6 +186,9 @@ public class ChooseFolder extends K9ListActivity {
@Override public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.folder_select_option, menu);
if (mAccount.getStoreUri().startsWith("webdav")) {
menu.findItem(R.id.create_folder).setVisible(false);
}
return true;
}
@ -283,21 +289,37 @@ public class ChooseFolder extends K9ListActivity {
}
/*
Show a dialog to create a new folder on the remote Store.
Currently only IMAP is supported.
Exactly the same as activity.FolderList.onCreateFolder().
Show a dialog to create a new folder.
Currently only IMAP and Pop3 supported.
IMAP folders are both remote and local. Pop3 folders are only local.
Exactly the same as activity.FolderList.onCreateFolder(), plus one line, plus s/mInflater/getLayoutInflater()/;
*/
private void onCreateFolder() {
final EditText input = new EditText(this);
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle(R.string.create_folder_action);
dialog.setView(input);
View view = getLayoutInflater().inflate(R.layout.create_folder, null);
final EditText input = (EditText) view.findViewById(R.id.create_folder_text);
final CheckBox checkBox = (CheckBox) view.findViewById(R.id.create_folder_local);
if (mAccount.getStoreUri().startsWith("pop3") || !K9.isShowAdvancedOptions()) {
checkBox.setVisibility(View.GONE);
}
dialog.setView(view);
dialog.setPositiveButton(R.string.okay_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String folderName = input.getText().toString().trim();
if (folderName.matches("")) {
Toast.makeText(getApplication(), "Folder name not given!", Toast.LENGTH_LONG).show();
return;
}
try {
Store store = mAccount.getRemoteStore();
if (store instanceof ImapStore) {
if (store instanceof Pop3Store || checkBox.isChecked()) {
boolean result = mAccount.getLocalStore().createFolder(folderName, true);
String toastText = "Creation of folder \"" + folderName +
((result) ? "\" succeeded." : "\" failed.");
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
onRefresh(false);
} else if (store instanceof ImapStore) {
boolean result = ((ImapStore)store).createFolder(folderName);
String toastText = "Creation of folder \"" + folderName +
((result) ? "\" succeeded." : "\" failed.");
@ -307,12 +329,6 @@ public class ChooseFolder extends K9ListActivity {
} else if (store instanceof WebDavStore) {
String toastText = "Creating WebDav Folders not currently implemented.";
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
} else if (store instanceof Pop3Store) {
boolean result = mAccount.getLocalStore().createFolder(folderName, true);
String toastText = "Creation of folder \"" + folderName +
((result) ? "\" succeeded." : "\" failed.");
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
onRefresh(false);
} else {
Log.d(K9.LOG_TAG, "Unhandled store type " + store.getClass());
}
@ -323,9 +339,7 @@ public class ChooseFolder extends K9ListActivity {
}
});
dialog.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked cancel so do some stuff */
}
public void onClick(DialogInterface dialog, int whichButton) {}
});
dialog.show();
}

View File

@ -45,8 +45,10 @@ import com.fsck.k9.service.MailService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* FolderList is the primary user interface for the program. This
@ -477,16 +479,31 @@ public class FolderList extends K9ListActivity {
Exactly the same as activity.ChooseFolder.onCreateFolder().
*/
private void onCreateFolder() {
final EditText input = new EditText(this);
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle(R.string.create_folder_action);
dialog.setView(input);
View view = mInflater.inflate(R.layout.create_folder, null);
final EditText input = (EditText) view.findViewById(R.id.create_folder_text);
final CheckBox checkBox = (CheckBox) view.findViewById(R.id.create_folder_local);
if (mAccount.getStoreUri().startsWith("pop3") || !K9.isShowAdvancedOptions()) {
checkBox.setVisibility(View.GONE);
}
dialog.setView(view);
dialog.setPositiveButton(R.string.okay_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String folderName = input.getText().toString().trim();
if (folderName.matches("")) {
Toast.makeText(getApplication(), "Folder name not given!", Toast.LENGTH_LONG).show();
return;
}
try {
Store store = mAccount.getRemoteStore();
if (store instanceof ImapStore) {
if (store instanceof Pop3Store || checkBox.isChecked()) {
boolean result = mAccount.getLocalStore().createFolder(folderName, true);
String toastText = "Creation of folder \"" + folderName +
((result) ? "\" succeeded." : "\" failed.");
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
onRefresh(false);
} else if (store instanceof ImapStore) {
boolean result = ((ImapStore)store).createFolder(folderName);
String toastText = "Creation of folder \"" + folderName +
((result) ? "\" succeeded." : "\" failed.");
@ -495,12 +512,6 @@ public class FolderList extends K9ListActivity {
} else if (store instanceof WebDavStore) {
String toastText = "Creating WebDav Folders not currently implemented.";
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
} else if (store instanceof Pop3Store) {
boolean result = mAccount.getLocalStore().createFolder(folderName, true);
String toastText = "Creation of folder \"" + folderName +
((result) ? "\" succeeded." : "\" failed.");
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
onRefresh(false);
} else {
Log.d(K9.LOG_TAG, "Unhandled store type " + store.getClass());
}
@ -511,9 +522,7 @@ public class FolderList extends K9ListActivity {
}
});
dialog.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked cancel so do some stuff */
}
public void onClick(DialogInterface dialog, int whichButton) {}
});
dialog.show();
}
@ -544,33 +553,65 @@ public class FolderList extends K9ListActivity {
MessagingController.getInstance(getApplication()).expunge(account, folderName, null);
}
private void onClearFolder(Account account, String folderName) {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setCancelable(false);
dialog.setTitle(R.string.clear_local_folder_action);
View view = mInflater.inflate(R.layout.clear_local_folder, null);
final TextView text = (TextView) view.findViewById(R.id.clear_local_folder_text);
final TextView textLocal = (TextView) view.findViewById(R.id.clear_local_only_folder_text);
final CheckBox checkBox = (CheckBox) view.findViewById(R.id.clear_local_folder_all);
// There has to be a cheaper way to get at the localFolder object than this
LocalFolder localFolder = null;
try {
if (account == null || folderName == null || !account.isAvailable(FolderList.this)) {
Log.i(K9.LOG_TAG, "not clear folder of unavailable account");
Log.i(K9.LOG_TAG, "Not clearing folder of unavailable account");
return;
}
localFolder = account.getLocalStore().getFolder(folderName);
localFolder.open(Folder.OpenMode.READ_WRITE);
localFolder.clearAllMessages();
if (localFolder.isLocalOnly()) {
checkBox.setChecked(true);
checkBox.setVisibility(View.GONE);
text.setVisibility(View.GONE);
} else {
textLocal.setVisibility(View.GONE);
}
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Exception while clearing folder", e);
} finally {
if (localFolder != null) {
localFolder.close();
}
return;
}
final LocalFolder localFolderFinal = localFolder;
onRefresh(!REFRESH_REMOTE);
dialog.setView(view);
dialog.setPositiveButton(R.string.okay_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
try {
localFolderFinal.clearAllMessages(checkBox.isChecked());
} catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Exception while clearing folder", e);
} finally {
if (localFolderFinal != null) {
localFolderFinal.close();
}
onRefresh(!REFRESH_REMOTE);
}
}
});
dialog.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (localFolderFinal != null) {
localFolderFinal.close();
}
}
});
dialog.show();
}
private void sendMail(Account account) {
MessagingController.getInstance(getApplication()).sendPendingMessages(account, mAdapter.mListener);
}
@ -759,7 +800,17 @@ public class FolderList extends K9ListActivity {
String folderName = input.getText().toString().trim();
try {
Store store = mAccount.getRemoteStore();
if (store instanceof ImapStore) {
boolean isLocalOnly = ((LocalFolder)folder.folder).isLocalOnly();
if (store instanceof Pop3Store || isLocalOnly) {
boolean result = mAccount.getLocalStore().renameFolder(folder.name, folderName);
if (result && mAccount.isSpecialFolder(folder.name)) {
resetSpecialFolders(folder.name, folderName);
}
String toastText = "Renaming folder \"" + folder.name + "\" to \"" + folderName +
((result) ? "\" succeeded." : "\" failed.");
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
onRefresh(false);
} else if (store instanceof ImapStore) {
boolean result = false;
if (((ImapStore)store).renameFolder(folder.name, folderName)) {
result = mAccount.getLocalStore().renameFolder(folder.name, folderName);
@ -779,28 +830,17 @@ public class FolderList extends K9ListActivity {
} else if (store instanceof WebDavStore) {
String toastText = "Deleting WebDav Folders not currently implemented.";
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
} else if (store instanceof Pop3Store) {
boolean result = mAccount.getLocalStore().renameFolder(folder.name, folderName);
if (result && mAccount.isSpecialFolder(folder.name)) {
resetSpecialFolders(folder.name, folderName);
}
String toastText = "Renaming folder \"" + folder.name + "\" to \"" + folderName +
((result) ? "\" succeeded." : "\" failed.");
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
onRefresh(false);
} else {
Log.d(K9.LOG_TAG, "Unhandled store type " + store.getClass());
}
} catch (com.fsck.k9.mail.MessagingException me) {
Log.e(K9.LOG_TAG, "MessagingException trying to deletefolder \"" +
Log.e(K9.LOG_TAG, "MessagingException trying to rename folder \"" +
folder.name + "\": " + me);
}
}
});
dialog.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked cancel so do some stuff */
}
public void onClick(DialogInterface dialog, int whichButton) {}
});
dialog.show();
@ -819,7 +859,17 @@ public class FolderList extends K9ListActivity {
public void onClick(DialogInterface dialog, int whichButton) {
try {
Store store = mAccount.getRemoteStore();
if (store instanceof ImapStore) {
boolean isLocalOnly = ((LocalFolder)folder.folder).isLocalOnly();
if (store instanceof Pop3Store || isLocalOnly) {
boolean result = mAccount.getLocalStore().delete(folder.name);
if (result && mAccount.isSpecialFolder(folder.name)) {
resetSpecialFolders(folder.name, K9.FOLDER_NONE);
}
String toastText = "Deletion of folder \"" + folder.name +
((result) ? "\" succeeded." : "\" failed.");
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
onRefresh(false);
} else if (store instanceof ImapStore) {
boolean result = ((ImapStore)store).delete(folder.name);
if (result) {
mAccount.getLocalStore().delete(folder.name);
@ -834,28 +884,17 @@ public class FolderList extends K9ListActivity {
} else if (store instanceof WebDavStore) {
String toastText = "Deleting WebDav Folders not currently implemented.";
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
} else if (store instanceof Pop3Store) {
boolean result = mAccount.getLocalStore().delete(folder.name);
if (result && mAccount.isSpecialFolder(folder.name)) {
resetSpecialFolders(folder.name, K9.FOLDER_NONE);
}
String toastText = "Deletion of folder \"" + folder.name +
((result) ? "\" succeeded." : "\" failed.");
Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG).show();
onRefresh(false);
} else {
Log.d(K9.LOG_TAG, "Unhandled store type " + store.getClass());
}
} catch (com.fsck.k9.mail.MessagingException me) {
Log.e(K9.LOG_TAG, "MessagingException trying to deletefolder \"" +
Log.e(K9.LOG_TAG, "MessagingException trying to delete folder \"" +
folder.name + "\": " + me);
}
}
});
dialog.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked cancel so do some stuff */
}
public void onClick(DialogInterface dialog, int whichButton) {}
});
dialog.show();
}
@ -921,18 +960,28 @@ public class FolderList extends K9ListActivity {
getMenuInflater().inflate(R.menu.folder_context, menu);
FolderInfoHolder folder = (FolderInfoHolder) mAdapter.getItem(info.position);
boolean isLocalOnly = ((LocalFolder)folder.folder).isLocalOnly();
boolean isExpungeCapable = false;
try {
isExpungeCapable = mAccount.getRemoteStore().isExpungeCapable();
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "MessagingException trying to get remote store " + e);
}
menu.setHeaderTitle(folder.displayName);
if (!folder.name.equals(mAccount.getTrashFolderName()))
if (!folder.name.equals(mAccount.getTrashFolderName())) {
menu.findItem(R.id.empty_trash).setVisible(false);
}
if (folder.name.equals(mAccount.getOutboxFolderName())) {
menu.findItem(R.id.check_mail).setVisible(false);
} else {
menu.findItem(R.id.send_messages).setVisible(false);
}
if (K9.ERROR_FOLDER_NAME.equals(folder.name)) {
if (isLocalOnly) {
menu.findItem(R.id.check_mail).setVisible(false);
}
if (K9.ERROR_FOLDER_NAME.equals(folder.name) || isLocalOnly || !isExpungeCapable) {
menu.findItem(R.id.expunge).setVisible(false);
}
@ -946,6 +995,11 @@ public class FolderList extends K9ListActivity {
menu.findItem(R.id.rename_folder).setVisible(false);
menu.findItem(R.id.delete_folder).setVisible(false);
}
if (mAccount.getStoreUri().startsWith("webdav")) {
menu.findItem(R.id.create_folder).setVisible(false);
menu.findItem(R.id.rename_folder).setVisible(false);
menu.findItem(R.id.delete_folder).setVisible(false);
}
menu.setHeaderTitle(folder.displayName);
}

View File

@ -245,11 +245,11 @@ public class MessageList
private boolean mTouchView = true;
private int mPreviewLines = 0;
private MessageListAdapter mAdapter;
private View mFooterView;
private FolderInfoHolder mCurrentFolder;
private boolean mLocalOnly = false;
private LayoutInflater mInflater;
@ -729,8 +729,26 @@ public class MessageList
mCurrentFolder = mAdapter.getFolder(mFolderName, mAccount);
}
// Hide "Load up to x more" footer for search views
mFooterView.setVisibility((mQueryString != null) ? View.GONE : View.VISIBLE);
// ASH this seems wrong, but it works for now.
if (mCurrentFolder != null) {
LocalFolder folder = (LocalFolder)mCurrentFolder.folder;
if (folder != null) {
try {
folder.open(Folder.OpenMode.READ_ONLY);
mLocalOnly = folder.isLocalOnly();
} catch(com.fsck.k9.mail.MessagingException e) {
Log.e("ASH", "ack! " + e);
}
}
Log.d("ASH", "mLocalOnly = " + mLocalOnly + " for " + mCurrentFolder.name);
} else {
// should mLocalOnly be true or false or ???
// if true, it hides "Load up to x more", but this should be hidden anyway.
// it also hides R.id.check_mail and R.id.expunge
}
// Hide "Load up to x more" footer for search views and local-only folders
mFooterView.setVisibility((mQueryString != null || mLocalOnly) ? View.GONE : View.VISIBLE);
mController = MessagingController.getInstance(getApplication());
mListView.setAdapter(mAdapter);
@ -1527,6 +1545,10 @@ public class MessageList
onMove(selection);
return true;
}
case R.id.batch_upload_op: {
onUpload(selection);
return true;
}
case R.id.expunge: {
if (mCurrentFolder != null) {
onExpunge(mAccount, mCurrentFolder.name);
@ -1569,18 +1591,28 @@ public class MessageList
menu.findItem(R.id.batch_spam_op).setVisible(false);
menu.findItem(R.id.batch_move_op).setVisible(false);
menu.findItem(R.id.batch_copy_op).setVisible(false);
menu.findItem(R.id.batch_upload_op).setVisible(false);
menu.findItem(R.id.check_mail).setVisible(false);
menu.findItem(R.id.send_messages).setVisible(false);
menu.findItem(R.id.folder_settings).setVisible(false);
menu.findItem(R.id.account_settings).setVisible(false);
} else {
boolean isExpungeCapable = false;
try {
isExpungeCapable = mAccount.getRemoteStore().isExpungeCapable();
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "MessagingException trying to get remote store " + e);
}
if (mCurrentFolder != null && mCurrentFolder.name.equals(mAccount.getOutboxFolderName())) {
menu.findItem(R.id.check_mail).setVisible(false);
} else {
menu.findItem(R.id.send_messages).setVisible(false);
}
if (mCurrentFolder != null && K9.ERROR_FOLDER_NAME.equals(mCurrentFolder.name)) {
if (mLocalOnly) {
menu.findItem(R.id.check_mail).setEnabled(false);
}
if ((mCurrentFolder != null && K9.ERROR_FOLDER_NAME.equals(mCurrentFolder.name)) ||
mLocalOnly || !isExpungeCapable) {
menu.findItem(R.id.expunge).setVisible(false);
}
if (K9.FOLDER_NONE.equalsIgnoreCase(mAccount.getArchiveFolderName())) {
@ -1589,6 +1621,14 @@ public class MessageList
if (K9.FOLDER_NONE.equalsIgnoreCase(mAccount.getSpamFolderName())) {
menu.findItem(R.id.batch_spam_op).setVisible(false);
}
try {
if (((com.fsck.k9.mail.store.LocalStore.LocalFolder)mCurrentFolder.folder).isLocalOnly() ||
!mAccount.getRemoteStore().isAppendCapable()) {
menu.findItem(R.id.batch_upload_op).setVisible(false);
}
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "Error trying to get remote store: " + e);
}
}
boolean newFlagState = computeBatchDirection(true);
@ -1677,6 +1717,10 @@ public class MessageList
onCopy(selection);
break;
}
case R.id.upload: {
onUpload(selection);
break;
}
case R.id.send_alternate: {
onSendAlternate(mAccount, holder);
break;
@ -1776,6 +1820,16 @@ public class MessageList
menu.findItem(R.id.spam).setVisible(false);
}
try {
if (!message.uid.startsWith(K9.LOCAL_UID_PREFIX) || mQueryString != null ||
((com.fsck.k9.mail.store.LocalStore.LocalFolder)message.message.getFolder()).isLocalOnly() ||
!account.getRemoteStore().isAppendCapable()) {
menu.findItem(R.id.upload).setVisible(false);
}
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "Error trying to get remote store: " + e);
}
if (message.selected) {
menu.findItem(R.id.select).setVisible(false);
menu.findItem(R.id.deselect).setVisible(true);
@ -2262,6 +2316,10 @@ public class MessageList
holder.chip.setBackgroundDrawable(message.message.getFolder().getAccount().generateColorChip().drawable());
holder.chip.getBackground().setAlpha(message.read ? 127 : 255);
view.getBackground().setAlpha(message.downloaded ? 0 : 127);
if (message.uid.startsWith(K9.LOCAL_UID_PREFIX)) {
view.setBackgroundColor(message.message.getFolder().getAccount().getChipColor());
view.getBackground().setAlpha(31);
}
if ((message.message.getSubject() == null) || message.message.getSubject().equals("")) {
holder.subject.setText(getText(R.string.general_no_subject));
@ -2662,6 +2720,31 @@ public class MessageList
displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, folder, holders);
}
/**
* Append messages to server.
*
* @param holders
* Never {@code null}.
*/
private void onUpload(final List<MessageInfoHolder> holders) {
if (holders.isEmpty()) {
return;
}
boolean isAppendCapable = false;
try {
isAppendCapable = mAccount.getRemoteStore().isAppendCapable();
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "Error trying to get remote store: " + e);
}
for (MessageInfoHolder holder : holders) {
if (holder.uid.startsWith(K9.LOCAL_UID_PREFIX) && !((LocalFolder)holder.folder.folder).isLocalOnly() && isAppendCapable) {
mController.saveMessage(mAccount, holder.message, holder.folder.name);
} else {
Log.d("ASH", "cannot sync " + holder.folder.name + " " + holder.uid + " " + holder.message.getSubject());
}
}
}
/**
* Helper method to manage the invocation of
* {@link #startActivityForResult(Intent, int)} for a folder operation

View File

@ -752,6 +752,19 @@ public class MessageView extends K9Activity implements OnClickListener {
startActivityForResult(intent, activity);
}
private void onUpload() {
boolean isAppendCapable = false;
try {
isAppendCapable = mAccount.getRemoteStore().isAppendCapable();
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "Error trying to get remote store: " + e);
}
if (mMessageReference.uid.startsWith(K9.LOCAL_UID_PREFIX) && !((com.fsck.k9.mail.store.LocalStore.LocalFolder)mMessage.getFolder()).isLocalOnly() && isAppendCapable) {
mController.saveMessage(mAccount, mMessage, mMessageReference.folderName);
} else {
Log.d("ASH", "cannot sync " + mMessageReference.folderName + " " + mMessageReference.uid + " " + mMessage.getSubject());
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
@ -965,6 +978,9 @@ public class MessageView extends K9Activity implements OnClickListener {
case R.id.copy:
onCopy();
break;
case R.id.upload:
onUpload();
break;
case R.id.show_full_header:
runOnUiThread(new Runnable() {
@Override
@ -993,6 +1009,15 @@ public class MessageView extends K9Activity implements OnClickListener {
if (K9.FOLDER_NONE.equalsIgnoreCase(mAccount.getSpamFolderName())) {
menu.findItem(R.id.spam).setVisible(false);
}
try {
if (!mMessage.getUid().startsWith(K9.LOCAL_UID_PREFIX) ||
((com.fsck.k9.mail.store.LocalStore.LocalFolder)mMessage.getFolder()).isLocalOnly() ||
!mAccount.getRemoteStore().isAppendCapable()) {
menu.findItem(R.id.upload).setVisible(false);
}
} catch (com.fsck.k9.mail.MessagingException e) {
Log.e(K9.LOG_TAG, "Error trying to get remote store: " + e);
}
return true;
}
@ -1057,6 +1082,8 @@ public class MessageView extends K9Activity implements OnClickListener {
}
public void displayMessageBody(final Account account, final String folder, final String uid, final Message message) {
Log.d("ASH", MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL) + " " + MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_FULL));
Log.d("ASH", message.isSet(Flag.X_DOWNLOADED_PARTIAL) + " " + message.isSet(Flag.X_DOWNLOADED_FULL));
runOnUiThread(new Runnable() {
public void run() {
mTopView.scrollTo(0, 0);

View File

@ -86,6 +86,7 @@ public class AccountSettings extends K9PreferenceActivity {
private static final String PREFERENCE_MESSAGE_AGE = "account_message_age";
private static final String PREFERENCE_MESSAGE_SIZE = "account_autodownload_size";
private static final String PREFERENCE_SAVE_ALL_HEADERS = "account_save_all_headers";
private static final String PREFERENCE_AUTO_UPLOAD_ON_MOVE = "account_auto_upload_on_move";
private static final String PREFERENCE_MESSAGE_FORMAT = "message_format";
private static final String PREFERENCE_MESSAGE_READ_RECEIPT = "message_read_receipt";
private static final String PREFERENCE_QUOTE_PREFIX = "account_quote_prefix";
@ -112,6 +113,7 @@ public class AccountSettings extends K9PreferenceActivity {
private Account mAccount;
private boolean mIsPushCapable = false;
private boolean mIsExpungeCapable = false;
private boolean mIsAppendCapable = false;
private PreferenceScreen mComposingScreen;
@ -155,6 +157,7 @@ public class AccountSettings extends K9PreferenceActivity {
private CheckBoxPreference mStripSignature;
private CheckBoxPreference mSyncRemoteDeletions;
private CheckBoxPreference mSaveAllHeaders;
private CheckBoxPreference mAutoUploadOnMove;
private CheckBoxPreference mPushPollOnConnect;
private ListPreference mIdleRefreshPeriod;
private ListPreference mMaxPushFolders;
@ -189,6 +192,7 @@ public class AccountSettings extends K9PreferenceActivity {
final Store store = mAccount.getRemoteStore();
mIsPushCapable = store.isPushCapable();
mIsExpungeCapable = store.isExpungeCapable();
mIsAppendCapable = store.isAppendCapable();
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Could not get remote store", e);
}
@ -362,6 +366,13 @@ public class AccountSettings extends K9PreferenceActivity {
mSaveAllHeaders = (CheckBoxPreference) findPreference(PREFERENCE_SAVE_ALL_HEADERS);
mSaveAllHeaders.setChecked(mAccount.saveAllHeaders());
mAutoUploadOnMove = (CheckBoxPreference) findPreference(PREFERENCE_AUTO_UPLOAD_ON_MOVE);
mAutoUploadOnMove.setChecked(mAccount.isAutoUploadOnMove() && mIsAppendCapable);
if (!mIsAppendCapable || !K9.isShowAdvancedOptions()) {
((PreferenceScreen) findPreference(PREFERENCE_SCREEN_INCOMING)).
removePreference(mAutoUploadOnMove);
}
mSearchableFolders = (ListPreference) findPreference(PREFERENCE_SEARCHABLE_FOLDERS);
mSearchableFolders.setValue(mAccount.getSearchableFolders().name());
mSearchableFolders.setSummary(mSearchableFolders.getEntry());
@ -721,6 +732,7 @@ public class AccountSettings extends K9PreferenceActivity {
}
mAccount.setSyncRemoteDeletions(mSyncRemoteDeletions.isChecked());
mAccount.setSaveAllHeaders(mSaveAllHeaders.isChecked());
mAccount.setAutoUploadOnMove(mAutoUploadOnMove.isChecked());
mAccount.setSearchableFolders(Account.Searchable.valueOf(mSearchableFolders.getValue()));
mAccount.setMessageFormat(Account.MessageFormat.valueOf(mMessageFormat.getValue()));
mAccount.setMessageReadReceipt(mMessageReadReceipt.isChecked());

View File

@ -7,15 +7,19 @@ import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.util.Log;
import com.fsck.k9.*;
import com.fsck.k9.activity.K9PreferenceActivity;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Folder.FolderClass;
import com.fsck.k9.mail.Folder.OpenMode;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
import com.fsck.k9.mail.store.Pop3Store;
import com.fsck.k9.service.MailService;
public class FolderSettings extends K9PreferenceActivity {
@ -29,11 +33,14 @@ public class FolderSettings extends K9PreferenceActivity {
private static final String PREFERENCE_PUSH_CLASS = "folder_settings_folder_push_mode";
private static final String PREFERENCE_IN_TOP_GROUP = "folder_settings_in_top_group";
private static final String PREFERENCE_INTEGRATE = "folder_settings_include_in_integrated_inbox";
private static final String PREFERENCE_LOCAL_ONLY = "folder_settings_local_only";
private Account mAccount;
private LocalFolder mFolder;
private CheckBoxPreference mInTopGroup;
private CheckBoxPreference mIntegrate;
private CheckBoxPreference mLocalOnly;
private ListPreference mDisplayClass;
private ListPreference mSyncClass;
private ListPreference mPushClass;
@ -51,7 +58,7 @@ public class FolderSettings extends K9PreferenceActivity {
String folderName = (String)getIntent().getSerializableExtra(EXTRA_FOLDER_NAME);
String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
Account mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
try {
LocalStore localStore = mAccount.getLocalStore();
@ -73,7 +80,7 @@ public class FolderSettings extends K9PreferenceActivity {
addPreferencesFromResource(R.xml.folder_settings_preferences);
Preference category = findPreference(PREFERENCE_TOP_CATERGORY);
PreferenceCategory category = (PreferenceCategory)findPreference(PREFERENCE_TOP_CATERGORY);
category.setTitle(folderName);
@ -81,6 +88,8 @@ public class FolderSettings extends K9PreferenceActivity {
mInTopGroup.setChecked(mFolder.isInTopGroup());
mIntegrate = (CheckBoxPreference)findPreference(PREFERENCE_INTEGRATE);
mIntegrate.setChecked(mFolder.isIntegrate());
mLocalOnly = (CheckBoxPreference)findPreference(PREFERENCE_LOCAL_ONLY);
mLocalOnly.setChecked(mFolder.isLocalOnly());
mDisplayClass = (ListPreference) findPreference(PREFERENCE_DISPLAY_CLASS);
mDisplayClass.setValue(mFolder.getDisplayClass().name());
@ -96,6 +105,7 @@ public class FolderSettings extends K9PreferenceActivity {
});
mSyncClass = (ListPreference) findPreference(PREFERENCE_SYNC_CLASS);
mSyncClass.setEnabled(!mLocalOnly.isChecked());
mSyncClass.setValue(mFolder.getRawSyncClass().name());
mSyncClass.setSummary(mSyncClass.getEntry());
mSyncClass.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@ -107,9 +117,12 @@ public class FolderSettings extends K9PreferenceActivity {
return false;
}
});
if (store instanceof Pop3Store) {
category.removePreference(mSyncClass);
}
mPushClass = (ListPreference) findPreference(PREFERENCE_PUSH_CLASS);
mPushClass.setEnabled(isPushCapable);
mPushClass.setEnabled(isPushCapable && !mLocalOnly.isChecked());
mPushClass.setValue(mFolder.getRawPushClass().name());
mPushClass.setSummary(mPushClass.getEntry());
mPushClass.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@ -121,20 +134,55 @@ public class FolderSettings extends K9PreferenceActivity {
return false;
}
});
if (store instanceof Pop3Store) {
category.removePreference(mPushClass);
}
mLocalOnly = (CheckBoxPreference)findPreference(PREFERENCE_LOCAL_ONLY);
mLocalOnly.setChecked(mFolder.isLocalOnly());
if (store instanceof Pop3Store || mAccount.getInboxFolderName().equals(folderName) ||
mAccount.getOutboxFolderName().equals(folderName)) {
mLocalOnly.setEnabled(false);
}
if (!K9.isShowAdvancedOptions()) {// ASH disabled for testing: || store instanceof Pop3Store) {
category.removePreference(mLocalOnly);
}
}
private void saveSettings() throws MessagingException {
mFolder.setInTopGroup(mInTopGroup.isChecked());
mFolder.setIntegrate(mIntegrate.isChecked());
boolean oldIsLocalOnly = mFolder.isLocalOnly();
mFolder.setLocalOnly(mLocalOnly.isChecked());
// We call getPushClass() because display class changes can affect push class when push class is set to inherit
FolderClass oldPushClass = mFolder.getPushClass();
FolderClass oldDisplayClass = mFolder.getDisplayClass();
mFolder.setDisplayClass(FolderClass.valueOf(mDisplayClass.getValue()));
mFolder.setSyncClass(FolderClass.valueOf(mSyncClass.getValue()));
mFolder.setPushClass(FolderClass.valueOf(mPushClass.getValue()));
if (mLocalOnly.isChecked()) {
mFolder.setSyncClass(FolderClass.NO_CLASS);
mFolder.setPushClass(FolderClass.NO_CLASS);
} else {
mFolder.setSyncClass(FolderClass.valueOf(mSyncClass.getValue()));
mFolder.setPushClass(FolderClass.valueOf(mPushClass.getValue()));
}
mFolder.save();
if (!oldIsLocalOnly && mFolder.isLocalOnly()) {
Log.w(K9.LOG_TAG, "Changing UIDs of messages in folder " + mFolder.getName() +
" to local UIDs.");
MessagingController.getInstance(getApplication()).localizeUids(mFolder);
} else if (oldIsLocalOnly && !mFolder.isLocalOnly()) {
// create folder if it does not exist.
Folder folder = mAccount.getRemoteStore().getFolder(mFolder.getName());
folder.close();
if (!folder.exists()) {
Log.w(K9.LOG_TAG, "creating remote folder " + mFolder.getName());
folder.create();
}
}
FolderClass newPushClass = mFolder.getPushClass();
FolderClass newDisplayClass = mFolder.getDisplayClass();

View File

@ -67,6 +67,7 @@ public class Prefs extends K9PreferenceActivity {
private static final String PREFERENCE_MESSAGELIST_CONTACT_NAME_COLOR = "messagelist_contact_name_color";
private static final String PREFERENCE_MESSAGEVIEW_FIXEDWIDTH = "messageview_fixedwidth_font";
private static final String PREFERENCE_COMPACT_LAYOUTS = "compact_layouts";
private static final String PREFERENCE_SHOW_ADVANCED_OPTIONS = "show_advanced_options";
private static final String PREFERENCE_MESSAGEVIEW_RETURN_TO_LIST = "messageview_return_to_list";
private static final String PREFERENCE_MESSAGEVIEW_SHOW_NEXT = "messageview_show_next";
@ -115,6 +116,7 @@ public class Prefs extends K9PreferenceActivity {
private CheckBoxPreference mDebugLogging;
private CheckBoxPreference mSensitiveLogging;
private CheckBoxPreference compactLayouts;
private CheckBoxPreference showAdvancedOptions;
private CheckBoxPreference mQuietTimeEnabled;
private com.fsck.k9.preferences.TimePickerPreference mQuietTimeStarts;
@ -179,6 +181,9 @@ public class Prefs extends K9PreferenceActivity {
compactLayouts = (CheckBoxPreference)findPreference(PREFERENCE_COMPACT_LAYOUTS);
compactLayouts.setChecked(K9.useCompactLayouts());
showAdvancedOptions = (CheckBoxPreference)findPreference(PREFERENCE_SHOW_ADVANCED_OPTIONS);
showAdvancedOptions.setChecked(K9.isShowAdvancedOptions());
mVolumeNavigation = (CheckBoxListPreference)findPreference(PREFERENCE_VOLUME_NAVIGATION);
mVolumeNavigation.setItems(new CharSequence[] {getString(R.string.volume_navigation_message), getString(R.string.volume_navigation_list)});
mVolumeNavigation.setCheckedItems(new boolean[] {K9.useVolumeKeysForNavigationEnabled(), K9.useVolumeKeysForListNavigationEnabled()});
@ -352,6 +357,7 @@ public class Prefs extends K9PreferenceActivity {
K9.setAnimations(mAnimations.isChecked());
K9.setGesturesEnabled(mGestures.isChecked());
K9.setCompactLayouts(compactLayouts.isChecked());
K9.setShowAdvancedOptions(showAdvancedOptions.isChecked());
K9.setUseVolumeKeysForNavigation(mVolumeNavigation.getCheckedItems()[0]);
K9.setUseVolumeKeysForListNavigation(mVolumeNavigation.getCheckedItems()[1]);
K9.setManageBack(mManageBack.isChecked());

View File

@ -27,6 +27,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.fsck.k9.Account;
import com.fsck.k9.AccountStats;
@ -476,20 +477,28 @@ public class MessagingController implements Runnable {
localFolders = localStore.getPersonalNamespaces(false);
/*
* Clear out any folders that are no longer on the remote store,
* unless they are tagged as local-only or are special folders.
* Clear out any folders that are no longer on the remote store, unless they
* are tagged as local-only or are special folders. If a folder contains
* messages with UIDs starting with K9.LOCAL_UID_PREFIX, then those messages
* are saved and the folder is converted to local-only.
*/
// ASH todo: also don't clear out folders with K9.LOCAL_UID_PREFIX
for (LocalFolder localFolder : localFolders) {
String localFolderName = localFolder.getName();
if (!account.isSpecialFolder(localFolderName) &&
!remoteFolderNames.contains(localFolderName) &&
!localFolder.isLocalOnly()) {
localFolder.delete(false);
for (Message message : localFolder.getMessages(null)) {
if (message.getUid().startsWith(K9.LOCAL_UID_PREFIX)) {
localFolder.clearAllMessages(false);
localFolder.setLocalOnly(true);
break;
}
}
if (!localFolder.isLocalOnly()) {
localFolder.delete(false);
}
}
if (remoteFolderNames.contains(localFolderName)) {
if (localFolder.isLocalOnly()) localFolder.setLocalOnly(false);
} else if (localFolder.exists()) {
if (!remoteFolderNames.contains(localFolderName) && localFolder.exists()) {
if (!localFolder.isLocalOnly()) localFolder.setLocalOnly(true);
}
}
@ -1016,8 +1025,9 @@ public class MessagingController implements Runnable {
ArrayList<Message> missingMessages = new ArrayList<Message>();
for (Message localMessage : localMessages) {
String uid = localMessage.getUid();
if (!uid.startsWith(K9.LOCAL_UID_PREFIX) && !remoteUidMap.containsKey(uid)) {
// This message used to be on the server
if ((!uid.startsWith(K9.LOCAL_UID_PREFIX) && !remoteUidMap.containsKey(uid)) ||
(uid.startsWith(K9.LOCAL_UID_PREFIX) && localMessage.isSet(Flag.DELETED))) {
// This message used to be on the server, or is local and should be expunged
missingMessages.add(localMessage);
}
}
@ -1988,6 +1998,12 @@ public class MessagingController implements Runnable {
LocalStore localStore = account.getLocalStore();
localFolder = localStore.getFolder(folder);
localFolder.open(OpenMode.READ_WRITE);
if (localFolder.isLocalOnly()) {
return;
}
LocalMessage localMessage = (LocalMessage) localFolder.getMessage(uid);
if (localMessage == null) {
@ -2101,15 +2117,26 @@ public class MessagingController implements Runnable {
if (account.getErrorFolderName().equals(srcFolder)) {
return;
}
final ArrayList<String> remoteUids = new ArrayList<String>();
for (String uid : uids) {
// ignore unsynced messages
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
remoteUids.add(uid);
}
}
if (remoteUids.size() == 0) {
return;
}
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK;
int length = 3 + uids.length;
int length = 3 + remoteUids.size();
command.arguments = new String[length];
command.arguments[0] = srcFolder;
command.arguments[1] = destFolder;
command.arguments[2] = Boolean.toString(isCopy);
System.arraycopy(uids, 0, command.arguments, 3, uids.length);
System.arraycopy(remoteUids.toArray(EMPTY_STRING_ARRAY), 0, command.arguments, 3,
remoteUids.size());
queuePendingCommand(account, command);
}
/**
@ -2222,13 +2249,13 @@ public class MessagingController implements Runnable {
remoteFolder = remoteStore.getFolder(folder);
if (!remoteFolder.exists()) {
if (!remoteFolder.create()) {
// ASH log something?
Log.w(K9.LOG_TAG, "updateUids: Cannot create remote folder " + folder);
return;
}
}
remoteFolder.open(OpenMode.READ_WRITE);
if (remoteFolder.getMode() != OpenMode.READ_WRITE) {
// ASH log something?
Log.w(K9.LOG_TAG, "updateUids: Cannot open remote folder " + folder);
return;
}
@ -2247,12 +2274,12 @@ public class MessagingController implements Runnable {
for (MessagingListener l : getListeners()) {
l.messageUidChanged(account, folder, oldUid, localMessage.getUid());
}
} else {
}/* else {
Log.w(K9.LOG_TAG, "No remote message with message-id found, appending instead.");
/*
* If the message does not exist remotely we just upload it and then
* update our local copy with the new uid.
*/
*//*
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.BODY);
localFolder.fetch(new Message[] { localMessage } , fp, null);
@ -2264,7 +2291,7 @@ public class MessagingController implements Runnable {
for (MessagingListener l : getListeners()) {
l.messageUidChanged(account, folder, oldUid, localMessage.getUid());
}
}
}*/
}
} finally {
closeFolder(localFolder);
@ -2272,19 +2299,47 @@ public class MessagingController implements Runnable {
}
}
public void localizeUids(LocalFolder folder) throws MessagingException {
Message[] messages = folder.getMessages(null);
for (Message message : messages) {
String oldUid = message.getUid();
Log.d("ASH", "old UID = " + oldUid);
if (!oldUid.startsWith(K9.LOCAL_UID_PREFIX)) {
String newUid = K9.LOCAL_UID_PREFIX + java.util.UUID.randomUUID().toString();
Log.d("ASH", "new UID = " + newUid);
message.setUid(newUid);
folder.changeUid((LocalMessage)message);
for (MessagingListener l : getListeners()) {
l.messageUidChanged(folder.getAccount(), folder.getName(), oldUid, newUid);
}
}
}
}
private void queueSetFlag(final Account account, final String folderName, final String newState, final String flag, final String[] uids) {
final ArrayList<String> remoteUids = new ArrayList<String>();
for (String uid : uids) {
// ignore unsynced messages
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
remoteUids.add(uid);
}
}
if (remoteUids.size() == 0) {
return;
}
putBackground("queueSetFlag " + account.getDescription() + ":" + folderName, null, new Runnable() {
@Override
public void run() {
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_SET_FLAG_BULK;
int length = 3 + uids.length;
int length = 3 + remoteUids.size();
command.arguments = new String[length];
command.arguments[0] = folderName;
command.arguments[1] = newState;
command.arguments[2] = flag;
System.arraycopy(uids, 0, command.arguments, 3, uids.length);
System.arraycopy(remoteUids.toArray(EMPTY_STRING_ARRAY), 0, command.arguments, 3,
remoteUids.size());
queuePendingCommand(account, command);
processPendingCommands(account);
}
@ -2375,7 +2430,9 @@ public class MessagingController implements Runnable {
closeFolder(remoteFolder);
}
}
private void queueExpunge(final Account account, final String folderName) {
// ASH if folder.isLocalOnly() return ?
putBackground("queueExpunge " + account.getDescription() + ":" + folderName, null, new Runnable() {
@Override
public void run() {
@ -2390,6 +2447,7 @@ public class MessagingController implements Runnable {
}
});
}
private void processPendingExpunge(PendingCommand command, Account account)
throws MessagingException {
String folder = command.arguments[0];
@ -2445,6 +2503,10 @@ public class MessagingController implements Runnable {
return;
}
if (localFolder.isLocalOnly()) {
return;
}
Store remoteStore = account.getRemoteStore();
remoteFolder = remoteStore.getFolder(folder);
@ -2576,9 +2638,9 @@ public class MessagingController implements Runnable {
final boolean newState) {
// TODO: put this into the background, but right now that causes odd behavior
// because the FolderMessageList doesn't have its own cache of the flag states
Folder localFolder = null;
LocalFolder localFolder = null;
try {
Store localStore = account.getLocalStore();
LocalStore localStore = account.getLocalStore();
localFolder = localStore.getFolder(folderName);
localFolder.open(OpenMode.READ_WRITE);
ArrayList<Message> messages = new ArrayList<Message>();
@ -2606,6 +2668,10 @@ public class MessagingController implements Runnable {
return;
}
if (localFolder.isLocalOnly()) {
return;
}
queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids);
processPendingCommands(account);
} catch (MessagingException me) {
@ -2633,75 +2699,95 @@ public class MessagingController implements Runnable {
put("loadMessageForViewRemote", listener, new Runnable() {
@Override
public void run() {
Folder remoteFolder = null;
LocalFolder localFolder = null;
try {
LocalStore localStore = account.getLocalStore();
localFolder = localStore.getFolder(folder);
localFolder.open(OpenMode.READ_WRITE);
Message message = localFolder.getMessage(uid);
if (message.isSet(Flag.X_DOWNLOADED_FULL)) {
/*
* If the message has been synchronized since we were called we'll
* just hand it back cause it's ready to go.
*/
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(FetchProfile.Item.BODY);
localFolder.fetch(new Message[] { message }, fp, null);
} else {
/*
* At this point the message is not available, so we need to download it
* fully if possible.
*/
Store remoteStore = account.getRemoteStore();
remoteFolder = remoteStore.getFolder(folder);
remoteFolder.open(OpenMode.READ_WRITE);
// Get the remote message and fully download it
Message remoteMessage = remoteFolder.getMessage(uid);
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.BODY);
remoteFolder.fetch(new Message[] { remoteMessage }, fp, null);
// Store the message locally and load the stored message into memory
localFolder.appendMessages(new Message[] { remoteMessage });
fp.add(FetchProfile.Item.ENVELOPE);
message = localFolder.getMessage(uid);
localFolder.fetch(new Message[] { message }, fp, null);
// Mark that this message is now fully synched
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
}
// now that we have the full message, refresh the headers
for (MessagingListener l : getListeners(listener)) {
l.loadMessageForViewHeadersAvailable(account, folder, uid, message);
}
for (MessagingListener l : getListeners(listener)) {
l.loadMessageForViewBodyAvailable(account, folder, uid, message);
}
for (MessagingListener l : getListeners(listener)) {
l.loadMessageForViewFinished(account, folder, uid, message);
}
} catch (Exception e) {
for (MessagingListener l : getListeners(listener)) {
l.loadMessageForViewFailed(account, folder, uid, e);
}
addErrorMessage(account, null, e);
} finally {
closeFolder(remoteFolder);
closeFolder(localFolder);
}
}//run
loadMessageForViewRemoteSynchronous(account, folder, uid, listener);
}
});
}
public boolean loadMessageForViewRemoteSynchronous(final Account account, final String folder,
final String uid, final MessagingListener listener) {
Folder remoteFolder = null;
LocalFolder localFolder = null;
try {
LocalStore localStore = account.getLocalStore();
localFolder = localStore.getFolder(folder);
localFolder.open(OpenMode.READ_WRITE);
Message message = localFolder.getMessage(uid);
if (uid.startsWith(K9.LOCAL_UID_PREFIX)) {
Log.w(K9.LOG_TAG, "Message has local UID so cannot download fully.");
Toast.makeText(mApplication, "Message has local UID so cannot download fully",
Toast.LENGTH_LONG).show();
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
message.setFlag(Flag.X_DOWNLOADED_PARTIAL, false);
} else if (localFolder.isLocalOnly()) {
Log.w(K9.LOG_TAG, "Message in local-only folder so cannot download fully.");
Toast.makeText(mApplication, "Message in local-only folder so cannot download fully",
Toast.LENGTH_LONG).show();
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
message.setFlag(Flag.X_DOWNLOADED_PARTIAL, false);
}
if (message.isSet(Flag.X_DOWNLOADED_FULL)) {
/*
* If the message has been synchronized since we were called we'll
* just hand it back cause it's ready to go.
*/
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(FetchProfile.Item.BODY);
localFolder.fetch(new Message[] { message }, fp, null);
} else {
/*
* At this point the message is not available, so we need to download it
* fully if possible.
*/
Store remoteStore = account.getRemoteStore();
remoteFolder = remoteStore.getFolder(folder);
remoteFolder.open(OpenMode.READ_WRITE);
// Get the remote message and fully download it
Message remoteMessage = remoteFolder.getMessage(uid);
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.BODY);
remoteFolder.fetch(new Message[] { remoteMessage }, fp, null);
// Store the message locally and load the stored message into memory
localFolder.appendMessages(new Message[] { remoteMessage });
fp.add(FetchProfile.Item.ENVELOPE);
message = localFolder.getMessage(uid);
localFolder.fetch(new Message[] { message }, fp, null);
// Mark that this message is now fully synched
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
}
// now that we have the full message, refresh the headers
for (MessagingListener l : getListeners(listener)) {
l.loadMessageForViewHeadersAvailable(account, folder, uid, message);
}
for (MessagingListener l : getListeners(listener)) {
l.loadMessageForViewBodyAvailable(account, folder, uid, message);
}
for (MessagingListener l : getListeners(listener)) {
l.loadMessageForViewFinished(account, folder, uid, message);
}
return true;
} catch (Exception e) {
for (MessagingListener l : getListeners(listener)) {
l.loadMessageForViewFailed(account, folder, uid, e);
}
addErrorMessage(account, null, e);
return false;
} finally {
closeFolder(remoteFolder);
closeFolder(localFolder);
}
}
public void loadMessageForView(final Account account, final String folder, final String uid,
final MessagingListener listener) {
for (MessagingListener l : getListeners(listener)) {
@ -3105,11 +3191,13 @@ public class MessagingController implements Runnable {
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Moved sent message to folder '" + account.getSentFolderName() + "' (" + localSentFolder.getId() + ") ");
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_APPEND;
command.arguments = new String[] { localSentFolder.getName(), message.getUid() };
queuePendingCommand(account, command);
processPendingCommands(account);
if (!localSentFolder.isLocalOnly()) {
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_APPEND;
command.arguments = new String[] { localSentFolder.getName(), message.getUid() };
queuePendingCommand(account, command);
processPendingCommands(account);
}
}
} catch (Exception e) {
@ -3243,61 +3331,105 @@ public class MessagingController implements Runnable {
Store remoteStore = account.getRemoteStore();
LocalFolder localSrcFolder = localStore.getFolder(srcFolder);
LocalFolder localDestFolder = localStore.getFolder(destFolder);
List<String> uids = new LinkedList<String>();
List<String> localUids = new LinkedList<String>();
localSrcFolder.open(OpenMode.READ_WRITE);
boolean needToLocalizeSourceFolder = false;
for (Message message : inMessages) {
String uid = message.getUid();
// ASH fixme: add all messages, and later handle local ones separately?
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
// ASH instead, add all messages, and later handle local ones separately?
if (!uid.startsWith(K9.LOCAL_UID_PREFIX) && !localSrcFolder.isLocalOnly()) {
uids.add(uid);
} else {
localUids.add(uid);
if (isCopy && !uid.startsWith(K9.LOCAL_UID_PREFIX)) {
needToLocalizeSourceFolder = true;
}
}
}
Message[] messages = localSrcFolder.getMessages(uids.toArray(EMPTY_STRING_ARRAY), null);
if (messages.length > 0) {
localDestFolder.open(OpenMode.READ_WRITE);
boolean checkForPartialDownload = (!localSrcFolder.isLocalOnly() &&
localDestFolder.isLocalOnly()) ? true : false;
Map<String, Message> origUidMap = new HashMap<String, Message>();
for (Message message : messages) {
origUidMap.put(message.getUid(), message);
}
if (K9.DEBUG) {
Log.i(K9.LOG_TAG, "moveOrCopyMessageSynchronous: source folder = " + srcFolder
+ ", " + messages.length + " messages, " + ", destination folder = " +
destFolder + ", isCopy = " + isCopy);
}
if (isCopy) {
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(FetchProfile.Item.BODY);
localSrcFolder.fetch(messages, fp, null);
localSrcFolder.copyMessages(messages, localDestFolder);
} else {
localSrcFolder.moveMessages(messages, localDestFolder);
for (Map.Entry<String, Message> entry : origUidMap.entrySet()) {
String origUid = entry.getKey();
Message message = entry.getValue();
for (MessagingListener l : getListeners()) {
l.messageUidChanged(account, srcFolder, origUid, message.getUid());
if (checkForPartialDownload && message.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
// fully download message if it's moved/coied to a local-only folder
Log.d("ASH", "downloading message...");
if (loadMessageForViewRemoteSynchronous(account, srcFolder,
message.getUid(), listener)) {
Log.d("ASH", "downloaded message");
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
message.setFlag(Flag.X_DOWNLOADED_PARTIAL, false);
origUidMap.put(message.getUid(), message);
} else {
Log.e(K9.LOG_TAG, "Cannot download message " + message.getUid() +
" in folder " + srcFolder + " -- skipping it for move/copy.");
Toast.makeText(mApplication, "Cannot download message " +
message.getSubject() + " in folder " + srcFolder +
" -- skipping it for move/copy.", Toast.LENGTH_LONG).show();
}
unsuppressMessage(account, srcFolder, origUid);
} else {
origUidMap.put(message.getUid(), message);
}
}
if (!localDestFolder.isLocalOnly() && ((!isCopy && remoteStore.isMoveCapable()) ||
(isCopy && remoteStore.isCopyCapable()))) {
queueMoveOrCopy(account, srcFolder, destFolder, isCopy,
origUidMap.keySet().toArray(EMPTY_STRING_ARRAY));
} else if (!isCopy && !localSrcFolder.isLocalOnly()) {
queueSetFlag(account, srcFolder, Boolean.toString(true),
Flag.DELETED.toString(),
origUidMap.keySet().toArray(EMPTY_STRING_ARRAY));
if (Account.EXPUNGE_IMMEDIATELY.equals(account.getExpungePolicy())) {
queueExpunge(account, srcFolder);
if (checkForPartialDownload) {
messages = origUidMap.values().toArray(EMPTY_MESSAGE_ARRAY);
}
if (messages.length > 0) {
if (K9.DEBUG) {
Log.i(K9.LOG_TAG, "moveOrCopyMessageSynchronous: source folder = " + srcFolder
+ ", " + messages.length + " messages, " + ", destination folder = " +
destFolder + ", isCopy = " + isCopy);
}
if (isCopy) {
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(FetchProfile.Item.BODY);
localSrcFolder.fetch(messages, fp, null);
localSrcFolder.copyMessages(messages, localDestFolder);
} else {
localSrcFolder.moveMessages(messages, localDestFolder);
for (Map.Entry<String, Message> entry : origUidMap.entrySet()) {
String origUid = entry.getKey();
Message message = entry.getValue();
for (MessagingListener l : getListeners()) {
l.messageUidChanged(account, srcFolder, origUid, message.getUid());
}
unsuppressMessage(account, srcFolder, origUid);
}
}
if (!localDestFolder.isLocalOnly() &&
(isCopy ? remoteStore.isCopyCapable() : remoteStore.isMoveCapable())) {
// synced message copy/move to remote folder
queueMoveOrCopy(account, srcFolder, destFolder, isCopy,
origUidMap.keySet().toArray(EMPTY_STRING_ARRAY));
} else if (!isCopy && !localSrcFolder.isLocalOnly()) {
// synced message move to local folder: delete from source folder ASH maybe a better way.
if (account.getDeletePolicy() == Account.DELETE_POLICY_ON_DELETE) {
queueSetFlag(account, srcFolder, Boolean.toString(true),
Flag.DELETED.toString(),
origUidMap.keySet().toArray(EMPTY_STRING_ARRAY));
if (Account.EXPUNGE_IMMEDIATELY.equals(account.getExpungePolicy())) {
queueExpunge(account, srcFolder);
}
} else if (account.getDeletePolicy() == Account.DELETE_POLICY_MARK_AS_READ) {
queueSetFlag(account, srcFolder, Boolean.toString(true),
Flag.SEEN.toString(),
origUidMap.keySet().toArray(EMPTY_STRING_ARRAY));
} else {
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Delete policy " + account.getDeletePolicy() + " prevents delete from server");
}
}
}
}
@ -3334,17 +3466,22 @@ public class MessagingController implements Runnable {
fp.add(FetchProfile.Item.BODY);
localSrcFolder.fetch(localMessages, fp, null);
if ((!localDestFolder.isLocalOnly()) && ((!isCopy && (remoteStore.isMoveCapable()))
|| (isCopy && (remoteStore.isCopyCapable())))) {
if (account.isAutoUploadOnMove() && !localDestFolder.isLocalOnly() &&
(isCopy ? remoteStore.isCopyCapable() : remoteStore.isMoveCapable())) {
// local message copy/move to remote folder
for (Message message : localMessages) {
saveMessage(account, message, destFolder);
}
} else if (isCopy) {
// local message copy to local folder
localSrcFolder.copyMessages(localMessages, localDestFolder);
}
}
processPendingCommands(account);
if (needToLocalizeSourceFolder) {
localizeUids(localSrcFolder);
}
} catch (UnavailableStorageException e) {
Log.i(K9.LOG_TAG, "Failed to move/copy message because storage is not available - trying again later.");
throw new UnavailableAccountException(e);
@ -3406,10 +3543,10 @@ public class MessagingController implements Runnable {
}
private void deleteMessagesSynchronous(final Account account, final String folder, final Message[] messages,
private void deleteMessagesSynchronous(final Account account, final String folder, Message[] messages,
MessagingListener listener) {
Folder localFolder = null;
Folder localTrashFolder = null;
LocalFolder localFolder = null;
LocalFolder localTrashFolder = null;
String[] uids = getUidsFromMessages(messages);
try {
//We need to make these callbacks before moving the messages to the trash
@ -3419,7 +3556,7 @@ public class MessagingController implements Runnable {
l.messageDeleted(account, folder, message);
}
}
Store localStore = account.getLocalStore();
LocalStore localStore = account.getLocalStore();
localFolder = localStore.getFolder(folder);
if (folder.equals(account.getTrashFolderName()) || K9.FOLDER_NONE.equals(account.getTrashFolderName())) {
if (K9.DEBUG)
@ -3429,12 +3566,54 @@ public class MessagingController implements Runnable {
} else {
localTrashFolder = localStore.getFolder(account.getTrashFolderName());
if (!localTrashFolder.exists()) {
localTrashFolder.create();
if (account.getRemoteStore().isMoveCapable()) {
localTrashFolder.create();
} else {
localTrashFolder.create(true);
}
}
if (localTrashFolder.exists()) {
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Deleting messages in normal folder, moving");
localTrashFolder.open(OpenMode.READ_WRITE);
// download messages first if trash folder is local-only,
// otherwise it's not undoable.
if (account.getDeletePolicy() == Account.DELETE_POLICY_ON_DELETE &&
localTrashFolder.isLocalOnly() && !localFolder.isLocalOnly()) {
Map<String, Message> origUidMap = new HashMap<String, Message>();
Map<String, Message> skipUidMap = new HashMap<String, Message>();
for (Message message : messages) {
if (message.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
Log.d("ASH", "downloading message...");
if (loadMessageForViewRemoteSynchronous(account, folder,
message.getUid(), listener)) {
Log.d("ASH", "downloaded message");
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
message.setFlag(Flag.X_DOWNLOADED_PARTIAL, false);
origUidMap.put(message.getUid(), message);
} else {
skipUidMap.put(message.getUid(), message);
Log.e(K9.LOG_TAG, "Cannot download message -- skipping it for move to trash.");
Toast.makeText(mApplication, "Cannot download message " +
message.getSubject() + " in folder " + folder +
" -- skipping it for move to trash.",
Toast.LENGTH_LONG).show();
}
} else {
origUidMap.put(message.getUid(), message);
}
}
messages = origUidMap.values().toArray(EMPTY_MESSAGE_ARRAY);
uids = origUidMap.keySet().toArray(EMPTY_STRING_ARRAY);
for (String uid : skipUidMap.keySet()) {
unsuppressMessage(account, folder, uid);
}
if (messages.length == 0) {
Log.w(K9.LOG_TAG, "Not deleting any messages.");
return;
}
}
localFolder.moveMessages(messages, localTrashFolder);
}
@ -3451,32 +3630,37 @@ public class MessagingController implements Runnable {
Log.d(K9.LOG_TAG, "Delete policy for account " + account.getDescription() + " is " + account.getDeletePolicy());
if (folder.equals(account.getOutboxFolderName())) {
for (Message message : messages) {
// If the message was in the Outbox, then it has been copied to local Trash, and has
// to be copied to remote trash
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_APPEND;
command.arguments =
new String[] {
account.getTrashFolderName(),
message.getUid()
};
queuePendingCommand(account, command);
localTrashFolder = (LocalFolder)localStore.getFolder(account.getTrashFolderName());
localTrashFolder.open(OpenMode.READ_WRITE);
if (!localTrashFolder.isLocalOnly()) {
for (Message message : messages) {
// If the message was in the Outbox, then it has been copied to local Trash, and has
// to be copied to remote trash
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_APPEND;
command.arguments = new String[] { account.getTrashFolderName(),
message.getUid() };
queuePendingCommand(account, command);
}
processPendingCommands(account);
}
processPendingCommands(account);
} else if (account.getDeletePolicy() == Account.DELETE_POLICY_ON_DELETE) {
if (folder.equals(account.getTrashFolderName())) {
queueSetFlag(account, folder, Boolean.toString(true), Flag.DELETED.toString(), uids);
} else if (!localFolder.isLocalOnly()) {
localTrashFolder = (LocalFolder)localStore.getFolder(account.getTrashFolderName());
localTrashFolder.open(OpenMode.READ_WRITE);
if (account.getDeletePolicy() == Account.DELETE_POLICY_ON_DELETE) {
if (folder.equals(account.getTrashFolderName()) || localTrashFolder.isLocalOnly()) {
queueSetFlag(account, folder, Boolean.toString(true), Flag.DELETED.toString(), uids);
} else {
queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids);
}
processPendingCommands(account);
} else if (account.getDeletePolicy() == Account.DELETE_POLICY_MARK_AS_READ) {
queueSetFlag(account, folder, Boolean.toString(true), Flag.SEEN.toString(), uids);
processPendingCommands(account);
} else {
queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids);
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Delete policy " + account.getDeletePolicy() + " prevents delete from server");
}
processPendingCommands(account);
} else if (account.getDeletePolicy() == Account.DELETE_POLICY_MARK_AS_READ) {
queueSetFlag(account, folder, Boolean.toString(true), Flag.SEEN.toString(), uids);
processPendingCommands(account);
} else {
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Delete policy " + account.getDeletePolicy() + " prevents delete from server");
}
for (String uid : uids) {
unsuppressMessage(account, folder, uid);
@ -3532,8 +3716,8 @@ public class MessagingController implements Runnable {
public void run() {
LocalFolder localFolder = null;
try {
Store localStore = account.getLocalStore();
localFolder = (LocalFolder) localStore.getFolder(account.getTrashFolderName());
LocalStore localStore = account.getLocalStore();
localFolder = localStore.getFolder(account.getTrashFolderName());
localFolder.open(OpenMode.READ_WRITE);
localFolder.setFlags(new Flag[] { Flag.DELETED }, true);
localFolder.resetUnreadAndFlaggedCounts();
@ -3541,12 +3725,14 @@ public class MessagingController implements Runnable {
for (MessagingListener l : getListeners()) {
l.emptyTrashCompleted(account);
}
List<String> args = new ArrayList<String>();
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_EMPTY_TRASH;
command.arguments = args.toArray(EMPTY_STRING_ARRAY);
queuePendingCommand(account, command);
processPendingCommands(account);
if (!localFolder.isLocalOnly()) {
List<String> args = new ArrayList<String>();
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_EMPTY_TRASH;
command.arguments = args.toArray(EMPTY_STRING_ARRAY);
queuePendingCommand(account, command);
processPendingCommands(account);
} // ASH else do we need to destroy local messages? cketti says yes.
} catch (UnavailableStorageException e) {
Log.i(K9.LOG_TAG, "Failed to empty trash because storage is not available - trying again later.");
throw new UnavailableAccountException(e);
@ -3724,11 +3910,16 @@ public class MessagingController implements Runnable {
Account.FolderMode aDisplayMode = account.getFolderDisplayMode();
Account.FolderMode aSyncMode = account.getFolderSyncMode();
Store localStore = account.getLocalStore();
for (final Folder folder : localStore.getPersonalNamespaces(false)) {
LocalStore localStore = account.getLocalStore();
for (final LocalFolder folder : localStore.getPersonalNamespaces(false)) {
folder.open(Folder.OpenMode.READ_WRITE);
folder.refresh(prefs);
if (folder.isLocalOnly()) {
// Never sync a folder that is marked as local-only.
continue;
}
Folder.FolderClass fDisplayClass = folder.getDisplayClass();
Folder.FolderClass fSyncClass = folder.getSyncClass();
@ -4171,14 +4362,13 @@ public class MessagingController implements Runnable {
localMessage = localFolder.getMessage(message.getUid());
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_APPEND;
command.arguments = new String[] {
localFolder.getName(),
localMessage.getUid()
};
queuePendingCommand(account, command);
processPendingCommands(account);
if (!localFolder.isLocalOnly()) {
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_APPEND;
command.arguments = new String[] { localFolder.getName(), localMessage.getUid() };
queuePendingCommand(account, command);
processPendingCommands(account);
}
} catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Unable to save message to " + folderName + ".", e);
@ -4288,8 +4478,8 @@ public class MessagingController implements Runnable {
List<String> names = new ArrayList<String>();
Store localStore = account.getLocalStore();
for (final Folder folder : localStore.getPersonalNamespaces(false)) {
LocalStore localStore = account.getLocalStore();
for (final LocalFolder folder : localStore.getPersonalNamespaces(false)) {
if (folder.getName().equals(account.getErrorFolderName())
|| folder.getName().equals(account.getOutboxFolderName())) {
/*
@ -4303,6 +4493,11 @@ public class MessagingController implements Runnable {
folder.open(Folder.OpenMode.READ_WRITE);
folder.refresh(prefs);
if (folder.isLocalOnly()) {
// Never push a folder that is marked as local-only.
continue;
}
Folder.FolderClass fDisplayClass = folder.getDisplayClass();
Folder.FolderClass fPushClass = folder.getPushClass();

View File

@ -104,11 +104,7 @@ public abstract class Folder {
public void moveMessages(Message[] msgs, Folder folder) throws MessagingException {}
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {
// ASH remove these two lines but keep the empty method
Log.e("ASH", "and i didn't think that Folder.delete(msgs, trashFolderName) ever got called: " + msgs[0].getClass());
throw new MessagingException("ASH : and i didn't think that Folder.delete(msgs, trashFolderName) ever got called: " + msgs[0].getClass());
}
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {}
public abstract void setFlags(Message[] messages, Flag[] flags, boolean value)
throws MessagingException;

View File

@ -166,6 +166,10 @@ public abstract class Store {
return false;
}
public boolean isAppendCapable() {
return false;
}
public void sendMessages(Message[] messages) throws MessagingException {
}

View File

@ -833,6 +833,10 @@ public class ImapStore extends Store {
return true;
}
@Override
public boolean isAppendCapable() {
return true;
}
class ImapFolder extends Folder {
private String mName;

View File

@ -888,6 +888,11 @@ Log.d("ASH", "updatedb " + mAccount.getDescription());
return true;
}
@Override
public boolean isAppendCapable() {
return true;
}
public Message[] searchForMessages(MessageRetrievalListener listener, String[] queryFields,
String queryString, List<LocalFolder> folders, Message[] messages, final Flag[]
requiredFlags, final Flag[] forbiddenFlags) throws MessagingException {
@ -1248,7 +1253,8 @@ Log.d("ASH", "updatedb " + mAccount.getDescription());
}
} else {
Log.w(K9.LOG_TAG, "Creating folder " + getName() + " with existing id " + getId());
create(true); // ASH should this always be true?
create(!LocalStore.this.mAccount.getRemoteStore().isAppendCapable() &&
!LocalStore.this.mAccount.getInboxFolderName().equals(getName()));
open(mode);
}
} catch (MessagingException e) {
@ -2135,6 +2141,17 @@ Log.d("ASH", "setting folder " + mName + " to localOnly = " + localOnly);
appendMessages(messages, false);
}
public void expunge() throws MessagingException {
List<Message> deletedMessages = new ArrayList<Message>();
for (Message message : getMessages(null)) {
if (message.isSet(Flag.DELETED)) {
deletedMessages.add(message);
Log.d("ASH", "about to destroy " + message.getUid());
}
}
destroyMessages(deletedMessages.toArray(EMPTY_MESSAGE_ARRAY));
}
public void destroyMessages(final Message[] messages) throws MessagingException {
try {
database.execute(true, new DbCallback<Void>() {
@ -2709,14 +2726,14 @@ Log.d("ASH", "setting folder " + mName + " to localOnly = " + localOnly);
clearMessagesWhere(where, params);
}
public void clearAllMessages() throws MessagingException {
final String where = "folder_id = ?";
final String[] params = new String[] {
Long.toString(mFolderId)
};
clearAllMessages(true);
}
public void clearAllMessages(boolean includeLocalOnly) throws MessagingException {
final String where = "folder_id = ?" + (includeLocalOnly ? "" :
" AND uid NOT LIKE '" + K9.LOCAL_UID_PREFIX + "%'");
final String[] params = new String[] { Long.toString(mFolderId) };
clearMessagesWhere(where, params);
setPushState(null);

View File

@ -570,6 +570,11 @@ public class WebDavStore extends Store {
return true;
}
@Override
public boolean isAppendCapable() {
return true;
}
private String getSpecialFoldersList() {
StringBuilder buffer = new StringBuilder(200);
buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>");

View File

@ -171,6 +171,9 @@ public class GlobalSettings {
s.put("registeredNameColor", Settings.versions(
new V(1, new ColorSetting(0xFF00008F))
));
s.put("showAdvancedOptions", Settings.versions(
new V(1, new BooleanSetting(false))
));
s.put("showContactName", Settings.versions(
new V(1, new BooleanSetting(false))
));

View File

@ -45,7 +45,6 @@ public class MessageHeader extends LinearLayout {
private View mChip;
private CheckBox mFlagged;
private int defaultSubjectColor;
private LinearLayout mToContainerView;
private LinearLayout mCcContainerView;
private TextView mAdditionalHeadersView;
@ -93,7 +92,6 @@ public class MessageHeader extends LinearLayout {
mTimeView = (TextView) findViewById(R.id.time);
mFlagged = (CheckBox) findViewById(R.id.flagged);
defaultSubjectColor = mSubjectView.getCurrentTextColor();
mSubjectView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewSubject());
mTimeView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewTime());
mDateView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewDate());
@ -212,7 +210,11 @@ public class MessageHeader extends LinearLayout {
} else {
mSubjectView.setText(subject);
}
mSubjectView.setTextColor(0xff000000 | defaultSubjectColor);
if (message.getUid().startsWith(K9.LOCAL_UID_PREFIX)) {
mSubjectView.setTextColor(mAccount.getChipColor());
} else {
mSubjectView.setTextColor(0xff000000);
}
mFromView.setText(from);