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

Provides the ability to move and copy messages between folders. Issue

3.

Each remote Store (IMAP, POP3, WebDAV) can provide indications to the
higher level classes as to whether copy and move are implemented.
Currently, copy and move are only provided by ImapStore.java.  When
the facilities are not available, the user interface suppresses the
menu options.

The available destination folders can be configured using folder
classes.

Copy results in null message bodies in the destination copy of the
message until the next sync.  This is a problem that occurred with
deletes previously, and is likely a fault with the copyMessages method
of LocalStore.java.

Utilizes a modified version of the ChooseFolder.java Activity that
debuachedsloth contributed in a patch attached to Issue 285 on 16 Feb
2008.
This commit is contained in:
Daniel Applebaum 2009-03-05 07:32:45 +00:00
parent db426c44b7
commit 15e5cb4eaf
17 changed files with 867 additions and 39 deletions

View File

@ -59,6 +59,11 @@
android:label="@string/account_setup_names_title"
>
</activity>
<activity
android:name="com.android.email.activity.ChooseFolder"
android:label="@string/choose_folder_title"
>
</activity>
<!-- XXX Note: this activity is hacked to ignore config changes,
since it doesn't currently handle them correctly in code. -->
<activity

View File

@ -28,6 +28,14 @@
android:id="@+id/flag"
android:title="@string/flag_action"
/>
<item
android:id="@+id/move"
android:title="@string/move_action"
/>
<item
android:id="@+id/copy"
android:title="@string/copy_action"
/>
<item
android:id="@+id/send_alternate"
android:title="@string/send_alternate_action"

View File

@ -42,4 +42,12 @@
android:title="@string/send_alternate_action"
android:icon="@drawable/ic_menu_forward_mail"
/>
<item
android:id="@+id/move"
android:title="@string/move_action"
/>
<item
android:id="@+id/copy"
android:title="@string/copy_action"
/>
</menu>

View File

@ -90,6 +90,20 @@
<item>NOT_SECOND_CLASS</item>
</string-array>
<string-array name="account_settings_folder_target_mode_entries">
<item>@string/account_settings_folder_target_mode_all</item>
<item>@string/account_settings_folder_target_mode_first_class</item>
<item>@string/account_settings_folder_target_mode_first_and_second_class</item>
<item>@string/account_settings_folder_target_mode_not_second_class</item>
</string-array>
<string-array name="account_settings_folder_target_mode_values">
<item>ALL</item>
<item>FIRST_CLASS</item>
<item>FIRST_AND_SECOND_CLASS</item>
<item>NOT_SECOND_CLASS</item>
</string-array>
<string-array name="folder_settings_folder_display_mode_entries">
<item>@string/folder_settings_folder_display_mode_normal</item>
<item>@string/folder_settings_folder_display_mode_first_class</item>

View File

@ -14,6 +14,7 @@
<string name="accounts_title">Your accounts</string>
<string name="compose_title">Compose</string>
<string name="debug_title">Debug</string>
<string name="choose_folder_title">Choose Folder</string>
<!-- Actions will be used as buttons and in menu items -->
<string name="next_action">Next</string> <!-- Used as part of a multi-step process -->
@ -50,6 +51,8 @@
<string name="flag_action">Flag</string>
<string name="unflag_action">Unflag</string>
<string name="move_action">Move</string>
<string name="copy_action">Copy</string>
<string name="mark_as_unread_action">Mark as unread</string>
<string name="move_to_action">Move to</string>
@ -294,6 +297,8 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="account_setup_options_mail_display_count_25">25 Messages</string>
<string name="account_setup_options_mail_display_count_50">50 Messages</string>
<string name="account_setup_options_mail_display_count_100">100 Messages</string>
<string name="move_copy_cannot_copy_unsynced_message">Cannot copy or move a message that is not synchronized with the server</string>
<string name="account_setup_failed_dlg_title">Setup could not finish</string>
<string name="account_setup_failed_dlg_auth_message_fmt">Username or password incorrect.\n(<xliff:g id="error">%s</xliff:g>)</string> <!-- Username or password incorrect\n(ERR01 Account does not exist) -->
@ -335,6 +340,12 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="account_settings_folder_sync_mode_first_class">Only 1st Class folders</string>
<string name="account_settings_folder_sync_mode_first_and_second_class">1st and 2nd Class folders</string>
<string name="account_settings_folder_sync_mode_not_second_class">All except 2nd Class folders</string>
<string name="account_settings_folder_target_mode_label">Move/copy destination folders</string>
<string name="account_settings_folder_target_mode_all">All</string>
<string name="account_settings_folder_target_mode_first_class">Only 1st Class folders</string>
<string name="account_settings_folder_target_mode_first_and_second_class">1st and 2nd Class folders</string>
<string name="account_settings_folder_target_mode_not_second_class">All except 2nd Class folders</string>
<string name="folder_settings_title">Folder settings</string>
<string name="folder_settings_folder_display_mode_label">Folder display class</string>
@ -414,9 +425,9 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based
<string name="message_help_key">Del (or D) - Delete\u000AR -
Reply\u000AA - Reply All\u000AF - Forward\u000AJ or P - Previous
Message\u000AK, N - Next Message\u000AZ - Zoom Out\u000AShift-Z -
Message\u000AK, N - Next Message\u000AM - Move\u000AY - Copy\u000AZ - Zoom Out\u000AShift-Z -
Zoom In\u000aG - Flag</string>
<string name="message_list_help_key">Del (or D) - Delete\u000AR -
Reply\u000AA - Reply All\u000AC - Compose\u000AF - Forward\u000aG - Flag\u000AO - Sort type\u000AI - Sort order\u000AQ
Reply\u000AA - Reply All\u000AC - Compose\u000AF - Forward\u000aM - Move\u000AY - Copy\u000AG - Flag\u000AO - Sort type\u000AI - Sort order\u000AQ
- Return to Accounts</string>
</resources>

View File

@ -69,7 +69,15 @@
android:title="@string/account_settings_folder_sync_mode_label"
android:entries="@array/account_settings_folder_sync_mode_entries"
android:entryValues="@array/account_settings_folder_sync_mode_values"
android:dialogTitle="@string/account_settings_folder_sync_mode_label" />
android:dialogTitle="@string/account_settings_folder_sync_mode_label" />
<ListPreference
android:key="folder_target_mode"
android:title="@string/account_settings_folder_target_mode_label"
android:entries="@array/account_settings_folder_target_mode_entries"
android:entryValues="@array/account_settings_folder_target_mode_values"
android:dialogTitle="@string/account_settings_folder_target_mode_label" />
<ListPreference
android:key="delete_policy"

View File

@ -49,6 +49,7 @@ public class Account implements Serializable {
String mOutboxFolderName;
FolderMode mFolderDisplayMode;
FolderMode mFolderSyncMode;
FolderMode mFolderTargetMode;
int mAccountNumber;
boolean mVibrate;
String mRingtoneUri;
@ -85,6 +86,7 @@ public class Account implements Serializable {
mVibrate = false;
mFolderDisplayMode = FolderMode.NOT_SECOND_CLASS;
mFolderSyncMode = FolderMode.FIRST_CLASS;
mFolderTargetMode = FolderMode.NOT_SECOND_CLASS;
mHideMessageViewButtons = HideButtons.NEVER;
mRingtoneUri = "content://settings/system/notification_sound";
}
@ -160,6 +162,16 @@ public class Account implements Serializable {
{
mFolderSyncMode = FolderMode.FIRST_CLASS;
}
try
{
mFolderTargetMode = FolderMode.valueOf(preferences.mSharedPreferences.getString(mUuid + ".folderTargetMode",
FolderMode.NOT_SECOND_CLASS.name()));
}
catch (Exception e)
{
mFolderTargetMode = FolderMode.NOT_SECOND_CLASS;
}
}
@ -276,6 +288,7 @@ public class Account implements Serializable {
editor.remove(mUuid + ".lastFullSync");
editor.remove(mUuid + ".folderDisplayMode");
editor.remove(mUuid + ".folderSyncMode");
editor.remove(mUuid + ".folderTargetMode");
editor.remove(mUuid + ".hideButtonsEnum");
editor.commit();
}
@ -342,6 +355,7 @@ public class Account implements Serializable {
editor.putString(mUuid + ".ringtone", mRingtoneUri);
editor.putString(mUuid + ".folderDisplayMode", mFolderDisplayMode.name());
editor.putString(mUuid + ".folderSyncMode", mFolderSyncMode.name());
editor.putString(mUuid + ".folderTargetMode", mFolderTargetMode.name());
editor.commit();
}
@ -556,4 +570,14 @@ public class Account implements Serializable {
mHideMessageViewButtons = hideMessageViewButtons;
}
public FolderMode getFolderTargetMode()
{
return mFolderTargetMode;
}
public void setFolderTargetMode(FolderMode folderTargetMode)
{
mFolderTargetMode = folderTargetMode;
}
}

View File

@ -86,8 +86,8 @@ public class MessagingController implements Runnable {
// Daniel I. Applebaum Changing to 5k for faster syncing
private static final int MAX_SMALL_MESSAGE_SIZE = (5 * 1024);
private static final String PENDING_COMMAND_TRASH =
"com.android.email.MessagingController.trash";
private static final String PENDING_COMMAND_MOVE_OR_COPY =
"com.android.email.MessagingController.moveOrCopy";
private static final String PENDING_COMMAND_EMPTY_TRASH =
"com.android.email.MessagingController.emptyTrash";
private static final String PENDING_COMMAND_SET_FLAG =
@ -161,7 +161,12 @@ public class MessagingController implements Runnable {
private String createMessageKey(Account account, String folder, Message message)
{
return account.getUuid() + ":" + folder + ":" + message.getUid();
return createMessageKey(account, folder, message.getUid());
}
private String createMessageKey(Account account, String folder, String uid)
{
return account.getUuid() + ":" + folder + ":" + uid;
}
private String createFolderKey(Account account, String folder)
@ -187,7 +192,16 @@ public class MessagingController implements Runnable {
{
return;
}
String messKey = createMessageKey(account, folder, message);
unsuppressMessage(account, folder, message.getUid());
}
private void unsuppressMessage(Account account, String folder, String uid)
{
if (account == null || folder == null || uid == null)
{
return;
}
String messKey = createMessageKey(account, folder, uid);
//Log.d(Email.LOG_TAG, "Unsuppressing message with key " + messKey);
deletedUids.remove(messKey);
}
@ -388,6 +402,9 @@ public class MessagingController implements Runnable {
for (MessagingListener l : getListeners()) {
l.listFoldersStarted(account);
}
if (listener != null) {
listener.listFoldersStarted(account);
}
try {
Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
Folder[] localFolders = localStore.getPersonalNamespaces();
@ -399,21 +416,32 @@ public class MessagingController implements Runnable {
for (MessagingListener l : getListeners()) {
l.listFolders(account, localFolders);
}
if (listener != null)
{
listener.listFolders(account, localFolders);
}
}
catch (Exception e) {
for (MessagingListener l : getListeners()) {
l.listFoldersFailed(account, e.getMessage());
}
if (listener != null) {
listener.listFoldersFailed(account, e.getMessage());
}
addErrorMessage(account, e);
return;
}
for (MessagingListener l : getListeners()) {
l.listFoldersFinished(account);
}
if (listener != null) {
listener.listFoldersFinished(account);
}
}
private void doRefreshRemote (final Account account, MessagingListener listener) {
put("listFolders", listener, new Runnable() {
put("doRefreshRemote", listener, new Runnable() {
public void run() {
try {
Store store = Store.getInstance(account.getStoreUri(), mApplication);
@ -1213,8 +1241,8 @@ s * critical data as fast as possible, and then we'll fill in the de
else if (PENDING_COMMAND_MARK_ALL_AS_READ.equals(command.command)) {
processPendingMarkAllAsRead(command, account);
}
else if (PENDING_COMMAND_TRASH.equals(command.command)) {
processPendingTrash(command, account);
else if (PENDING_COMMAND_MOVE_OR_COPY.equals(command.command)) {
processPendingMoveOrCopy(command, account);
}
else if (PENDING_COMMAND_EMPTY_TRASH.equals(command.command)) {
processPendingEmptyTrash(command, account);
@ -1377,46 +1405,69 @@ s * critical data as fast as possible, and then we'll fill in the de
* @param account
* @throws MessagingException
*/
private void processPendingTrash(PendingCommand command, Account account)
private void processPendingMoveOrCopy(PendingCommand command, Account account)
throws MessagingException {
String folder = command.arguments[0];
String srcFolder = command.arguments[0];
String uid = command.arguments[1];
String destFolder = command.arguments[2];
String isCopyS = command.arguments[3];
boolean isCopy = false;
if (isCopyS != null)
{
isCopy = Boolean.parseBoolean(isCopyS);
}
if (account.getErrorFolderName().equals(folder))
if (account.getErrorFolderName().equals(srcFolder))
{
return;
}
Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
Folder remoteFolder = remoteStore.getFolder(folder);
if (!remoteFolder.exists()) {
Log.w(Email.LOG_TAG, "processingPendingTrash: remoteFolder " + folder + " does not exist");
Folder remoteSrcFolder = remoteStore.getFolder(srcFolder);
Folder remoteDestFolder = remoteStore.getFolder(destFolder);
if (!remoteSrcFolder.exists()) {
Log.w(Email.LOG_TAG, "processingPendingMoveOrCopy: remoteFolder " + srcFolder + " does not exist");
return;
}
remoteFolder.open(OpenMode.READ_WRITE);
if (remoteFolder.getMode() != OpenMode.READ_WRITE) {
Log.w(Email.LOG_TAG, "processingPendingTrash: could not open remoteFolder " + folder + " read/write");
remoteSrcFolder.open(OpenMode.READ_WRITE);
if (remoteSrcFolder.getMode() != OpenMode.READ_WRITE) {
Log.w(Email.LOG_TAG, "processingPendingMoveOrCopy: could not open remoteSrcFolder " + srcFolder + " read/write");
return;
}
remoteDestFolder.open(OpenMode.READ_WRITE);
if (remoteDestFolder.getMode() != OpenMode.READ_WRITE) {
Log.w(Email.LOG_TAG, "processingPendingMoveOrCopy: could not open remoteDestFolder " + srcFolder + " read/write");
return;
}
Message remoteMessage = null;
if (!uid.startsWith("Local")
&& !uid.contains("-")) {
remoteMessage = remoteFolder.getMessage(uid);
// Why bother with this, perhaps just pass the UID to the store to save a roundtrip? And check for error returns, of course
// Same applies for deletion
remoteMessage = remoteSrcFolder.getMessage(uid);
}
if (remoteMessage == null) {
Log.w(Email.LOG_TAG, "processingPendingTrash: remoteMessage " + uid + " does not exist");
Log.w(Email.LOG_TAG, "processingPendingMoveOrCopy: remoteMessage " + uid + " does not exist");
return;
}
if (Config.LOGD)
{
Log.d(Email.LOG_TAG, "processingPendingTrash: remote trash folder = " + account.getTrashFolderName());
Log.d(Email.LOG_TAG, "processingPendingMoveOrCopy: source folder = " + srcFolder
+ ", uid = " + uid + ", destination folder = " + destFolder + ", isCopy = " + isCopy);
}
remoteMessage.delete(account.getTrashFolderName());
remoteFolder.close(true);
if (isCopy) {
remoteSrcFolder.copyMessages(new Message[] { remoteMessage }, remoteDestFolder);
}
else {
remoteSrcFolder.moveMessages(new Message[] { remoteMessage }, remoteDestFolder);
}
remoteSrcFolder.close(true);
remoteDestFolder.close(true);
}
@ -2054,6 +2105,131 @@ s * critical data as fast as possible, and then we'll fill in the de
putBackground("getAccountUnread:" + account.getDescription(), l, unreadRunnable);
}
public boolean moveMessage(final Account account, final String srcFolder, final Message message, final String destFolder,
final MessagingListener listener)
{
if (!message.getUid().startsWith("Local")
&& !message.getUid().contains("-")) {
put("moveMessage", null, new Runnable() {
public void run() {
moveOrCopyMessageSynchronous(account, srcFolder, message, destFolder, false, listener);
}
});
return true;
}
else
{
return false;
}
}
public boolean isMoveCapable(Message message) {
if (!message.getUid().startsWith("Local")
&& !message.getUid().contains("-")) {
return true;
}
else {
return false;
}
}
public boolean isCopyCapable(Message message) {
return isMoveCapable(message);
}
public boolean isMoveCapable(final Account account)
{
try {
Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
return localStore.isMoveCapable() && remoteStore.isMoveCapable();
}
catch (MessagingException me)
{
Log.e(Email.LOG_TAG, "Exception while ascertaining move capability", me);
return false;
}
}
public boolean isCopyCapable(final Account account)
{
try {
Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
return localStore.isCopyCapable() && remoteStore.isCopyCapable();
}
catch (MessagingException me)
{
Log.e(Email.LOG_TAG, "Exception while ascertaining copy capability", me);
return false;
}
}
public boolean copyMessage(final Account account, final String srcFolder, final Message message, final String destFolder,
final MessagingListener listener)
{
if (!message.getUid().startsWith("Local")
&& !message.getUid().contains("-")) {
put("copyMessage", null, new Runnable() {
public void run() {
moveOrCopyMessageSynchronous(account, srcFolder, message, destFolder, true, listener);
}
});
return true;
}
else
{
return false;
}
}
private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder, final Message message,
final String destFolder, final boolean isCopy, MessagingListener listener)
{
try {
Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication);
if (isCopy == false && (remoteStore.isMoveCapable() == false || localStore.isMoveCapable() == false)) {
return;
}
if (isCopy == true && (remoteStore.isCopyCapable() == false || localStore.isCopyCapable() == false)) {
return;
}
Folder localSrcFolder = localStore.getFolder(srcFolder);
Folder localDestFolder = localStore.getFolder(destFolder);
Message lMessage = localSrcFolder.getMessage(message.getUid());
String origUid = message.getUid();
if (lMessage != null)
{
if (Config.LOGD)
{
Log.d(Email.LOG_TAG, "moveOrCopyMessageSynchronous: source folder = " + srcFolder
+ ", uid = " + origUid + ", destination folder = " + destFolder + ", isCopy = " + isCopy);
}
if (isCopy) {
localSrcFolder.copyMessages(new Message[] { message }, localDestFolder);
}
else {
localSrcFolder.moveMessages(new Message[] { message }, localDestFolder);
for (MessagingListener l : getListeners()) {
l.messageUidChanged(account, srcFolder, origUid, message.getUid());
}
unsuppressMessage(account, srcFolder, origUid);
}
}
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_MOVE_OR_COPY;
command.arguments = new String[] { srcFolder, origUid, destFolder, Boolean.toString(isCopy) };
queuePendingCommand(account, command);
processPendingCommands(account);
}
catch (MessagingException me) {
addErrorMessage(account, me);
throw new RuntimeException("Error moving message", me);
}
}
public void deleteMessage(final Account account, final String folder, final Message message,
final MessagingListener listener) {
suppressMessage(account, folder, message);
@ -2064,13 +2240,10 @@ s * critical data as fast as possible, and then we'll fill in the de
}
});
}
private void deleteMessageSynchronous(final Account account, final String folder, final Message message,
MessagingListener listener) {
account.getUuid();
message.getUid();
try {
Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
Folder localFolder = localStore.getFolder(folder);
@ -2139,10 +2312,18 @@ s * critical data as fast as possible, and then we'll fill in the de
queuePendingCommand(account, command);
processPendingCommands(account);
}
else if (folder.equals(account.getTrashFolderName()) && account.getDeletePolicy() == Account.DELETE_POLICY_ON_DELETE)
{
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_SET_FLAG;
command.arguments = new String[] { folder, message.getUid(), Boolean.toString(true), Flag.DELETED.toString() };
queuePendingCommand(account, command);
processPendingCommands(account);
}
else if (account.getDeletePolicy() == Account.DELETE_POLICY_ON_DELETE) {
PendingCommand command = new PendingCommand();
command.command = PENDING_COMMAND_TRASH;
command.arguments = new String[] { folder, message.getUid() };
command.command = PENDING_COMMAND_MOVE_OR_COPY;
command.arguments = new String[] { folder, message.getUid(), account.getTrashFolderName(), "false" };
queuePendingCommand(account, command);
processPendingCommands(account);
}

View File

@ -0,0 +1,231 @@
package com.android.email.activity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.android.email.Account;
import com.android.email.Email;
import com.android.email.MessagingController;
import com.android.email.MessagingListener;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.mail.Folder;
import com.android.email.mail.MessagingException;
public class ChooseFolder extends ListActivity
{
String mFolder;
Account mAccount;
String mUID;
ArrayAdapter<String> adapter;
private ChooseFolderHandler mHandler = new ChooseFolderHandler();
String heldInbox = null;
public static final String EXTRA_ACCOUNT = "com.android.email.ChooseFolder_account";
public static final String EXTRA_CUR_FOLDER = "com.android.email.ChooseFolder_curfolder";
public static final String EXTRA_NEW_FOLDER = "com.android.email.ChooseFolder_newfolder";
public static final String EXTRA_MESSAGE_UID = "com.android.email.ChooseFolder_messageuid";
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
getListView().setTextFilterEnabled(true);
getListView().setItemsCanFocus(false);
getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
Intent intent = getIntent();
mAccount = (Account) intent.getSerializableExtra(EXTRA_ACCOUNT);
mUID = intent.getStringExtra(EXTRA_MESSAGE_UID);
mFolder = intent.getStringExtra(EXTRA_CUR_FOLDER);
if(mFolder == null)
mFolder = "";
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
setListAdapter(adapter);
new Thread()
{
public void run()
{
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
MessagingController.getInstance(getApplication()).listFolders(mAccount,
false, mListener);
}
}.start();
this.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView adapterview, View view, int i, long l)
{
Intent intent = new Intent();
intent.putExtra(EXTRA_CUR_FOLDER, mFolder);
String destFolderName = (String)((TextView)view).getText();
if (heldInbox != null && getString(R.string.special_mailbox_name_inbox).equals(destFolderName))
{
destFolderName = heldInbox;
}
intent.putExtra(EXTRA_NEW_FOLDER, destFolderName);
intent.putExtra(EXTRA_MESSAGE_UID, mUID);
setResult(RESULT_OK, intent);
finish();
}
});
}
class ChooseFolderHandler extends Handler
{
private static final int MSG_PROGRESS = 2;
private static final int MSG_DATA_CHANGED = 3;
public void handleMessage(android.os.Message msg)
{
switch (msg.what)
{
case MSG_PROGRESS:
setProgressBarIndeterminateVisibility(msg.arg1 != 0);
break;
case MSG_DATA_CHANGED:
adapter.notifyDataSetChanged();
}
}
public void progress(boolean progress)
{
android.os.Message msg = new android.os.Message();
msg.what = MSG_PROGRESS;
msg.arg1 = progress ? 1 : 0;
sendMessage(msg);
}
public void dataChanged()
{
sendEmptyMessage(MSG_DATA_CHANGED);
}
}
private MessagingListener mListener = new MessagingListener()
{
public void listFoldersStarted(Account account)
{
if (!account.equals(mAccount))
{
return;
}
mHandler.progress(true);
}
@Override
public void listFoldersFailed(Account account, String message)
{
if (!account.equals(mAccount))
{
return;
}
// mHandler.progress(false);
}
@Override
public void listFoldersFinished(Account account)
{
if (!account.equals(mAccount))
{
return;
}
// mHandler.progress(false);
}
@Override
public void listFolders(Account account, Folder[] folders)
{
if (!account.equals(mAccount))
{
return;
}
Account.FolderMode aMode = account.getFolderTargetMode();
Preferences prefs = Preferences.getPreferences(getApplication().getApplicationContext());
ArrayList<String> localFolders = new ArrayList<String>();
for (Folder folder : folders)
{
String name = folder.getName();
// Inbox needs to be compared case-insensitively
if(name.equals(mFolder) || (Email.INBOX.equalsIgnoreCase(mFolder) && Email.INBOX.equalsIgnoreCase(name))) {
continue;
}
try
{
folder.refresh(prefs);
Folder.FolderClass fMode = folder.getDisplayClass();
if ((aMode == Account.FolderMode.FIRST_CLASS && fMode != Folder.FolderClass.FIRST_CLASS)
|| (aMode == Account.FolderMode.FIRST_AND_SECOND_CLASS &&
fMode != Folder.FolderClass.FIRST_CLASS &&
fMode != Folder.FolderClass.SECOND_CLASS)
|| (aMode == Account.FolderMode.NOT_SECOND_CLASS && fMode == Folder.FolderClass.SECOND_CLASS))
{
continue;
}
}
catch (MessagingException me)
{
Log.e(Email.LOG_TAG, "Couldn't get prefs to check for displayability of folder " + folder.getName(), me);
}
localFolders.add(folder.getName());
}
Collections.sort(localFolders, new Comparator<String>() {
public int compare(String aName, String bName)
{
if (Email.INBOX.equalsIgnoreCase(aName))
{
return -1;
}
if (Email.INBOX.equalsIgnoreCase(bName))
{
return 1;
}
return aName.compareToIgnoreCase(bName);
}
});
adapter.setNotifyOnChange(false);
adapter.clear();
for (String name : localFolders) {
if (Email.INBOX.equalsIgnoreCase(name))
{
adapter.add(getString(R.string.special_mailbox_name_inbox));
heldInbox = name;
}
else {
adapter.add(name);
}
}
mHandler.dataChanged();
}
};
}

View File

@ -86,6 +86,12 @@ public class FolderMessageList extends ExpandableListActivity
{
private static final String INTENT_DATA_PATH_SUFFIX = "/accounts";
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
private static final String EXTRA_ACCOUNT = "account";
private static final String EXTRA_CLEAR_NOTIFICATION = "clearNotification";
@ -723,6 +729,8 @@ public class FolderMessageList extends ExpandableListActivity
case KeyEvent.KEYCODE_A: { onReplyAll(message); return true; }
case KeyEvent.KEYCODE_R: { onReply(message); return true; }
case KeyEvent.KEYCODE_G: { onToggleFlag(message); return true; }
case KeyEvent.KEYCODE_M: { onMove(message); return true; }
case KeyEvent.KEYCODE_Y: { onCopy(message); return true; }
}
}
}
@ -913,6 +921,137 @@ public class FolderMessageList extends ExpandableListActivity
}
private void onMove(MessageInfoHolder holder)
{
if (MessagingController.getInstance(getApplication()).isMoveCapable(mAccount) == false)
{
return;
}
if (MessagingController.getInstance(getApplication()).isMoveCapable(holder.message) == false)
{
Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG);
toast.show();
return;
}
Intent intent = new Intent(this, ChooseFolder.class);
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount);
intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, holder.folder.name);
intent.putExtra(ChooseFolder.EXTRA_MESSAGE_UID, holder.message.getUid());
startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_MOVE);
}
private void onCopy(MessageInfoHolder holder)
{
if (MessagingController.getInstance(getApplication()).isCopyCapable(mAccount) == false)
{
return;
}
if (MessagingController.getInstance(getApplication()).isMoveCapable(holder.message) == false)
{
Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG);
toast.show();
return;
}
Intent intent = new Intent(this, ChooseFolder.class);
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount);
intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, holder.folder.name);
intent.putExtra(ChooseFolder.EXTRA_MESSAGE_UID, holder.message.getUid());
startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_COPY);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode != RESULT_OK)
return;
switch(requestCode) {
case ACTIVITY_CHOOSE_FOLDER_MOVE:
case ACTIVITY_CHOOSE_FOLDER_COPY:
if (data == null)
return;
String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
String srcFolderName = data.getStringExtra(ChooseFolder.EXTRA_CUR_FOLDER);
String uid = data.getStringExtra(ChooseFolder.EXTRA_MESSAGE_UID);
FolderInfoHolder srcHolder = mAdapter.getFolder(srcFolderName);
FolderInfoHolder destHolder = mAdapter.getFolder(destFolderName);
if (srcHolder != null && destHolder != null) {
MessageInfoHolder m = mAdapter.getMessage(srcHolder, uid);
if (m != null) {
switch (requestCode) {
case ACTIVITY_CHOOSE_FOLDER_MOVE:
onMoveChosen(m, destHolder);
break;
case ACTIVITY_CHOOSE_FOLDER_COPY:
onCopyChosen(m, destHolder);
break;
}
}
}
}
}
private void onMoveChosen(MessageInfoHolder holder, FolderInfoHolder folder)
{
if (MessagingController.getInstance(getApplication()).isMoveCapable(mAccount) == false)
{
return;
}
String destFolderName = folder.name;
FolderInfoHolder destHolder = mAdapter.getFolder(destFolderName);
if (destHolder == null) {
return;
}
if (holder.read == false && holder.folder.unreadMessageCount > 0)
{
holder.folder.unreadMessageCount--;
destHolder.unreadMessageCount++;
}
if (destHolder != null)
{
destHolder.needsRefresh = true;
}
mAdapter.removeMessage(holder.message.getFolder().getName(), holder.uid);
MessagingController.getInstance(getApplication()).moveMessage(mAccount,
holder.message.getFolder().getName(), holder.message, destFolderName, null);
}
private void onCopyChosen(MessageInfoHolder holder, FolderInfoHolder folder)
{
if (MessagingController.getInstance(getApplication()).isCopyCapable(mAccount) == false)
{
return;
}
String destFolderName = folder.name;
FolderInfoHolder destHolder = mAdapter.getFolder(destFolderName);
if (destHolder == null) {
return;
}
if (holder.read == false && holder.folder.unreadMessageCount > 0)
{
destHolder.unreadMessageCount++;
}
if (destHolder != null)
{
destHolder.needsRefresh = true;
}
MessagingController.getInstance(getApplication()).copyMessage(mAccount,
holder.message.getFolder().getName(), holder.message, destFolderName, null);
}
private void onReply(MessageInfoHolder holder)
{
MessageCompose.actionReply(this, mAccount, holder.message, false);
@ -1096,6 +1235,12 @@ public class FolderMessageList extends ExpandableListActivity
case R.id.flag:
onToggleFlag(holder);
break;
case R.id.move:
onMove(holder);
break;
case R.id.copy:
onCopy(holder);
break;
case R.id.send_alternate:
onSendAlternate(mAccount, holder);
break;
@ -1177,6 +1322,14 @@ public class FolderMessageList extends ExpandableListActivity
menu.findItem(R.id.flag).setTitle(
R.string.unflag_action);
}
if (MessagingController.getInstance(getApplication()).isCopyCapable(mAccount) == false)
{
menu.findItem(R.id.copy).setVisible(false);
}
if (MessagingController.getInstance(getApplication()).isMoveCapable(mAccount) == false)
{
menu.findItem(R.id.move).setVisible(false);
}
}
} else if (ExpandableListView.getPackedPositionType(info.packedPosition) == ExpandableListView.PACKED_POSITION_TYPE_GROUP)
{

View File

@ -56,6 +56,8 @@ import com.android.email.MessagingController;
import com.android.email.MessagingListener;
import com.android.email.R;
import com.android.email.Utility;
import com.android.email.activity.FolderMessageList.FolderMessageListAdapter.FolderInfoHolder;
import com.android.email.activity.FolderMessageList.FolderMessageListAdapter.MessageInfoHolder;
import com.android.email.mail.Address;
import com.android.email.mail.Flag;
import com.android.email.mail.Message;
@ -78,6 +80,11 @@ public class MessageView extends Activity
private static final String EXTRA_FOLDER_UIDS = "com.android.email.MessageView_folderUids";
private static final String EXTRA_NEXT = "com.android.email.MessageView_next";
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
private TextView mFromView;
private TextView mDateView;
private TextView mToView;
@ -149,6 +156,9 @@ public class MessageView extends Activity
case KeyEvent.KEYCODE_A: { onReplyAll(); return true; }
case KeyEvent.KEYCODE_R: { onReply(); return true; }
case KeyEvent.KEYCODE_G: { onFlag(); return true; }
case KeyEvent.KEYCODE_M: { onMove(); return true; }
case KeyEvent.KEYCODE_Y: { onCopy(); return true; }
case KeyEvent.KEYCODE_J:
case KeyEvent.KEYCODE_P:
{ onPrevious(); return true; }
@ -583,6 +593,68 @@ public class MessageView extends Activity
}
}
}
private void onMove()
{
if (MessagingController.getInstance(getApplication()).isMoveCapable(mMessage) == false)
{
Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG);
toast.show();
return;
}
Intent intent = new Intent(this, ChooseFolder.class);
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount);
intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, mFolder);
intent.putExtra(ChooseFolder.EXTRA_MESSAGE_UID, mMessageUid);
startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_MOVE);
}
private void onCopy()
{
if (MessagingController.getInstance(getApplication()).isMoveCapable(mMessage) == false)
{
Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG);
toast.show();
return;
}
Intent intent = new Intent(this, ChooseFolder.class);
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount);
intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, mFolder);
intent.putExtra(ChooseFolder.EXTRA_MESSAGE_UID, mMessageUid);
startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_COPY);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode != RESULT_OK)
return;
switch(requestCode) {
case ACTIVITY_CHOOSE_FOLDER_MOVE:
case ACTIVITY_CHOOSE_FOLDER_COPY:
if (data == null)
return;
String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
String srcFolderName = data.getStringExtra(ChooseFolder.EXTRA_CUR_FOLDER);
String uid = data.getStringExtra(ChooseFolder.EXTRA_MESSAGE_UID);
if (uid.equals(mMessageUid) && srcFolderName.equals(mFolder))
{
switch (requestCode) {
case ACTIVITY_CHOOSE_FOLDER_MOVE:
MessagingController.getInstance(getApplication()).moveMessage(mAccount,
srcFolderName, mMessage, destFolderName, null);
break;
case ACTIVITY_CHOOSE_FOLDER_COPY:
MessagingController.getInstance(getApplication()).copyMessage(mAccount,
srcFolderName, mMessage, destFolderName, null);
break;
}
}
}
}
private void onSendAlternate() {
@ -760,6 +832,12 @@ public class MessageView extends Activity
case R.id.flag:
onFlag();
break;
case R.id.move:
onMove();
break;
case R.id.copy:
onCopy();
break;
default:
return super.onOptionsItemSelected(item);
}
@ -771,6 +849,14 @@ public class MessageView extends Activity
getMenuInflater().inflate(R.menu.message_view_option, menu);
optionsMenu = menu;
setMenuFlag();
if (MessagingController.getInstance(getApplication()).isCopyCapable(mAccount) == false)
{
menu.findItem(R.id.copy).setVisible(false);
}
if (MessagingController.getInstance(getApplication()).isMoveCapable(mAccount) == false)
{
menu.findItem(R.id.move).setVisible(false);
}
return true;
}

View File

@ -36,6 +36,7 @@ public class AccountSettings extends PreferenceActivity {
private static final String PREFERENCE_OUTGOING = "outgoing";
private static final String PREFERENCE_DISPLAY_MODE = "folder_display_mode";
private static final String PREFERENCE_SYNC_MODE = "folder_sync_mode";
private static final String PREFERENCE_TARGET_MODE = "folder_target_mode";
private static final String PREFERENCE_DELETE_POLICY = "delete_policy";
private Account mAccount;
@ -51,6 +52,7 @@ public class AccountSettings extends PreferenceActivity {
private RingtonePreference mAccountRingtone;
private ListPreference mDisplayMode;
private ListPreference mSyncMode;
private ListPreference mTargetMode;
private ListPreference mDeletePolicy;
public static void actionSettings(Context context, Account account) {
@ -122,6 +124,19 @@ public class AccountSettings extends PreferenceActivity {
}
});
mTargetMode = (ListPreference) findPreference(PREFERENCE_TARGET_MODE);
mTargetMode.setValue(mAccount.getFolderTargetMode().name());
mTargetMode.setSummary(mTargetMode.getEntry());
mTargetMode.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
final String summary = newValue.toString();
int index = mTargetMode.findIndexOfValue(summary);
mTargetMode.setSummary(mTargetMode.getEntries()[index]);
mTargetMode.setValue(summary);
return false;
}
});
mDeletePolicy = (ListPreference) findPreference(PREFERENCE_DELETE_POLICY);
mDeletePolicy.setValue("" + mAccount.getDeletePolicy());
mDeletePolicy.setSummary(mDeletePolicy.getEntry());
@ -225,6 +240,7 @@ public class AccountSettings extends PreferenceActivity {
mAccount.setVibrate(mAccountVibrate.isChecked());
mAccount.setFolderDisplayMode(Account.FolderMode.valueOf(mDisplayMode.getValue()));
mAccount.setFolderSyncMode(Account.FolderMode.valueOf(mSyncMode.getValue()));
mAccount.setFolderTargetMode(Account.FolderMode.valueOf(mTargetMode.getValue()));
mAccount.setDeletePolicy(Integer.parseInt(mDeletePolicy.getValue()));
SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null));

View File

@ -88,7 +88,9 @@ public abstract class Folder {
public abstract void appendMessages(Message[] messages) throws MessagingException;
public abstract void copyMessages(Message[] msgs, Folder folder) throws MessagingException;
public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {} ;
public void moveMessages(Message[] msgs, Folder folder) throws MessagingException {} ;
public abstract void setFlags(Message[] messages, Flag[] flags, boolean value)
throws MessagingException;

View File

@ -77,4 +77,12 @@ public abstract class Store {
public abstract Folder[] getPersonalNamespaces() throws MessagingException;
public abstract void checkSettings() throws MessagingException;
public boolean isCopyCapable() {
return false;
}
public boolean isMoveCapable() {
return false;
}
}

View File

@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
@ -54,6 +55,8 @@ import com.android.email.mail.internet.MimeMultipart;
import com.android.email.mail.internet.MimeUtility;
import com.android.email.mail.store.ImapResponseParser.ImapList;
import com.android.email.mail.store.ImapResponseParser.ImapResponse;
import com.android.email.mail.store.LocalStore.LocalFolder;
import com.android.email.mail.store.LocalStore.LocalMessage;
import com.android.email.mail.transport.CountingOutputStream;
import com.android.email.mail.transport.EOLConvertingOutputStream;
import com.beetstra.jutf7.CharsetProvider;
@ -306,6 +309,17 @@ public class ImapStore extends Store {
throw new RuntimeException("Unable to decode folder name: " + name, uee);
}
}
@Override
public boolean isMoveCapable() {
return true;
}
@Override
public boolean isCopyCapable()
{
return true;
}
class ImapFolder extends Folder {
private String mName;
@ -517,6 +531,12 @@ public class ImapStore extends Store {
throw ioExceptionHandler(mConnection, ioe);
}
}
@Override
public void moveMessages(Message[] messages, Folder folder) throws MessagingException {
copyMessages(messages, folder);
setFlags(messages, new Flag[] { Flag.DELETED }, true);
}
@Override
public int getMessageCount() {
@ -1351,11 +1371,19 @@ public class ImapStore extends Store {
}
public ImapResponse readResponse() throws IOException, MessagingException {
try {
return mParser.readResponse();
}
catch (IOException ioe)
{
close();
throw ioe;
}
}
public String sendCommand(String command, boolean sensitive)
throws MessagingException, IOException {
try {
open();
String tag = Integer.toString(mNextCommandTag++);
String commandToSend = tag + " " + command;
@ -1374,6 +1402,22 @@ public class ImapStore extends Store {
}
}
return tag;
}
catch (IOException ioe)
{
close();
throw ioe;
}
catch (ImapException ie)
{
close();
throw ie;
}
catch (MessagingException me)
{
close();
throw me;
}
}
public List<ImapResponse> executeSimpleCommand(String command) throws IOException,

View File

@ -448,6 +448,15 @@ public class LocalStore extends Store implements Serializable {
return sb.toString();
}
}
public boolean isMoveCapable() {
return true;
}
public boolean isCopyCapable() {
return true;
}
public class LocalFolder extends Folder implements Serializable {
private String mName;
@ -968,6 +977,32 @@ public class LocalStore extends Store implements Serializable {
}
((LocalFolder) folder).appendMessages(msgs, true);
}
@Override
public void moveMessages(Message[] msgs, Folder destFolder) throws MessagingException {
if (!(destFolder instanceof LocalFolder)) {
throw new MessagingException("copyMessages called with incorrect Folder");
}
LocalFolder lDestFolder = (LocalFolder)destFolder;
for (Message message : msgs)
{
LocalMessage lMessage = (LocalMessage)message;
if (!message.isSet(Flag.SEEN)) {
setUnreadMessageCount(getUnreadMessageCount() - 1);
lDestFolder.setUnreadMessageCount(destFolder.getUnreadMessageCount() + 1);
}
message.setUid("Local" + UUID.randomUUID().toString());
mDb.execSQL("UPDATE messages " + "SET folder_id = ?, uid = ? " + "WHERE id = ?", new Object[] {
lDestFolder.getId(),
message.getUid(),
lMessage.getId() });
}
}
/**
* The method differs slightly from the contract; If an incoming message already has a uid

View File

@ -1469,12 +1469,6 @@ public class WebDavStore extends Store {
Log.e(Email.LOG_TAG, "appendMessages() not implmented");
}
@Override
public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {
Log.e(Email.LOG_TAG, "copyMessages() not implemented");
}
@Override
public Message[] expunge() throws MessagingException {
/** Do nothing, deletes occur as soon as the call is made rather than flags on the message */