diff --git a/.classpath b/.classpath
index 323d9666c..7c3f645a0 100644
--- a/.classpath
+++ b/.classpath
@@ -9,5 +9,6 @@
+
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index fa40d48d5..e1fff9465 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,8 +1,8 @@
-
diff --git a/res/layout/choose_account_item.xml b/res/layout/choose_account_item.xml
index d212594f0..910312441 100644
--- a/res/layout/choose_account_item.xml
+++ b/res/layout/choose_account_item.xml
@@ -3,37 +3,25 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
- android:paddingRight="6dip"
- android:paddingBottom="2dip"
- android:descendantFocusability="blocksDescendants"
- android:gravity="center_vertical" >
-
+ android:background="#cccccc"
+ android:gravity="left|center_vertical">
+ android:layout_width="6dp"/>
-
-
-
-
-
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:paddingTop="8dp"
+ android:paddingLeft="18dp"
+ android:paddingRight="4dp"
+ android:paddingBottom="8dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
diff --git a/res/layout/choose_identity_item.xml b/res/layout/choose_identity_item.xml
index 0f1999003..e641a4025 100644
--- a/res/layout/choose_identity_item.xml
+++ b/res/layout/choose_identity_item.xml
@@ -4,45 +4,27 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
- android:paddingRight="6dip"
- android:paddingBottom="2dip"
- android:descendantFocusability="blocksDescendants"
- android:gravity="center_vertical" >
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ android:paddingLeft="12dp"
+ android:paddingRight="4dp">
-
-
-
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
-
-
-
-
-
+
diff --git a/res/layout/message_compose.xml b/res/layout/message_compose.xml
index 2cafb2428..826040c99 100644
--- a/res/layout/message_compose.xml
+++ b/res/layout/message_compose.xml
@@ -2,15 +2,14 @@
+ android:orientation="vertical">
+ android:scrollbarStyle="insideOverlay"
+ android:fillViewport="true">
+ android:background="#45bcbcbc">
-
+ android:layout_marginRight="6dip"/>
+
-
-
+
+
-
+ android:layout_width="wrap_content"
+ android:layout_marginTop="1dip"/>
+
+
+
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
+ android:layout_width="wrap_content"
+ android:src="@drawable/ic_button_contacts"/>
+
+
@@ -154,7 +155,6 @@
android:id="@+id/userId"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@android:color/primary_text_light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
@@ -162,7 +162,6 @@
android:id="@+id/userIdRest"
android:textSize="10sp"
android:ellipsize="end"
- android:textColor="@android:color/primary_text_light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
@@ -173,7 +172,6 @@
+ android:layout_width="match_parent"/>
@@ -303,13 +296,13 @@
android:id="@+id/lower_signature"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_weight="1.0"
android:gravity="left|top"
android:editable="false"
android:minLines="0"
android:autoText="true"
android:capitalize="sentences"
- android:textColor="@android:color/primary_text_light"
+ android:hint="@string/message_compose_signature_hint"
+ android:inputType="textMultiLine|textAutoCorrect|textCapSentences"
android:textAppearance="?android:attr/textAppearanceMedium" />
diff --git a/res/layout/message_view_header.xml b/res/layout/message_view_header.xml
index fe5c486a0..04a6e70a5 100644
--- a/res/layout/message_view_header.xml
+++ b/res/layout/message_view_header.xml
@@ -122,16 +122,6 @@
-
-
@@ -192,6 +182,31 @@
+
+
+
+
+
+
+
+
+
+
@@ -201,19 +216,19 @@
android:id="@+id/show_additional_headers_area"
android:orientation="vertical"
android:layout_width="fill_parent"
- android:layout_height="21.5dp"
+ android:layout_height="21dp"
android:focusable="true"
android:clickable="true"
- android:background="@drawable/separator_area_background">
+ android:background="@drawable/message_view_header_background">
-
+
+ android:layout_height="1dip"
+ android:background="@drawable/divider_horizontal_email" />
diff --git a/res/menu/message_compose_option.xml b/res/menu/message_compose_option.xml
index b2fb00c15..e0f16a838 100644
--- a/res/menu/message_compose_option.xml
+++ b/res/menu/message_compose_option.xml
@@ -30,12 +30,6 @@
android:title="@string/discard_action"
android:icon="@drawable/ic_menu_close_clear_cancel"
/>
-
+ Fertig
EntfernenVerwerfen
- Als Entwurf speichern
+ SpeichernErneut versuchenAktualisierenNachrichten abrufen
@@ -218,7 +218,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
\n * Speichern von Anhängen auf SD-Karte
\n * Papierkorb leeren
\n * Sortieren der Nachrichten
-\n * ...und viele mehr
+\n * …und viele mehr
\n
\nBitte beachten Sie, dass K-9, wie viele andere E-Mail-Anwendungen auch, die meisten kostenlosen Hotmail-Accounts nicht unterstützt. Zudem gibt es einige Probleme mit Microsoft Exchange-Servern.
\n
@@ -256,6 +256,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
BCCBetreffNachrichtentext
+ Signatur-------- Original-Nachricht --------Betreff:Gesendet:
@@ -269,6 +270,11 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Einige Anhänge wurden nicht heruntergeladen. Sie werden automatisch heruntergeladen, bevor diese Nachricht gesendet wird.Einige Anhänge können nicht weitergeleitet werden, da diese nicht heruntergeladen wurden.Original-Nachricht zitieren
+ Empfänger hinzufügen
+ Empfänger hinzufügen (CC)
+ Empfänger hinzufügen (BCC)
+ Zitierten Text entfernen
+ Zitierten Text bearbeitenVon: %s <%s>An:
@@ -285,6 +291,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Anhang konnte nicht auf SD-Karte gespeichert werden.Wählen Sie \"Bilder anzeigen\", um eingebettete Bilder abzurufen.Bilder anzeigen
+ Zeige NachrichtZeige AnhängeMehr…Lade Anhang.
@@ -402,7 +409,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
TLS (falls verfügbar)TLS (immer)
- Bei Löschen von Nachrichten:
+ Beim Löschen von NachrichtenNie von Server löschenNach 7 Tagen löschenAuch auf Server löschen
@@ -426,7 +433,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Bei jedem AbrufenNur manuell
- IMAP Namensraum automatisch ermitteln
+ IMAP-Namensraum automatisch ermittelnIMAP-VerzeichnispräfixOrdner für Entwürfe
@@ -457,7 +464,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Anmeldung erforderlich.BenutzernamePasswort
- Authentifizierungstyp
+ AuthentifizierungsmethodeBenutzername & PasswortBenutzername
@@ -492,7 +499,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Abruf beim Start der Push-VerbindungPush-Mail für dieses Konto aktivierenNeue Nachrichten werden nach dem Eintreffen umgehend abgerufen, falls Ihr Server dies unterstützt. Diese Einstellung kann zur Reduzierung der Laufzeit führen oder diese verbessern.
- IDLE Verbindung refreshen
+ Push-Verbindung erneuernJede MinuteAlle 2 MinutenAlle 3 Minuten
@@ -544,6 +551,8 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Zeigt beim Öffnen einer Benachrichtigung Liste der ungelesenen Nachrichten anAnzahl ungelesener Nachrichten anzeigenZeigt die Anzahl der ungelesenen Nachrichten in der Statuszeile.
+ Nachricht beim Öffnen als gelesen markieren
+ Markiert eine Nachricht als gelesen, sobald sie zum Betrachten geöffnet wird.Spam-LeisteZeige Archivieren-, Verschieben- und Spam-Schaltfläche.
@@ -582,16 +591,15 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
OrdnerListe der NachrichtenAnzeige der Nachricht
- Prefix beim Zitieren
+ Präfix beim ZitierenKryptographieOpenPGP ProviderKeinerNicht verfügbar
- Auto-sign
- Email Adresse des Kontos verwenden um Signaturschlüssel zu schätzen.
-
- Automatische verschlüsselung
- Verschlüsselung aktivieren falls für den Empfänger ein öffentlichen Schlüssel abgespeichert ist.
+ Automatisches Signieren
+ Verwendet die E-Mail-Adresse des Kontos, um den Signaturschlüssel zu finden.
+ Automatische Verschlüsselung
+ Verschlüsselt automatisch, falls für den Empfänger ein öffentlicher Schlüssel vorhanden ist.Häufigkeit der E-Mail-AbfrageHäufigkeit der Abfrage für Nebenordner
@@ -664,7 +672,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Alle außer NebenordnerÜbernehme Löschungen vom Server
- Lösche Nachrichten wenn sie am Server gelöscht werden
+ Lösche Nachrichten, wenn sie vom Server gelöscht wurdenOrdner-Einstellungen
@@ -776,8 +784,9 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Nachrichten mit Anhängen zuerstNachrichten ohne Anhänge zuerst
- Sortieren nach...
+ Sortieren nach…Datum
+ AnkunftsdatumAbsenderBetreffWichtigkeit
@@ -978,6 +987,9 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
DatumNachrichtentext
+ Nachricht verfassen
+ Texteingabefelder
+
WinzigSehr kleinKleiner
@@ -1019,6 +1031,9 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Entwurf speichern?Entwurf speichern oder verwerfen?
+ Nachricht verwerfen?
+ Sind Sie sicher, dass Sie die Nachricht verwerfen möchten?
+
Speichern des Entwurfs verweigern.Die Speicherung von als verschlüsselt markierten Entwürfen verweigern.
@@ -1058,9 +1073,9 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Einstellungen importierenAuswahl importierenGlobale Einstellungen
- Einstellungen Exportieren...
- Einstellungen Importieren...
- Datei lesen...
+ Einstellungen exportieren…
+ Einstellungen importieren…
+ Datei lesen…Exportiere Einstellungen erfolgreich in %s gespeichertGlobale Einstellungen erfolgreich von %s importiert%s erfolgreich von %s importiert
@@ -1069,21 +1084,21 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
%s KontenExportieren der Einstellungen ist fehlgeschlagen
- Importieren der Einstellungen von %s sind fehlgeschlagen
+ Importieren der Einstellungen von %s ist fehlgeschlagenExport erfolgreichExport fehlgeschlagenImport erfolgreichImport fehlgeschlagenKonto aktivieren
- Um das Konto \"%s\" zu benutzen müssen sie das %s angeben.
+ Um das Konto \"%s\" benutzen zu können, müssen Sie %s angeben.
- Server Passwort
- Server Passwörter
+ das Server Passwort
+ die Server PasswörterPosteingangsserver (%s):Postausgangsserver (%s):
- Passwort Setzen...
+ Passwort setzen...Passwörter setzen...Passwort des Posteingangsservers benutzen
@@ -1093,12 +1108,44 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
Konto \"%s\" ist nicht verfügbar; Bitte SD-Karte prüfen.
- Anhang speichern unter...
+ Anhang speichern unter…Anhang speichernEs wurde kein Dateimanager gefunden. Wo soll der Anhang abgelegt werden?Nach oben verschiebenNach unten verschieben
- Konto verschieben...
+ Konto verschieben…
+ K-9 Ungelesen
+ Zeige die Anzahl ungelesener Nachrichten für…
+
+ Kein Dateimanager gefunden!
+ Es wurde keine geeignete Applikation gefunden, um den Import durchzuführen. Bitte installieren Sie einen Dateimanager aus dem Play Store.
+ Play Store öffnen
+ Abbrechen
+
+ Öffnen
+ Link weitergeben
+ Link kopieren
+ Link
+
+ Bild
+ Bild anzeigen
+ Bild speichern
+ Bild herunterladen
+ Bild-URL kopieren
+ Bild-URL
+
+ Anrufen
+ Im Adressbuch speichern
+ Telefonnummer kopieren
+ Telefonnummer
+
+ E-Mail senden
+ Im Adressbuch speichern
+ E-Mail-Adresse kopieren
+ E-Mail-Adresse
+
+ Das Bild wurde als \"%s\" gespeichert.
+ Speichern des Bildes fehlgeschlagen.
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 55815636e..4e43c6997 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -258,6 +258,7 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
Bcc件名本文
+ 署名-------- 元メール --------件名:送信日:
@@ -271,6 +272,11 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
一部の添付ファイルをダウンロードしていません。このメールが送信される前に自動的にダウンロードされます。ダウンロードしていないため、一部の添付ファイルを転送することはできません。元のメッセージを引用する
+ Toに宛先を追加します
+ CCに宛先を追加します
+ BCCに宛先を追加します
+ 引用文を削除します
+ 引用文を編集します送信者: %s <%s>宛先:
@@ -287,8 +293,11 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
SDカードに添付ファイルを保存できません\"画像表示\"ボタンを押下すると描画します画像表示
- 添付取込中
- 添付ファイルのビューワー見つけられません .%s
+ メッセージ表示
+ 添付ファイル表示
+ 他…
+ 添付ファイル取得中
+ %sのビューワーが見つかりませんすべてダウンロード
@@ -544,6 +553,8 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
通知を開いた際に未読メールを検索する未読件数の表示通知バーに未読メッセージの件数を表示する
+ 開くと同時に既読にする
+ メッセージを参照したときに既読にするメール整理ボタンを有効にするアーカイブ、移動、迷惑メールボタンを表示
@@ -777,6 +788,7 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
並べ替え...日付
+ 受信順送信者件名フラグ
@@ -795,7 +807,9 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
このプログラムでPOPアクセスが許可されているのは一部の「Plus」アカウントだけです。有料の「Plus」アカウントがなければ、正しいメールアドレスとパスワードを入力してもログインできません。これらのアカウントにはブラウザからアクセスしてください。
- Yahoo! JapanでPOP3アクセスを使う場合は、Yahoo!メールサイトの「メールの設定」にてPOPアクセスが許可されていることを確認してください。
+ Yahoo! JapanでPOP3アクセスを行う場合は、Yahoo!メールサイトの「メールの設定」にてPOPアクセスが許可されていることを確認してください。
+
+ au oneでIMAPアクセスを行う場合は、au oneポータルサイトの「メール」→「設定」ページにて「IMAPを有効にする」をチェックしてください。証明書が無効です許可
@@ -1112,4 +1126,29 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
設定をインポートするためのアプリケーションがありません。Androidマーケットからファイルマネージャをインストールしてください。マーケット閉じる
+
+ 開く
+ リンクを共有
+ リンクをコピー
+ リンク
+
+ 画像
+ 表示
+ 画像を保存
+ 画像をダウンロード
+ 画像のURLをコピー
+ 画像のURL
+
+ 発信
+ 連絡先に保存
+ 電話番号をコピー
+ 電話番号
+
+ メール送信
+ 連絡先に保存
+ メールアドレスをコピー
+ メールアドレス
+
+ \"%s\"に保存しました
+ 画像の保存に失敗しました
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 17f7ccb30..113532e3a 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -2,5 +2,5 @@
#ffffff#eeeeee
- #1a080808
+ #45bcbcbc
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3f8e6dd80..c0db66391 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -268,6 +268,7 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
BccSubjectMessage text
+ Signature-------- Original Message --------Subject:Sent:
@@ -281,6 +282,11 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
Some attachments were not downloaded. They will be downloaded automatically before this message is sent.Some attachments cannot be forwarded because they have not been downloaded.Quote message
+ Add recipient (To)
+ Add recipient (CC)
+ Add recipient (BCC)
+ Remove quoted text
+ Edit quoted textFrom: %s <%s>To:
@@ -561,6 +567,8 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
Searches for unread messages when Notification is openedShow unread countShow the number of unread messages in the notification bar.
+ Mark message as read when opening
+ Mark a message as read when it is opened for viewingEnable refile buttonsShow the Archive, Move, and Spam buttons.
@@ -797,6 +805,7 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
Sort by...Date
+ ArrivalSenderSubjectStar
@@ -819,6 +828,7 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
\"Plus\" account. Please launch the Web browser to gain access to
these mail accounts.
If you would like to use POP3 for this provider, You should permit to use POP3 on Yahoo mail settings page.
+ If you would like to use IMAP or POP3 for this provider, You should permit to use IMAP or POP3 on au one mail settings page.If you would like to use IMAP or POP3 for this provider, You should permit to use IMAP or POP3 on Naver mail settings page.If you would like to use IMAP or POP3 for this provider, You should permit to use IMAP or POP3 on Hanmail(Daum) mail settings page.If you would like to use IMAP or POP3 for this provider, You should permit to use IMAP or POP3 on Paran mail settings page.
@@ -1150,4 +1160,29 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
the import operation. Please install a file manager application from Android Market
Open MarketClose
+
+ Open for viewing
+ Share link
+ Copy link to clipboard
+ Link
+
+ Image
+ View image
+ Save image
+ Download image
+ Copy image URL to clipboard
+ Image URL
+
+ Call number
+ Save to Contacts
+ Copy phone number to clipboard
+ Phone number
+
+ Send mail
+ Save to Contacts
+ Copy email address to clipboard
+ Email address
+
+ Saved image as \"%s\"
+ Saving the image failed.
diff --git a/res/xml/account_settings_preferences.xml b/res/xml/account_settings_preferences.xml
index afd7e4e83..bf82d203f 100644
--- a/res/xml/account_settings_preferences.xml
+++ b/res/xml/account_settings_preferences.xml
@@ -81,6 +81,17 @@
+
+
+
+
+
+
diff --git a/res/xml/providers.xml b/res/xml/providers.xml
index a00108996..d94d929a7 100644
--- a/res/xml/providers.xml
+++ b/res/xml/providers.xml
@@ -283,6 +283,11 @@
+
+
+
+ accounts = new ArrayList();
if (displaySpecialAccounts() && !K9.isHideSpecialAccounts()) {
- BaseAccount integratedInboxAccount = new SearchAccount(this, true, null, null);
- integratedInboxAccount.setDescription(getString(R.string.integrated_inbox_title));
- integratedInboxAccount.setEmail(getString(R.string.integrated_inbox_detail));
+ BaseAccount unifiedInboxAccount = SearchAccount.createUnifiedInboxAccount(this);
+ BaseAccount allMessagesAccount = SearchAccount.createAllMessagesAccount(this);
- BaseAccount unreadAccount = new SearchAccount(this, false, null, null);
- unreadAccount.setDescription(getString(R.string.search_all_messages_title));
- unreadAccount.setEmail(getString(R.string.search_all_messages_detail));
-
- accounts.add(integratedInboxAccount);
- accounts.add(unreadAccount);
+ accounts.add(unifiedInboxAccount);
+ accounts.add(allMessagesAccount);
}
accounts.addAll(Arrays.asList(realAccounts));
diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java
index 4df049d20..e86fb4364 100644
--- a/src/com/fsck/k9/activity/Accounts.java
+++ b/src/com/fsck/k9/activity/Accounts.java
@@ -23,6 +23,7 @@ import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -47,12 +48,10 @@ import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
-import android.widget.CheckedTextView;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
-import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
@@ -60,7 +59,6 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CompoundButton.OnCheckedChangeListener;
import com.fsck.k9.Account;
@@ -385,16 +383,11 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
}
/**
- * Creates and initializes the special accounts ('Integrated Inbox' and 'All Messages')
+ * Creates and initializes the special accounts ('Unified Inbox' and 'All Messages')
*/
private void createSpecialAccounts() {
- unreadAccount = new SearchAccount(this, false, null, null);
- unreadAccount.setDescription(getString(R.string.search_all_messages_title));
- unreadAccount.setEmail(getString(R.string.search_all_messages_detail));
-
- integratedInboxAccount = new SearchAccount(this, true, null, null);
- integratedInboxAccount.setDescription(getString(R.string.integrated_inbox_title));
- integratedInboxAccount.setEmail(getString(R.string.integrated_inbox_detail));
+ integratedInboxAccount = SearchAccount.createUnifiedInboxAccount(this);
+ unreadAccount = SearchAccount.createAllMessagesAccount(this);
}
@SuppressWarnings("unchecked")
@@ -951,107 +944,128 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
@Override
public Dialog onCreateDialog(int id) {
+ // Android recreates our dialogs on configuration changes even when they have been
+ // dismissed. Make sure we have all information necessary before creating a new dialog.
switch (id) {
- case DIALOG_REMOVE_ACCOUNT:
- return ConfirmationDialog.create(this, id,
- R.string.account_delete_dlg_title,
- getString(R.string.account_delete_dlg_instructions_fmt,
- mSelectedContextAccount.getDescription()),
- R.string.okay_action,
- R.string.cancel_action,
- new Runnable() {
- @Override
- public void run() {
- if (mSelectedContextAccount instanceof Account) {
- Account realAccount = (Account)mSelectedContextAccount;
- try {
- realAccount.getLocalStore().delete();
- } catch (Exception e) {
- // Ignore, this may lead to localStores on sd-cards that are
- // currently not inserted to be left
- }
- MessagingController.getInstance(getApplication())
- .notifyAccountCancel(Accounts.this, realAccount);
- Preferences.getPreferences(Accounts.this).deleteAccount(realAccount);
- K9.setServicesEnabled(Accounts.this);
- refresh();
- }
+ case DIALOG_REMOVE_ACCOUNT: {
+ if (mSelectedContextAccount == null) {
+ return null;
}
- });
- case DIALOG_CLEAR_ACCOUNT:
- return ConfirmationDialog.create(this, id,
- R.string.account_clear_dlg_title,
- getString(R.string.account_clear_dlg_instructions_fmt,
- mSelectedContextAccount.getDescription()),
- R.string.okay_action,
- R.string.cancel_action,
- new Runnable() {
- @Override
- public void run() {
- if (mSelectedContextAccount instanceof Account) {
- Account realAccount = (Account)mSelectedContextAccount;
- mHandler.workingAccount(realAccount, R.string.clearing_account);
- MessagingController.getInstance(getApplication()).clear(realAccount, null);
- }
+ return ConfirmationDialog.create(this, id,
+ R.string.account_delete_dlg_title,
+ getString(R.string.account_delete_dlg_instructions_fmt,
+ mSelectedContextAccount.getDescription()),
+ R.string.okay_action,
+ R.string.cancel_action,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mSelectedContextAccount instanceof Account) {
+ Account realAccount = (Account) mSelectedContextAccount;
+ try {
+ realAccount.getLocalStore().delete();
+ } catch (Exception e) {
+ // Ignore, this may lead to localStores on sd-cards that
+ // are currently not inserted to be left
+ }
+ MessagingController.getInstance(getApplication())
+ .notifyAccountCancel(Accounts.this, realAccount);
+ Preferences.getPreferences(Accounts.this)
+ .deleteAccount(realAccount);
+ K9.setServicesEnabled(Accounts.this);
+ refresh();
+ }
+ }
+ });
+ }
+ case DIALOG_CLEAR_ACCOUNT: {
+ if (mSelectedContextAccount == null) {
+ return null;
}
- });
- case DIALOG_RECREATE_ACCOUNT:
- return ConfirmationDialog.create(this, id,
- R.string.account_recreate_dlg_title,
- getString(R.string.account_recreate_dlg_instructions_fmt,
- mSelectedContextAccount.getDescription()),
- R.string.okay_action,
- R.string.cancel_action,
- new Runnable() {
- @Override
- public void run() {
- if (mSelectedContextAccount instanceof Account) {
- Account realAccount = (Account)mSelectedContextAccount;
- mHandler.workingAccount(realAccount, R.string.recreating_account);
- MessagingController.getInstance(getApplication()).recreate(realAccount, null);
- }
+ return ConfirmationDialog.create(this, id,
+ R.string.account_clear_dlg_title,
+ getString(R.string.account_clear_dlg_instructions_fmt,
+ mSelectedContextAccount.getDescription()),
+ R.string.okay_action,
+ R.string.cancel_action,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mSelectedContextAccount instanceof Account) {
+ Account realAccount = (Account) mSelectedContextAccount;
+ mHandler.workingAccount(realAccount,
+ R.string.clearing_account);
+ MessagingController.getInstance(getApplication())
+ .clear(realAccount, null);
+ }
+ }
+ });
+ }
+ case DIALOG_RECREATE_ACCOUNT: {
+ if (mSelectedContextAccount == null) {
+ return null;
}
- });
- case DIALOG_NO_FILE_MANAGER:
- return ConfirmationDialog.create(this, id,
- R.string.import_dialog_error_title,
- getString(R.string.import_dialog_error_message),
- R.string.open_market,
- R.string.close,
- new Runnable() {
- @Override
- public void run() {
- Uri uri = Uri.parse(ANDROID_MARKET_URL);
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- startActivity(intent);
- }
- });
+
+ return ConfirmationDialog.create(this, id,
+ R.string.account_recreate_dlg_title,
+ getString(R.string.account_recreate_dlg_instructions_fmt,
+ mSelectedContextAccount.getDescription()),
+ R.string.okay_action,
+ R.string.cancel_action,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mSelectedContextAccount instanceof Account) {
+ Account realAccount = (Account) mSelectedContextAccount;
+ mHandler.workingAccount(realAccount,
+ R.string.recreating_account);
+ MessagingController.getInstance(getApplication())
+ .recreate(realAccount, null);
+ }
+ }
+ });
+ }
+ case DIALOG_NO_FILE_MANAGER: {
+ return ConfirmationDialog.create(this, id,
+ R.string.import_dialog_error_title,
+ getString(R.string.import_dialog_error_message),
+ R.string.open_market,
+ R.string.close,
+ new Runnable() {
+ @Override
+ public void run() {
+ Uri uri = Uri.parse(ANDROID_MARKET_URL);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ startActivity(intent);
+ }
+ });
+ }
}
+
return super.onCreateDialog(id);
}
@Override
public void onPrepareDialog(int id, Dialog d) {
-
AlertDialog alert = (AlertDialog) d;
switch (id) {
- case DIALOG_REMOVE_ACCOUNT:
- alert.setMessage(getString(R.string.account_delete_dlg_instructions_fmt,
- mSelectedContextAccount.getDescription()));
- break;
- case DIALOG_CLEAR_ACCOUNT:
- alert.setMessage(getString(R.string.account_clear_dlg_instructions_fmt,
- mSelectedContextAccount.getDescription()));
- break;
- case DIALOG_RECREATE_ACCOUNT:
- alert.setMessage(getString(R.string.account_recreate_dlg_instructions_fmt,
- mSelectedContextAccount.getDescription()));
- break;
- case DIALOG_NO_FILE_MANAGER:
- alert.setMessage(getString(R.string.import_dialog_error_message));
- break;
+ case DIALOG_REMOVE_ACCOUNT: {
+ alert.setMessage(getString(R.string.account_delete_dlg_instructions_fmt,
+ mSelectedContextAccount.getDescription()));
+ break;
+ }
+ case DIALOG_CLEAR_ACCOUNT: {
+ alert.setMessage(getString(R.string.account_clear_dlg_instructions_fmt,
+ mSelectedContextAccount.getDescription()));
+ break;
+ }
+ case DIALOG_RECREATE_ACCOUNT: {
+ alert.setMessage(getString(R.string.account_recreate_dlg_instructions_fmt,
+ mSelectedContextAccount.getDescription()));
+ break;
+ }
}
super.onPrepareDialog(id, d);
@@ -1495,8 +1509,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
private static class ImportSelectionDialog implements NonConfigurationInstance {
private ImportContents mImportContents;
private Uri mUri;
- private Dialog mDialog;
- private ListView mImportSelectionView;
+ private AlertDialog mDialog;
private SparseBooleanArray mSelection;
@@ -1514,8 +1527,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
public boolean retain() {
if (mDialog != null) {
// Save the selection state of each list item
- mSelection = mImportSelectionView.getCheckedItemPositions();
- mImportSelectionView = null;
+ mSelection = mDialog.getListView().getCheckedItemPositions();
mDialog.dismiss();
mDialog = null;
@@ -1529,8 +1541,6 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
}
public void show(final Accounts activity, SparseBooleanArray selection) {
- final ListView importSelectionView = new ListView(activity);
- mImportSelectionView = importSelectionView;
List contents = new ArrayList();
if (mImportContents.globalSettings) {
@@ -1541,23 +1551,15 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
contents.add(account.name);
}
- importSelectionView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- importSelectionView.setAdapter(new ArrayAdapter(activity,
- android.R.layout.simple_list_item_checked, contents));
- importSelectionView.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView> parent, View view, int pos, long id) {
- CheckedTextView ctv = (CheckedTextView)view;
- ctv.setChecked(!ctv.isChecked());
- }
-
- @Override
- public void onNothingSelected(AdapterView> arg0) { /* Do nothing */ }
- });
-
+ int count = contents.size();
+ boolean[] checkedItems = new boolean[count];
if (selection != null) {
- for (int i = 0, end = contents.size(); i < end; i++) {
- importSelectionView.setItemChecked(i, selection.get(i));
+ for (int i = 0; i < count; i++) {
+ checkedItems[i] = selection.get(i);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ checkedItems[i] = true;
}
}
@@ -1565,23 +1567,29 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
//TODO: listview footer: "Select all" / "Select none" buttons?
//TODO: listview footer: "Overwrite existing accounts?" checkbox
+ OnMultiChoiceClickListener listener = new OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ ((AlertDialog) dialog).getListView().setItemChecked(which, isChecked);
+ }
+ };
+
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setMultiChoiceItems(contents.toArray(new String[0]), checkedItems, listener);
builder.setTitle(activity.getString(R.string.settings_import_selection));
- builder.setView(importSelectionView);
builder.setInverseBackgroundForced(true);
builder.setPositiveButton(R.string.okay_action,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- ListAdapter adapter = importSelectionView.getAdapter();
- int count = adapter.getCount();
- SparseBooleanArray pos = importSelectionView.getCheckedItemPositions();
+ ListView listView = ((AlertDialog) dialog).getListView();
+ SparseBooleanArray pos = listView.getCheckedItemPositions();
boolean includeGlobals = mImportContents.globalSettings ? pos.get(0) : false;
List accountUuids = new ArrayList();
int start = mImportContents.globalSettings ? 1 : 0;
- for (int i = start; i < count; i++) {
+ for (int i = start, end = listView.getCount(); i < end; i++) {
if (pos.get(i)) {
accountUuids.add(mImportContents.accounts.get(i-start).uuid);
}
diff --git a/src/com/fsck/k9/activity/ChooseAccount.java b/src/com/fsck/k9/activity/ChooseAccount.java
deleted file mode 100644
index df2d47932..000000000
--- a/src/com/fsck/k9/activity/ChooseAccount.java
+++ /dev/null
@@ -1,243 +0,0 @@
-package com.fsck.k9.activity;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.ExpandableListAdapter;
-import android.widget.ExpandableListView;
-import android.widget.TextView;
-
-import com.fsck.k9.Account;
-import com.fsck.k9.Identity;
-import com.fsck.k9.K9;
-import com.fsck.k9.Preferences;
-import com.fsck.k9.R;
-
-import java.util.List;
-
-/**
- * Activity displaying list of accounts/identity for user choice
- *
- * @see K9ExpandableListActivity
- */
-public class ChooseAccount extends K9ExpandableListActivity {
- private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[0];
-
- /**
- * {@link Intent} extended data name for storing {@link Account#getUuid()
- * account UUID}
- */
- public static final String EXTRA_ACCOUNT = ChooseAccount.class.getName() + "_account";
-
- /**
- * {@link Intent} extended data name for storing serialized {@link Identity}
- */
- public static final String EXTRA_IDENTITY = ChooseAccount.class.getName() + "_identity";
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.choose_account);
-
- final ExpandableListView expandableListView = getExpandableListView();
- expandableListView.setItemsCanFocus(false);
-
- final IdentitiesAdapter adapter = createAdapter();
- setListAdapter(adapter);
-
- expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
- @Override
- public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
- int childPosition, long id) {
- final Identity identity = (Identity) adapter.getChild(groupPosition, childPosition);
- final Account account = (Account) adapter.getGroup(groupPosition);
-
- if (!account.isAvailable(v.getContext())) {
- Log.i(K9.LOG_TAG, "Refusing selection of unavailable account");
- return true;
- }
- final Intent intent = new Intent();
- intent.putExtra(EXTRA_ACCOUNT, account.getUuid());
- intent.putExtra(EXTRA_IDENTITY, identity);
- setResult(RESULT_OK, intent);
-
- finish();
- return true;
- }
- });
-
- final Bundle extras = getIntent().getExtras();
- final String uuid = extras.getString(EXTRA_ACCOUNT);
- if (uuid != null) {
- final Account[] accounts = adapter.getAccounts();
- final int length = accounts.length;
- for (int i = 0; i < length; i++) {
- final Account account = accounts[i];
- if (uuid.equals(account.getUuid())) {
- // setSelectedChild() doesn't seem to obey the
- // shouldExpandGroup parameter (2.1), manually expanding
- // group
- expandableListView.expandGroup(i);
-
- final List identities = account.getIdentities();
- final Identity identity = (Identity) extras.getSerializable(EXTRA_IDENTITY);
- if (identity == null) {
- expandableListView.setSelectedChild(i, 0, true);
- break;
- }
- for (int j = 0; j < identities.size(); j++) {
- final Identity loopIdentity = identities.get(j);
- if (identity.equals(loopIdentity)) {
- expandableListView.setSelectedChild(i, j, true);
- break;
- }
- }
- break;
- }
- }
- }
- }
-
- private IdentitiesAdapter createAdapter() {
- return new IdentitiesAdapter(this, getLayoutInflater());
- }
-
- /**
- * Dynamically provides accounts/identities data for
- * {@link ExpandableListView#setAdapter(ExpandableListAdapter)}:
- *
- *
- *
Groups represent {@link Account accounts}
- *
Children represent {@link Identity identities} of the parent account
- *
- */
- public static class IdentitiesAdapter extends BaseExpandableListAdapter {
-
- private Context mContext;
- private LayoutInflater mLayoutInflater;
- private Account[] mAccounts;
-
- public IdentitiesAdapter(final Context context, final LayoutInflater layoutInflater) {
- mContext = context;
- mLayoutInflater = layoutInflater;
- Preferences prefs = Preferences.getPreferences(mContext);
- mAccounts = prefs.getAvailableAccounts().toArray(EMPTY_ACCOUNT_ARRAY);
- }
-
- @Override
- public Object getChild(int groupPosition, int childPosition) {
- return getAccounts()[groupPosition].getIdentity(childPosition);
- }
-
- @Override
- public long getChildId(int groupPosition, int childPosition) {
- return Integer.valueOf(childPosition).longValue();
- }
-
- @Override
- public int getChildrenCount(int groupPosition) {
- return getAccounts()[groupPosition].getIdentities().size();
- }
-
- @Override
- public Object getGroup(int groupPosition) {
- return getAccounts()[groupPosition];
- }
-
- @Override
- public int getGroupCount() {
- return getAccounts().length;
- }
-
- @Override
- public long getGroupId(int groupPosition) {
- return Integer.valueOf(groupPosition).longValue();
- }
-
- @Override
- public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
- ViewGroup parent) {
- final View v;
- if (convertView == null) {
- v = mLayoutInflater.inflate(R.layout.choose_account_item, parent, false);
- } else {
- v = convertView;
- }
-
- final TextView description = (TextView) v.findViewById(R.id.description);
- final Account account = getAccounts()[groupPosition];
- description.setText(account.getDescription());
- description.setTextSize(TypedValue.COMPLEX_UNIT_SP, K9.getFontSizes().getAccountName());
-
- // display unavailable accounts translucent
- /*
- * 20101030/fiouzy: NullPointerException on null getBackground()
- *
- if (account.isAvailable(parent.getContext()))
- {
- description.getBackground().setAlpha(255);
- description.getBackground().setAlpha(255);
- }
- else
- {
- description.getBackground().setAlpha(127);
- description.getBackground().setAlpha(127);
- }
- */
-
- v.findViewById(R.id.chip).setBackgroundColor(account.getChipColor());
-
- return v;
- }
-
- @Override
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- final Account account = getAccounts()[groupPosition];
- final Identity identity = account.getIdentity(childPosition);
-
- final View v;
- if (convertView == null) {
- v = mLayoutInflater.inflate(R.layout.choose_identity_item, parent, false);
- } else {
- v = convertView;
- }
-
- final TextView name = (TextView) v.findViewById(R.id.name);
- final TextView description = (TextView) v.findViewById(R.id.description);
- name.setTextSize(TypedValue.COMPLEX_UNIT_SP, K9.getFontSizes().getAccountName());
- description.setTextSize(TypedValue.COMPLEX_UNIT_SP, K9.getFontSizes().getAccountDescription());
-
- name.setText(identity.getDescription());
- description.setText(String.format("%s <%s>", identity.getName(), identity.getEmail()));
-
- v.findViewById(R.id.chip).setBackgroundColor(account.getChipColor());
-
- return v;
- }
-
- @Override
- public boolean hasStableIds() {
- // returning false since accounts/identities are mutable
- return false;
- }
-
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
-
- private Account[] getAccounts() {
- return mAccounts;
- }
- }
-}
diff --git a/src/com/fsck/k9/activity/ChooseFolder.java b/src/com/fsck/k9/activity/ChooseFolder.java
index 060104e45..1546bb6c9 100644
--- a/src/com/fsck/k9/activity/ChooseFolder.java
+++ b/src/com/fsck/k9/activity/ChooseFolder.java
@@ -1,6 +1,13 @@
package com.fsck.k9.activity;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
@@ -21,8 +28,13 @@ import android.widget.Filter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
-import com.fsck.k9.*;
+
+//import com.fsck.k9.*;
+import com.fsck.k9.Account;
import com.fsck.k9.Account.FolderMode;
+import com.fsck.k9.K9;
+import com.fsck.k9.Preferences;
+import com.fsck.k9.R;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.mail.Folder;
@@ -31,38 +43,8 @@ import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.store.ImapStore;
import com.fsck.k9.mail.store.Pop3Store;
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;
- String mSelectFolder;
- Account mAccount;
- MessageReference mMessageReference;
- ArrayAdapter mAdapter;
- private ChooseFolderHandler mHandler = new ChooseFolderHandler();
- String heldInbox = null;
- boolean hideCurrentFolder = true;
- boolean showOptionNone = false;
- boolean showDisplayableOnly = false;
-
- /**
- * What folders to display.
- * Initialized to whatever is configured
- * but can be overridden via {@link #onOptionsItemSelected(MenuItem)}
- * while this activity is showing.
- */
- private Account.FolderMode mMode;
- /**
- * Current filter used by our ArrayAdapter.
- * Created on the fly and invalidated if a new
- * set of folders is chosen via {@link #onOptionsItemSelected(MenuItem)}
- */
- private FolderListFilter myFilter = null;
-
public static final String EXTRA_ACCOUNT = "com.fsck.k9.ChooseFolder_account";
public static final String EXTRA_CUR_FOLDER = "com.fsck.k9.ChooseFolder_curfolder";
public static final String EXTRA_SEL_FOLDER = "com.fsck.k9.ChooseFolder_selfolder";
@@ -72,6 +54,34 @@ public class ChooseFolder extends K9ListActivity {
public static final String EXTRA_SHOW_FOLDER_NONE = "com.fsck.k9.ChooseFolder_showOptionNone";
public static final String EXTRA_SHOW_DISPLAYABLE_ONLY = "com.fsck.k9.ChooseFolder_showDisplayableOnly";
+
+ String mFolder;
+ String mSelectFolder;
+ Account mAccount;
+ MessageReference mMessageReference;
+ ArrayAdapter mAdapter;
+ private ChooseFolderHandler mHandler = new ChooseFolderHandler();
+ String mHeldInbox = null;
+ boolean mHideCurrentFolder = true;
+ boolean mShowOptionNone = false;
+ boolean mShowDisplayableOnly = false;
+
+ /**
+ * What folders to display.
+ * Initialized to whatever is configured
+ * but can be overridden via {@link #onOptionsItemSelected(MenuItem)}
+ * while this activity is showing.
+ */
+ private Account.FolderMode mMode;
+
+ /**
+ * Current filter used by our ArrayAdapter.
+ * Created on the fly and invalidated if a new
+ * set of folders is chosen via {@link #onOptionsItemSelected(MenuItem)}
+ */
+ private FolderListFilter mMyFilter = null;
+
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -87,13 +97,13 @@ public class ChooseFolder extends K9ListActivity {
mFolder = intent.getStringExtra(EXTRA_CUR_FOLDER);
mSelectFolder = intent.getStringExtra(EXTRA_SEL_FOLDER);
if (intent.getStringExtra(EXTRA_SHOW_CURRENT) != null) {
- hideCurrentFolder = false;
+ mHideCurrentFolder = false;
}
if (intent.getStringExtra(EXTRA_SHOW_FOLDER_NONE) != null) {
- showOptionNone = true;
+ mShowOptionNone = true;
}
if (intent.getStringExtra(EXTRA_SHOW_DISPLAYABLE_ONLY) != null) {
- showDisplayableOnly = true;
+ mShowDisplayableOnly = true;
}
if (mFolder == null)
mFolder = "";
@@ -112,55 +122,42 @@ public class ChooseFolder extends K9ListActivity {
setListAdapter(mAdapter);
-
mMode = mAccount.getFolderTargetMode();
onRefresh(false);
-
this.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ACCOUNT, mAccount.getUuid());
- intent.putExtra(EXTRA_CUR_FOLDER, mFolder);
+ Intent result = new Intent();
+ result.putExtra(EXTRA_ACCOUNT, mAccount.getUuid());
+ result.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;
+ if (mHeldInbox != null && getString(R.string.special_mailbox_name_inbox).equals(destFolderName)) {
+ destFolderName = mHeldInbox;
}
- intent.putExtra(EXTRA_NEW_FOLDER, destFolderName);
- intent.putExtra(EXTRA_MESSAGE, mMessageReference);
- setResult(RESULT_OK, intent);
+ result.putExtra(EXTRA_NEW_FOLDER, destFolderName);
+ result.putExtra(EXTRA_MESSAGE, mMessageReference);
+ setResult(RESULT_OK, result);
finish();
}
});
-
}
class ChooseFolderHandler extends Handler {
-
- private static final int MSG_PROGRESS = 2;
-
- private static final int MSG_DATA_CHANGED = 3;
- private static final int MSG_SET_SELECTED_FOLDER = 4;
+ private static final int MSG_PROGRESS = 1;
+ private static final int MSG_SET_SELECTED_FOLDER = 2;
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
- case MSG_PROGRESS:
- setProgressBarIndeterminateVisibility(msg.arg1 != 0);
- break;
- case MSG_DATA_CHANGED:
- mAdapter.notifyDataSetChanged();
-
- /*
- * Only enable the text filter after the list has been
- * populated to avoid possible race conditions because our
- * FolderListFilter isn't really thread-safe.
- */
- getListView().setTextFilterEnabled(true);
- break;
- case MSG_SET_SELECTED_FOLDER:
- getListView().setSelection(msg.arg1);
- break;
+ case MSG_PROGRESS: {
+ setProgressBarIndeterminateVisibility(msg.arg1 != 0);
+ break;
+ }
+ case MSG_SET_SELECTED_FOLDER: {
+ getListView().setSelection(msg.arg1);
+ break;
+ }
}
}
@@ -177,13 +174,10 @@ public class ChooseFolder extends K9ListActivity {
msg.arg1 = position;
sendMessage(msg);
}
-
- public void dataChanged() {
- sendEmptyMessage(MSG_DATA_CHANGED);
- }
}
- @Override public boolean onCreateOptionsMenu(Menu menu) {
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.folder_select_option, menu);
if (mAccount.getStoreUri().startsWith("webdav")) {
@@ -192,55 +186,49 @@ public class ChooseFolder extends K9ListActivity {
return true;
}
- @Override public boolean onOptionsItemSelected(MenuItem item) {
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
-
-
- case R.id.display_1st_class: {
- setDisplayMode(FolderMode.FIRST_CLASS);
- return true;
- }
- case R.id.display_1st_and_2nd_class: {
- setDisplayMode(FolderMode.FIRST_AND_SECOND_CLASS);
- return true;
- }
- case R.id.display_not_second_class: {
- setDisplayMode(FolderMode.NOT_SECOND_CLASS);
- return true;
- }
- case R.id.display_all: {
- setDisplayMode(FolderMode.ALL);
- return true;
- }
-
- case R.id.list_folders: {
- onRefresh();
- return true;
- }
- case R.id.filter_folders: {
- onEnterFilter();
- return true;
- }
- case R.id.create_folder: {
- onCreateFolder();
- return true;
- }
- default:
- return super.onOptionsItemSelected(item);
+ case R.id.display_1st_class: {
+ setDisplayMode(FolderMode.FIRST_CLASS);
+ return true;
+ }
+ case R.id.display_1st_and_2nd_class: {
+ setDisplayMode(FolderMode.FIRST_AND_SECOND_CLASS);
+ return true;
+ }
+ case R.id.display_not_second_class: {
+ setDisplayMode(FolderMode.NOT_SECOND_CLASS);
+ return true;
+ }
+ case R.id.display_all: {
+ setDisplayMode(FolderMode.ALL);
+ return true;
+ }
+ case R.id.list_folders: {
+ onRefresh();
+ return true;
+ }
+ case R.id.filter_folders: {
+ onEnterFilter();
+ return true;
+ }
+ case R.id.create_folder: {
+ onCreateFolder();
+ return true;
+ }
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
}
}
-
private void onRefresh() {
-
onRefresh(true);
-
}
private void onRefresh(final boolean forceRemote) {
-
MessagingController.getInstance(getApplication()).listFolders(mAccount, forceRemote, mListener);
-
}
/**
@@ -248,44 +236,43 @@ public class ChooseFolder extends K9ListActivity {
* Filter {@link #mAdapter} with the user-input.
*/
private void onEnterFilter() {
- final AlertDialog.Builder filterAlert = new AlertDialog.Builder(this);
+ final AlertDialog.Builder filterAlert = new AlertDialog.Builder(this);
- final EditText input = new EditText(this);
- input.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- mAdapter.getFilter().filter(input.getText().toString());
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
- });
- input.setHint(R.string.folder_list_filter_hint);
- filterAlert.setView(input);
+ final EditText input = new EditText(this);
+ input.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mAdapter.getFilter().filter(input.getText().toString());
+ }
- filterAlert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- String value = input.getText().toString().trim();
- mAdapter.getFilter().filter(value);
- }
- });
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ /* not used */ }
- filterAlert.setNegativeButton("Cancel",
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- mAdapter.getFilter().filter("");
- }
- });
+ @Override
+ public void afterTextChanged(Editable s) { /* not used */ }
+ });
+ input.setHint(R.string.folder_list_filter_hint);
+ filterAlert.setView(input);
- filterAlert.show();
+ String okay = getString(R.string.okay_action);
+ filterAlert.setPositiveButton(okay, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ String value = input.getText().toString().trim();
+ mAdapter.getFilter().filter(value);
+ }
+ });
+ String cancel = getString(R.string.cancel_action);
+ filterAlert.setNegativeButton(cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ mAdapter.getFilter().filter("");
+ }
+ });
+
+ filterAlert.show();
}
/*
@@ -350,8 +337,8 @@ public class ChooseFolder extends K9ListActivity {
private void setDisplayMode(FolderMode aMode) {
mMode = aMode;
// invalidate the current filter as it is working on an inval
- if (myFilter != null) {
- myFilter.invalidate();
+ if (mMyFilter != null) {
+ mMyFilter.invalidate();
}
//re-populate the list
onRefresh(false);
@@ -394,34 +381,39 @@ public class ChooseFolder extends K9ListActivity {
String name = folder.getName();
// Inbox needs to be compared case-insensitively
- if (hideCurrentFolder && (name.equals(mFolder) ||
- (mAccount.getInboxFolderName().equalsIgnoreCase(mFolder) && mAccount.getInboxFolderName().equalsIgnoreCase(name)))) {
+ if (mHideCurrentFolder && (name.equals(mFolder) || (
+ mAccount.getInboxFolderName().equalsIgnoreCase(mFolder) &&
+ mAccount.getInboxFolderName().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 &&
+ 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)) {
+ fMode != Folder.FolderClass.SECOND_CLASS) || (
+ aMode == Account.FolderMode.NOT_SECOND_CLASS &&
+ fMode == Folder.FolderClass.SECOND_CLASS)) {
continue;
}
} catch (MessagingException me) {
- Log.e(K9.LOG_TAG, "Couldn't get prefs to check for displayability of folder " + folder.getName(), me);
+ Log.e(K9.LOG_TAG, "Couldn't get prefs to check for displayability of folder " +
+ folder.getName(), me);
}
localFolders.add(folder.getName());
}
- if (showOptionNone) {
+ if (mShowOptionNone) {
localFolders.add(K9.FOLDER_NONE);
}
Collections.sort(localFolders, new Comparator() {
+ @Override
public int compare(String aName, String bName) {
if (K9.FOLDER_NONE.equalsIgnoreCase(aName)) {
return -1;
@@ -439,17 +431,22 @@ public class ChooseFolder extends K9ListActivity {
return aName.compareToIgnoreCase(bName);
}
});
- mAdapter.setNotifyOnChange(false);
int selectedFolder = -1;
+
+ /*
+ * We're not allowed to change the adapter from a background thread, so we collect the
+ * folder names and update the adapter in the UI thread (see finally block).
+ */
+ final List folderList = new ArrayList();
try {
- mAdapter.clear();
int position = 0;
for (String name : localFolders) {
if (mAccount.getInboxFolderName().equalsIgnoreCase(name)) {
- mAdapter.add(getString(R.string.special_mailbox_name_inbox));
- heldInbox = name;
- } else if (!K9.ERROR_FOLDER_NAME.equals(name) && !account.getOutboxFolderName().equals(name)) {
- mAdapter.add(name);
+ folderList.add(getString(R.string.special_mailbox_name_inbox));
+ mHeldInbox = name;
+ } else if (!K9.ERROR_FOLDER_NAME.equals(name) &&
+ !account.getOutboxFolderName().equals(name)) {
+ folderList.add(name);
}
if (mSelectFolder != null) {
@@ -461,24 +458,35 @@ public class ChooseFolder extends K9ListActivity {
if (name.equals(mSelectFolder)) {
selectedFolder = position;
}
- } else if (name.equals(mFolder) ||
- (mAccount.getInboxFolderName().equalsIgnoreCase(mFolder) && mAccount.getInboxFolderName().equalsIgnoreCase(name))) {
+ } else if (name.equals(mFolder) || (
+ mAccount.getInboxFolderName().equalsIgnoreCase(mFolder) &&
+ mAccount.getInboxFolderName().equalsIgnoreCase(name))) {
selectedFolder = position;
}
position++;
}
} finally {
- mAdapter.setNotifyOnChange(true);
runOnUiThread(new Runnable() {
+ @Override
public void run() {
- // runOnUiThread(
+ // Now we're in the UI-thread, we can safely change the contents of the adapter.
+ mAdapter.clear();
+ for (String folderName: folderList) {
+ mAdapter.add(folderName);
+ }
+
mAdapter.notifyDataSetChanged();
+
+ /*
+ * Only enable the text filter after the list has been
+ * populated to avoid possible race conditions because our
+ * FolderListFilter isn't really thread-safe.
+ */
+ getListView().setTextFilterEnabled(true);
}
});
}
- mHandler.dataChanged();
-
if (selectedFolder != -1) {
mHandler.setSelectedFolder(selectedFolder);
}
diff --git a/src/com/fsck/k9/activity/InsertableHtmlContent.java b/src/com/fsck/k9/activity/InsertableHtmlContent.java
index ab413e19d..851f0ca9a 100644
--- a/src/com/fsck/k9/activity/InsertableHtmlContent.java
+++ b/src/com/fsck/k9/activity/InsertableHtmlContent.java
@@ -32,11 +32,20 @@ class InsertableHtmlContent implements Serializable {
}
public void setHeaderInsertionPoint(int headerInsertionPoint) {
- this.headerInsertionPoint = headerInsertionPoint;
+ if (headerInsertionPoint < 0 || headerInsertionPoint > quotedContent.length()) {
+ this.headerInsertionPoint = 0;
+ } else {
+ this.headerInsertionPoint = headerInsertionPoint;
+ }
}
public void setFooterInsertionPoint(int footerInsertionPoint) {
- this.footerInsertionPoint = footerInsertionPoint;
+ int len = quotedContent.length();
+ if (footerInsertionPoint < 0 || footerInsertionPoint > len) {
+ this.footerInsertionPoint = len;
+ } else {
+ this.footerInsertionPoint = footerInsertionPoint;
+ }
}
/**
diff --git a/src/com/fsck/k9/activity/K9Activity.java b/src/com/fsck/k9/activity/K9Activity.java
index e9a68eccf..c346ab71c 100644
--- a/src/com/fsck/k9/activity/K9Activity.java
+++ b/src/com/fsck/k9/activity/K9Activity.java
@@ -23,14 +23,8 @@ public class K9Activity extends Activity {
@Override
public void onCreate(Bundle icicle) {
- onCreate(icicle, true);
- }
-
- public void onCreate(Bundle icicle, boolean useTheme) {
setLanguage(this, K9.getK9Language());
- if (useTheme) {
- setTheme(K9.getK9Theme());
- }
+ setTheme(K9.getK9ThemeResourceId());
super.onCreate(icicle);
setupFormats();
diff --git a/src/com/fsck/k9/activity/K9ExpandableListActivity.java b/src/com/fsck/k9/activity/K9ExpandableListActivity.java
deleted file mode 100644
index 3e1b509a7..000000000
--- a/src/com/fsck/k9/activity/K9ExpandableListActivity.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.fsck.k9.activity;
-
-import android.app.ExpandableListActivity;
-import android.os.Bundle;
-
-import com.fsck.k9.K9;
-
-/**
- * @see ExpandableListActivity
- */
-public class K9ExpandableListActivity extends ExpandableListActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(K9.getK9Theme());
- super.onCreate(savedInstanceState);
- }
-}
diff --git a/src/com/fsck/k9/activity/K9ListActivity.java b/src/com/fsck/k9/activity/K9ListActivity.java
index 204182be2..613b22119 100644
--- a/src/com/fsck/k9/activity/K9ListActivity.java
+++ b/src/com/fsck/k9/activity/K9ListActivity.java
@@ -13,7 +13,7 @@ public class K9ListActivity extends ListActivity {
@Override
public void onCreate(Bundle icicle) {
K9Activity.setLanguage(this, K9.getK9Language());
- setTheme(K9.getK9Theme());
+ setTheme(K9.getK9ThemeResourceId());
super.onCreate(icicle);
setupFormats();
}
diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java
index 927078cf2..e02186771 100644
--- a/src/com/fsck/k9/activity/MessageCompose.java
+++ b/src/com/fsck/k9/activity/MessageCompose.java
@@ -15,15 +15,18 @@ import com.fsck.k9.mail.*;
import com.fsck.k9.view.MessageWebView;
import org.apache.james.mime4j.codec.EncoderUtil;
import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
@@ -31,14 +34,17 @@ import android.provider.OpenableColumns;
import android.text.util.Rfc822Tokenizer;
import android.util.Log;
import android.util.TypedValue;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
import android.view.Window;
import android.webkit.WebView;
import android.widget.AutoCompleteTextView.Validator;
+import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
@@ -84,6 +90,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private static final int DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED = 2;
private static final int DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY = 3;
private static final int DIALOG_CONFIRM_DISCARD_ON_BACK = 4;
+ private static final int DIALOG_CHOOSE_IDENTITY = 5;
private static final long INVALID_DRAFT_ID = MessagingController.INVALID_MESSAGE_ID;
@@ -128,12 +135,11 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private static final int MSG_DISCARDED_DRAFT = 6;
private static final int ACTIVITY_REQUEST_PICK_ATTACHMENT = 1;
- private static final int ACTIVITY_CHOOSE_IDENTITY = 2;
- private static final int ACTIVITY_CHOOSE_ACCOUNT = 3;
- private static final int CONTACT_PICKER_TO = 4;
- private static final int CONTACT_PICKER_CC = 5;
- private static final int CONTACT_PICKER_BCC = 6;
+ private static final int CONTACT_PICKER_TO = 2;
+ private static final int CONTACT_PICKER_CC = 3;
+ private static final int CONTACT_PICKER_BCC = 4;
+ private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[0];
/**
* Regular expression to remove the first localized "Re:" prefix in subjects.
@@ -186,7 +192,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private QuotedTextMode mQuotedTextMode = QuotedTextMode.NONE;
- private TextView mFromView;
+ private Button mChooseIdentityButton;
private LinearLayout mCcWrapper;
private LinearLayout mBccWrapper;
private MultiAutoCompleteTextView mToView;
@@ -410,7 +416,14 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mAddressAdapter = EmailAddressAdapter.getInstance(this);
mAddressValidator = new EmailAddressValidator();
- mFromView = (TextView) findViewById(R.id.from);
+ mChooseIdentityButton = (Button) findViewById(R.id.identity);
+ mChooseIdentityButton.setOnClickListener(this);
+
+ if (mAccount.getIdentities().size() == 1 &&
+ Preferences.getPreferences(this).getAvailableAccounts().size() == 1) {
+ mChooseIdentityButton.setVisibility(View.GONE);
+ }
+
mToView = (MultiAutoCompleteTextView) findViewById(R.id.to);
mCcView = (MultiAutoCompleteTextView) findViewById(R.id.cc);
mBccView = (MultiAutoCompleteTextView) findViewById(R.id.bcc);
@@ -548,8 +561,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mQuotedTextEdit.setOnClickListener(this);
mQuotedTextDelete.setOnClickListener(this);
- mFromView.setVisibility(View.GONE);
-
mToView.setAdapter(mAddressAdapter);
mToView.setTokenizer(new Rfc822Tokenizer());
mToView.setValidator(mAddressValidator);
@@ -607,8 +618,9 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
mReadReceipt = mAccount.isMessageReadReceiptAlways();
mQuoteStyle = mAccount.getQuoteStyle();
+ updateFrom();
+
if (!mSourceMessageProcessed) {
- updateFrom();
updateSignature();
if (ACTION_REPLY.equals(action) ||
@@ -941,7 +953,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
if (mQuotedTextMode != QuotedTextMode.NONE && mMessageFormat == MessageFormat.HTML) {
mQuotedHtmlContent = (InsertableHtmlContent) savedInstanceState.getSerializable(STATE_KEY_HTML_QUOTE);
if (mQuotedHtmlContent != null && mQuotedHtmlContent.getQuotedContent() != null) {
- mQuotedHTML.loadDataWithBaseURL("http://", mQuotedHtmlContent.getQuotedContent(), "text/html", "utf-8", null);
+ mQuotedHTML.setText(mQuotedHtmlContent.getQuotedContent(), "text/html");
}
}
mDraftId = savedInstanceState.getLong(STATE_KEY_DRAFT_ID);
@@ -1776,12 +1788,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
addAttachment(data.getData());
mDraftNeedsSaving = true;
break;
- case ACTIVITY_CHOOSE_IDENTITY:
- onIdentityChosen(data);
- break;
- case ACTIVITY_CHOOSE_ACCOUNT:
- onAccountChosen(data);
- break;
case CONTACT_PICKER_TO:
case CONTACT_PICKER_CC:
case CONTACT_PICKER_BCC:
@@ -1811,15 +1817,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
startActivityForResult(mContacts.contactPickerIntent(), resultId);
}
-
-
- private void onAccountChosen(final Intent intent) {
- final Bundle extras = intent.getExtras();
- final String uuid = extras.getString(ChooseAccount.EXTRA_ACCOUNT);
- final Identity identity = (Identity) extras.getSerializable(ChooseAccount.EXTRA_IDENTITY);
-
- final Account account = Preferences.getPreferences(this).getAccount(uuid);
-
+ private void onAccountChosen(Account account, Identity identity) {
if (!mAccount.equals(account)) {
if (K9.DEBUG) {
Log.v(K9.LOG_TAG, "Switching account from " + mAccount + " to " + account);
@@ -1863,11 +1861,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
switchToIdentity(identity);
}
- private void onIdentityChosen(Intent intent) {
- Bundle bundle = intent.getExtras();
- switchToIdentity((Identity) bundle.getSerializable(ChooseIdentity.EXTRA_IDENTITY));
- }
-
private void switchToIdentity(Identity identity) {
mIdentity = identity;
mIdentityChanged = true;
@@ -1877,10 +1870,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
}
private void updateFrom() {
- if (mIdentityChanged) {
- mFromView.setVisibility(View.VISIBLE);
- }
- mFromView.setText(getString(R.string.message_view_from_format, mIdentity.getName(), mIdentity.getEmail()));
+ mChooseIdentityButton.setText(mIdentity.getEmail());
}
private void updateSignature() {
@@ -1923,6 +1913,9 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
MessagingController.getInstance(getApplication()).loadMessageForView(account, folderName, sourceMessageUid, null);
}
break;
+ case R.id.identity:
+ showDialog(DIALOG_CHOOSE_IDENTITY);
+ break;
}
}
@@ -1989,9 +1982,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
case R.id.add_attachment_video:
onAddAttachment2("video/*");
break;
- case R.id.choose_identity:
- onChooseIdentity();
- break;
case R.id.read_receipt:
onReadReceipt();
default:
@@ -2000,25 +1990,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
return true;
}
- private void onChooseIdentity() {
- // keep things simple: trigger account choice only if there are more
- // than 1 account
- mIgnoreOnPause = true;
- if (Preferences.getPreferences(this).getAvailableAccounts().size() > 1) {
- final Intent intent = new Intent(this, ChooseAccount.class);
- intent.putExtra(ChooseAccount.EXTRA_ACCOUNT, mAccount.getUuid());
- intent.putExtra(ChooseAccount.EXTRA_IDENTITY, mIdentity);
- startActivityForResult(intent, ACTIVITY_CHOOSE_ACCOUNT);
- } else if (mAccount.getIdentities().size() > 1) {
- Intent intent = new Intent(this, ChooseIdentity.class);
- intent.putExtra(ChooseIdentity.EXTRA_ACCOUNT, mAccount.getUuid());
- startActivityForResult(intent, ACTIVITY_CHOOSE_IDENTITY);
- } else {
- Toast.makeText(this, getString(R.string.no_identities),
- Toast.LENGTH_LONG).show();
- }
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@@ -2148,6 +2119,21 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
}
})
.create();
+ case DIALOG_CHOOSE_IDENTITY:
+ Context context = new ContextWrapper(this);
+ context.setTheme(K9.getK9ThemeResourceId(K9.THEME_LIGHT));
+ Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.send_as);
+ final IdentityAdapter adapter = new IdentityAdapter(context, getLayoutInflater());
+ builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ IdentityContainer container = (IdentityContainer) adapter.getItem(which);
+ onAccountChosen(container.account, container.identity);
+ }
+ });
+
+ return builder.create();
}
return super.onCreateDialog(id);
}
@@ -2289,8 +2275,15 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
}
if (ACTION_REPLY_ALL.equals(action)) {
+ if (message.getReplyTo().length > 0) {
+ for (Address address : message.getFrom()) {
+ if (!mAccount.isAnIdentity(address)) {
+ addAddress(mToView, address);
+ }
+ }
+ }
for (Address address : message.getRecipients(RecipientType.TO)) {
- if (!mAccount.isAnIdentity(address)) {
+ if (!mAccount.isAnIdentity(address) && !Utility.arrayContains(replyToAddresses, address)) {
addAddress(mToView, address);
}
@@ -2395,7 +2388,15 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
if (k9identity.containsKey(IdentityField.ORIGINAL_MESSAGE)) {
mMessageReference = null;
try {
- mMessageReference = new MessageReference(k9identity.get(IdentityField.ORIGINAL_MESSAGE));
+ String originalMessage = k9identity.get(IdentityField.ORIGINAL_MESSAGE);
+ MessageReference messageReference = new MessageReference(originalMessage);
+
+ // Check if this is a valid account in our database
+ Preferences prefs = Preferences.getPreferences(getApplicationContext());
+ Account account = prefs.getAccount(messageReference.accountUuid);
+ if (account != null) {
+ mMessageReference = messageReference;
+ }
} catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Could not decode message reference in identity.", e);
}
@@ -2420,19 +2421,19 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
updateFrom();
Integer bodyLength = k9identity.get(IdentityField.LENGTH) != null
- ? Integer.parseInt(k9identity.get(IdentityField.LENGTH))
+ ? Integer.valueOf(k9identity.get(IdentityField.LENGTH))
: 0;
Integer bodyOffset = k9identity.get(IdentityField.OFFSET) != null
- ? Integer.parseInt(k9identity.get(IdentityField.OFFSET))
+ ? Integer.valueOf(k9identity.get(IdentityField.OFFSET))
: 0;
Integer bodyFooterOffset = k9identity.get(IdentityField.FOOTER_OFFSET) != null
- ? Integer.parseInt(k9identity.get(IdentityField.FOOTER_OFFSET))
+ ? Integer.valueOf(k9identity.get(IdentityField.FOOTER_OFFSET))
: null;
Integer bodyPlainLength = k9identity.get(IdentityField.PLAIN_LENGTH) != null
- ? Integer.parseInt(k9identity.get(IdentityField.PLAIN_LENGTH))
+ ? Integer.valueOf(k9identity.get(IdentityField.PLAIN_LENGTH))
: null;
Integer bodyPlainOffset = k9identity.get(IdentityField.PLAIN_OFFSET) != null
- ? Integer.parseInt(k9identity.get(IdentityField.PLAIN_OFFSET))
+ ? Integer.valueOf(k9identity.get(IdentityField.PLAIN_OFFSET))
: null;
mQuoteStyle = k9identity.get(IdentityField.QUOTE_STYLE) != null
? QuoteStyle.valueOf(k9identity.get(IdentityField.QUOTE_STYLE))
@@ -2482,7 +2483,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
} else {
mQuotedHtmlContent.setFooterInsertionPoint(bodyOffset);
}
- mQuotedHTML.loadDataWithBaseURL("http://", mQuotedHtmlContent.getQuotedContent(), "text/html", "utf-8", null);
+ mQuotedHTML.setText(mQuotedHtmlContent.getQuotedContent(), "text/html");
}
}
if (bodyPlainOffset != null && bodyPlainLength != null) {
@@ -2665,7 +2666,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
// Add the HTML reply header to the top of the content.
mQuotedHtmlContent = quoteOriginalHtmlMessage(mSourceMessage, content, mQuoteStyle);
// Load the message with the reply header.
- mQuotedHTML.loadDataWithBaseURL("http://", mQuotedHtmlContent.getQuotedContent(), "text/html", "utf-8", null);
+ mQuotedHTML.setText(mQuotedHtmlContent.getQuotedContent(), "text/html");
mQuotedText.setText(quoteOriginalTextMessage(mSourceMessage,
getBodyTextFromMessage(mSourceMessage, MessageFormat.TEXT), mQuoteStyle));
@@ -3177,4 +3178,139 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
return insertable;
}
+
+ /**
+ * Used to store an {@link Identity} instance together with the {@link Account} it belongs to.
+ *
+ * @see IdentityAdapter
+ */
+ static class IdentityContainer {
+ public final Identity identity;
+ public final Account account;
+
+ IdentityContainer(Identity identity, Account account) {
+ this.identity = identity;
+ this.account = account;
+ }
+ }
+
+ /**
+ * Adapter for the Choose identity list view.
+ *
+ *
+ * Account names are displayed as section headers, identities as selectable list items.
+ *
+ */
+ static class IdentityAdapter extends BaseAdapter {
+ private LayoutInflater mLayoutInflater;
+ private List
+ *
+ * @see #sanitizeFilename(String)
+ */
+ private static final String INVALID_CHARACTERS = "[^\\w !#$%&'()\\-@\\^`{}~.,]+";
+
+ /**
+ * Invalid characters in a file name are replaced by this character.
+ *
+ * @see #sanitizeFilename(String)
+ */
+ private static final String REPLACEMENT_CHARACTER = "_";
// \u00A0 (non-breaking space) happens to be used by French MUA
@@ -605,4 +628,16 @@ public class Utility {
cursor.close();
}
}
+
+ /**
+ * Replace characters we don't allow in file names with a replacement character.
+ *
+ * @param filename
+ * The original file name.
+ *
+ * @return The sanitized file name containing only allowed characters.
+ */
+ public static String sanitizeFilename(String filename) {
+ return filename.replaceAll(INVALID_CHARACTERS, REPLACEMENT_CHARACTER);
+ }
}
diff --git a/src/com/fsck/k9/mail/Folder.java b/src/com/fsck/k9/mail/Folder.java
index 037f83aad..e34674af4 100644
--- a/src/com/fsck/k9/mail/Folder.java
+++ b/src/com/fsck/k9/mail/Folder.java
@@ -1,6 +1,7 @@
package com.fsck.k9.mail;
import java.util.Date;
+import java.util.Map;
import android.util.Log;
import com.fsck.k9.Account;
@@ -98,11 +99,15 @@ public abstract class Folder {
public abstract Message[] getMessages(String[] uids, MessageRetrievalListener listener)
throws MessagingException;
- public abstract void appendMessages(Message[] messages) throws MessagingException;
+ public abstract Map appendMessages(Message[] messages) throws MessagingException;
- public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {}
+ public Map copyMessages(Message[] msgs, Folder folder) throws MessagingException {
+ return null;
+ }
- public void moveMessages(Message[] msgs, Folder folder) throws MessagingException {}
+ public Map moveMessages(Message[] msgs, Folder folder) throws MessagingException {
+ return null;
+ }
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {}
diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java
index 405243d4b..e1908670d 100644
--- a/src/com/fsck/k9/mail/internet/MimeUtility.java
+++ b/src/com/fsck/k9/mail/internet/MimeUtility.java
@@ -953,16 +953,20 @@ public class MimeUtility {
}
header = header.replaceAll("\r|\n", "");
String[] parts = header.split(";");
- if (name == null) {
+ if (name == null && parts.length > 0) {
return parts[0];
}
for (String part : parts) {
if (part.trim().toLowerCase(Locale.US).startsWith(name.toLowerCase(Locale.US))) {
- String parameter = part.split("=", 2)[1].trim();
- if (parameter.startsWith("\"") && parameter.endsWith("\"")) {
- return parameter.substring(1, parameter.length() - 1);
- } else {
- return parameter;
+ String[] partParts = part.split("=", 2);
+ if (partParts.length == 2) {
+ String parameter = partParts[1].trim();
+ int len = parameter.length();
+ if (len >= 2 && parameter.startsWith("\"") && parameter.endsWith("\"")) {
+ return parameter.substring(1, len - 1);
+ } else {
+ return parameter;
+ }
}
}
}
@@ -2178,8 +2182,11 @@ public class MimeUtility {
}
private static String getJisVariantFromAddress(String address) {
+ if (address == null)
+ return null;
if (isInDomain(address, "docomo.ne.jp") || isInDomain(address, "dwmail.jp") ||
- isInDomain(address, "pdx.ne.jp") || isInDomain(address, "willcom.com"))
+ isInDomain(address, "pdx.ne.jp") || isInDomain(address, "willcom.com") ||
+ isInDomain(address, "emnet.ne.jp") || isInDomain(address, "emobile.ne.jp"))
return "docomo";
else if (isInDomain(address, "softbank.ne.jp") || isInDomain(address, "vodafone.ne.jp") ||
isInDomain(address, "disney.ne.jp") || isInDomain(address, "vertuclub.ne.jp"))
@@ -3256,4 +3263,80 @@ public class MimeUtility {
return charset;
}
+
+ public static ViewableContainer extractPartsFromDraft(Message message)
+ throws MessagingException {
+
+ Body body = message.getBody();
+ if (message.isMimeType("multipart/mixed") && body instanceof MimeMultipart) {
+ MimeMultipart multipart = (MimeMultipart) body;
+
+ ViewableContainer container;
+ int count = multipart.getCount();
+ if (count >= 1) {
+ // The first part is either a text/plain or a multipart/alternative
+ BodyPart firstPart = multipart.getBodyPart(0);
+ container = extractTextual(firstPart);
+
+ // The rest should be attachments
+ for (int i = 1; i < count; i++) {
+ BodyPart bodyPart = multipart.getBodyPart(i);
+ container.attachments.add(bodyPart);
+ }
+ } else {
+ container = new ViewableContainer("", "", new ArrayList());
+ }
+
+ return container;
+ }
+
+ return extractTextual(message);
+ }
+
+ private static ViewableContainer extractTextual(Part part) throws MessagingException {
+ String text = "";
+ String html = "";
+ List attachments = new ArrayList();
+
+ Body firstBody = part.getBody();
+ if (part.isMimeType("text/plain")) {
+ String bodyText = getTextFromPart(part);
+ if (bodyText != null) {
+ text = fixDraftTextBody(bodyText);
+ }
+ } else if (part.isMimeType("multipart/alternative") &&
+ firstBody instanceof MimeMultipart) {
+ MimeMultipart multipart = (MimeMultipart) firstBody;
+ for (int i = 0, count = multipart.getCount(); i < count; i++) {
+ BodyPart bodyPart = multipart.getBodyPart(i);
+ String bodyText = getTextFromPart(bodyPart);
+ if (bodyText != null) {
+ if (text.length() == 0 && bodyPart.isMimeType("text/plain")) {
+ text = fixDraftTextBody(bodyText);
+ } else if (html.length() == 0 && bodyPart.isMimeType("text/html")) {
+ html = fixDraftTextBody(bodyText);
+ }
+ }
+ }
+ }
+
+ return new ViewableContainer(text, html, attachments);
+ }
+
+ /**
+ * Fix line endings of text bodies in draft messages.
+ *
+ *
+ * We create drafts with LF line endings. The values in the identity header are based on that.
+ * So we replace CRLF with LF when loading messages (from the server).
+ *
+ *
+ * @param text
+ * The body text with CRLF line endings
+ *
+ * @return The text with LF line endings
+ */
+ private static String fixDraftTextBody(String text) {
+ return text.replace("\r\n", "\n");
+ }
}
diff --git a/src/com/fsck/k9/mail/store/ImapResponseParser.java b/src/com/fsck/k9/mail/store/ImapResponseParser.java
index 6341b9520..c73324753 100644
--- a/src/com/fsck/k9/mail/store/ImapResponseParser.java
+++ b/src/com/fsck/k9/mail/store/ImapResponseParser.java
@@ -409,7 +409,7 @@ public class ImapResponseParser {
public Object getKeyedValue(Object key) {
- for (int i = 0, count = size(); i < count; i++) {
+ for (int i = 0, count = size() - 1; i < count; i++) {
if (equalsIgnoreCase(get(i), key)) {
return get(i + 1);
}
@@ -434,7 +434,7 @@ public class ImapResponseParser {
return false;
}
- for (int i = 0, count = size(); i < count; i++) {
+ for (int i = 0, count = size() - 1; i < count; i++) {
if (equalsIgnoreCase(key, get(i))) {
return true;
}
@@ -443,7 +443,7 @@ public class ImapResponseParser {
}
public int getKeyIndex(Object key) {
- for (int i = 0, count = size(); i < count; i++) {
+ for (int i = 0, count = size() - 1; i < count; i++) {
if (equalsIgnoreCase(key, get(i))) {
return i;
}
diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java
index 73e3f36d3..c764b3d61 100644
--- a/src/com/fsck/k9/mail/store/ImapStore.java
+++ b/src/com/fsck/k9/mail/store/ImapStore.java
@@ -61,6 +61,7 @@ import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.controller.MessageRetrievalListener;
+import com.fsck.k9.helper.StringUtils;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.helper.power.TracingPowerManager;
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
@@ -89,6 +90,7 @@ import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.ImapResponseParser.ImapList;
import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse;
+import com.fsck.k9.mail.store.imap.ImapUtility;
import com.fsck.k9.mail.transport.imap.ImapSettings;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
@@ -1153,24 +1155,57 @@ public class ImapStore extends Store {
}
}
+ /**
+ * Copies the given messages to the specified folder.
+ *
+ *
+ * Note:
+ * Only the UIDs of the given {@link Message} instances are used. It is assumed that all
+ * UIDs represent valid messages in this folder.
+ *
+ *
+ * @param messages
+ * The messages to copy to the specfied folder.
+ * @param folder
+ * The name of the target folder.
+ *
+ * @return The mapping of original message UIDs to the new server UIDs.
+ */
@Override
- public void copyMessages(Message[] messages, Folder folder) throws MessagingException {
+ public Map copyMessages(Message[] messages, Folder folder)
+ throws MessagingException {
if (!(folder instanceof ImapFolder)) {
throw new MessagingException("ImapFolder.copyMessages passed non-ImapFolder");
}
- if (messages.length == 0)
- return;
+ if (messages.length == 0) {
+ return null;
+ }
ImapFolder iFolder = (ImapFolder)folder;
checkOpen();
+
String[] uids = new String[messages.length];
for (int i = 0, count = messages.length; i < count; i++) {
uids[i] = messages[i].getUid();
}
+
try {
String remoteDestName = encodeString(encodeFolderName(iFolder.getPrefixedName()));
+ //TODO: Split this into multiple commands if the command exceeds a certain length.
+ mConnection.sendCommand(String.format("UID COPY %s %s",
+ Utility.combine(uids, ','),
+ remoteDestName), false);
+ ImapResponse response;
+ do {
+ response = mConnection.readResponse();
+ handleUntaggedResponse(response);
+ } while (response.mTag == null);
+
+ Map uidMap = null;
+
+ // ASH maybe this shouldn't be here at all.
if (!exists(remoteDestName)) {
/*
* If the remote folder doesn't exist we try to create it.
@@ -1180,26 +1215,70 @@ public class ImapStore extends Store {
iFolder.create();
}
- if (exists(remoteDestName)) {
- executeSimpleCommand(String.format("UID COPY %s %s",
- Utility.combine(uids, ','),
- remoteDestName));
- } else {
- throw new MessagingException("IMAPMessage.copyMessages: remote destination folder " + folder.getName()
- + " does not exist and could not be created for " + getLogId()
- , true);
+
+ if (response.size() > 1) {
+ /*
+ * If the server supports UIDPLUS, then along with the COPY response it will
+ * return an COPYUID response code, e.g.
+ *
+ * 24 OK [COPYUID 38505 304,319:320 3956:3958] Success
+ *
+ * COPYUID is followed by UIDVALIDITY, the set of UIDs of copied messages from
+ * the source folder and the set of corresponding UIDs assigned to them in the
+ * destination folder.
+ *
+ * We can use the new UIDs included in this response to update our records.
+ */
+ Object responseList = response.get(1);
+
+ if (responseList instanceof ImapList) {
+ final ImapList copyList = (ImapList) responseList;
+ if (copyList.size() >= 4 && copyList.getString(0).equals("COPYUID")) {
+ List srcUids = ImapUtility.getImapSequenceValues(
+ copyList.getString(2));
+ List destUids = ImapUtility.getImapSequenceValues(
+ copyList.getString(3));
+
+ if (srcUids != null && destUids != null) {
+ if (srcUids.size() == destUids.size()) {
+ Iterator srcUidsIterator = srcUids.iterator();
+ Iterator destUidsIterator = destUids.iterator();
+ uidMap = new HashMap();
+ while (srcUidsIterator.hasNext() &&
+ destUidsIterator.hasNext()) {
+ String srcUid = srcUidsIterator.next();
+ String destUid = destUidsIterator.next();
+ uidMap.put(srcUid, destUid);
+ }
+ } else {
+ if (K9.DEBUG) {
+ Log.v(K9.LOG_TAG, "Parse error: size of source UIDs " +
+ "list is not the same as size of destination " +
+ "UIDs list.");
+ }
+ }
+ } else {
+ if (K9.DEBUG) {
+ Log.v(K9.LOG_TAG, "Parsing of the sequence set failed.");
+ }
+ }
+ }
+ }
}
+
+ return uidMap;
} catch (IOException ioe) {
throw ioExceptionHandler(mConnection, ioe);
}
}
@Override
- public void moveMessages(Message[] messages, Folder folder) throws MessagingException {
+ public Map moveMessages(Message[] messages, Folder folder) throws MessagingException {
if (messages.length == 0)
- return;
- copyMessages(messages, folder);
+ return null;
+ Map uidMap = copyMessages(messages, folder);
setFlags(messages, new Flag[] { Flag.DELETED }, true);
+ return uidMap;
}
@Override
@@ -1675,13 +1754,16 @@ public class ImapStore extends Store {
if (fetchList.containsKey("BODY")) {
int index = fetchList.getKeyIndex("BODY") + 2;
- result = fetchList.getObject(index);
+ int size = fetchList.size();
+ if (index < size) {
+ result = fetchList.getObject(index);
- // Check if there's an origin octet
- if (result instanceof String) {
- String originOctet = (String)result;
- if (originOctet.startsWith("<")) {
- result = fetchList.getObject(index + 1);
+ // Check if there's an origin octet
+ if (result instanceof String) {
+ String originOctet = (String) result;
+ if (originOctet.startsWith("<") && (index + 1) < size) {
+ result = fetchList.getObject(index + 1);
+ }
}
}
}
@@ -1938,20 +2020,30 @@ public class ImapStore extends Store {
}
/**
- * Appends the given messages to the selected folder. This implementation also determines
- * the new UID of the given message on the IMAP server and sets the Message's UID to the
- * new server UID.
+ * Appends the given messages to the selected folder.
+ *
+ *
+ * This implementation also determines the new UIDs of the given messages on the IMAP
+ * server and changes the messages' UIDs to the new server UIDs.
+ *
+ *
+ * @param messages
+ * The messages to append to the folder.
+ *
+ * @return The mapping of original message UIDs to the new server UIDs.
*/
@Override
- public void appendMessages(Message[] messages) throws MessagingException {
+ public Map appendMessages(Message[] messages) throws MessagingException {
checkOpen();
try {
+ Map uidMap = new HashMap();
for (Message message : messages) {
mConnection.sendCommand(
String.format("APPEND %s (%s) {%d}",
encodeString(encodeFolderName(getPrefixedName())),
combineFlags(message.getFlags()),
message.calculateSize()), false);
+
ImapResponse response;
do {
response = mConnection.readResponse();
@@ -1965,16 +2057,54 @@ public class ImapStore extends Store {
}
} while (response.mTag == null);
- String newUid = getUidFromMessageId(message);
- if (K9.DEBUG)
- Log.d(K9.LOG_TAG, "Got UID " + newUid + " for message for " + getLogId());
+ if (response.size() > 1) {
+ /*
+ * If the server supports UIDPLUS, then along with the APPEND response it
+ * will return an APPENDUID response code, e.g.
+ *
+ * 11 OK [APPENDUID 2 238268] APPEND completed
+ *
+ * We can use the UID included in this response to update our records.
+ */
+ Object responseList = response.get(1);
- if (newUid != null) {
- message.setUid(newUid);
+ if (responseList instanceof ImapList) {
+ ImapList appendList = (ImapList) responseList;
+ if (appendList.size() >= 3 &&
+ appendList.getString(0).equals("APPENDUID")) {
+
+ String newUid = appendList.getString(2);
+
+ if (!StringUtils.isNullOrEmpty(newUid)) {
+ message.setUid(newUid);
+ uidMap.put(message.getUid(), newUid);
+ continue;
+ }
+ }
+ }
}
+ /*
+ * This part is executed in case the server does not support UIDPLUS or does
+ * not implement the APPENDUID response code.
+ */
+ String newUid = getUidFromMessageId(message);
+ if (K9.DEBUG) {
+ Log.d(K9.LOG_TAG, "Got UID " + newUid + " for message for " + getLogId());
+ }
+ if (!StringUtils.isNullOrEmpty(newUid)) {
+ uidMap.put(message.getUid(), newUid);
+ message.setUid(newUid);
+ }
}
+
+ /*
+ * We need uidMap to be null if new UIDs are not available to maintain consistency
+ * with the behavior of other similar methods (copyMessages, moveMessages) which
+ * return null.
+ */
+ return (uidMap.size() == 0) ? null : uidMap;
} catch (IOException ioe) {
throw ioExceptionHandler(mConnection, ioe);
}
@@ -2693,13 +2823,8 @@ public class ImapStore extends Store {
private static final long serialVersionUID = 3725007182205882394L;
String mAlertText;
- public ImapException(String message, String alertText, Throwable throwable) {
- super(message, throwable);
- this.mAlertText = alertText;
- }
-
public ImapException(String message, String alertText) {
- super(message);
+ super(message, true);
this.mAlertText = alertText;
}
diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java
index 5b26777c8..aa32cf2ed 100644
--- a/src/com/fsck/k9/mail/store/LocalStore.java
+++ b/src/com/fsck/k9/mail/store/LocalStore.java
@@ -2109,21 +2109,23 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
}
@Override
- public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {
+ public Map copyMessages(Message[] msgs, Folder folder) throws MessagingException {
if (!(folder instanceof LocalFolder)) {
throw new MessagingException("copyMessages called with incorrect Folder");
}
- ((LocalFolder) folder).appendMessages(msgs, true);
+ return ((LocalFolder) folder).appendMessages(msgs, true);
}
@Override
- public void moveMessages(final Message[] msgs, final Folder destFolder) throws MessagingException {
+ public Map moveMessages(final Message[] msgs, final Folder destFolder) throws MessagingException {
if (!(destFolder instanceof LocalFolder)) {
throw new MessagingException("moveMessages called with non-LocalFolder");
}
final LocalFolder lDestFolder = (LocalFolder)destFolder;
+ final Map uidMap = new HashMap();
+
try {
database.execute(false, new DbCallback() {
@Override
@@ -2149,7 +2151,10 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
Log.d(K9.LOG_TAG, "Updating folder_id to " + lDestFolder.getId() + " for message with UID "
+ message.getUid() + ", id " + lMessage.getId() + " currently in folder " + getName());
- message.setUid(K9.LOCAL_UID_PREFIX + UUID.randomUUID().toString());
+ String newUid = K9.LOCAL_UID_PREFIX + UUID.randomUUID().toString();
+ message.setUid(newUid);
+
+ uidMap.put(oldUID, newUid);
db.execSQL("UPDATE messages " + "SET folder_id = ?, uid = ? " + "WHERE id = ?", new Object[] {
lDestFolder.getId(),
@@ -2157,6 +2162,11 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
lMessage.getId()
});
+ /*
+ * Add a placeholder message so we won't download the original
+ * message again if we synchronize before the remote move is
+ * complete.
+ */
LocalMessage placeHolder = new LocalMessage(oldUID, LocalFolder.this);
placeHolder.setFlagInternal(Flag.DELETED, true);
placeHolder.setFlagInternal(Flag.SEEN, true);
@@ -2168,6 +2178,7 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
return null;
}
});
+ return uidMap;
} catch (WrappedException e) {
throw(MessagingException) e.getCause();
}
@@ -2213,8 +2224,8 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
* message, retrieve the appropriate local message instance first (if it already exists).
*/
@Override
- public void appendMessages(Message[] messages) throws MessagingException {
- appendMessages(messages, false);
+ public Map appendMessages(Message[] messages) throws MessagingException {
+ return appendMessages(messages, false);
}
public void expunge() throws MessagingException {
@@ -2261,10 +2272,12 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
* message, retrieve the appropriate local message instance first (if it already exists).
* @param messages
* @param copy
+ * @return Map uidMap of srcUids -> destUids
*/
- private void appendMessages(final Message[] messages, final boolean copy) throws MessagingException {
+ private Map appendMessages(final Message[] messages, final boolean copy) throws MessagingException {
open(OpenMode.READ_WRITE);
try {
+ final Map uidMap = new HashMap();
database.execute(true, new DbCallback() {
@Override
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
@@ -2277,11 +2290,26 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
long oldMessageId = -1;
String uid = message.getUid();
if (uid == null || copy) {
- uid = K9.LOCAL_UID_PREFIX + UUID.randomUUID().toString();
- if (!copy) {
- message.setUid(uid);
+ /*
+ * Create a new message in the database
+ */
+ String randomLocalUid = K9.LOCAL_UID_PREFIX +
+ UUID.randomUUID().toString();
+
+ if (copy) {
+ // Save mapping: source UID -> target UID
+ uidMap.put(uid, randomLocalUid);
+ } else {
+ // Modify the Message instance to reference the new UID
+ message.setUid(randomLocalUid);
}
+
+ // The message will be saved with the newly generated UID
+ uid = randomLocalUid;
} else {
+ /*
+ * Replace an existing message in the database
+ */
LocalMessage oldMessage = (LocalMessage) getMessage(uid);
if (oldMessage != null) {
@@ -2298,12 +2326,29 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
deleteAttachments(message.getUid());
}
- ViewableContainer container =
- MimeUtility.extractTextAndAttachments(mApplication, message);
+ boolean isDraft = (message.getHeader(K9.IDENTITY_HEADER) != null);
- List attachments = container.attachments;
- String text = container.text;
- String html = HtmlConverter.convertEmoji2Img(container.html);
+ List attachments;
+ String text;
+ String html;
+ if (isDraft) {
+ // Don't modify the text/plain or text/html part of our own
+ // draft messages because this will cause the values stored in
+ // the identity header to be wrong.
+ ViewableContainer container =
+ MimeUtility.extractPartsFromDraft(message);
+
+ text = container.text;
+ html = container.html;
+ attachments = container.attachments;
+ } else {
+ ViewableContainer container =
+ MimeUtility.extractTextAndAttachments(mApplication, message);
+
+ attachments = container.attachments;
+ text = container.text;
+ html = HtmlConverter.convertEmoji2Img(container.html);
+ }
String preview = calculateContentPreview(text);
@@ -2361,6 +2406,7 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
return null;
}
});
+ return uidMap;
} catch (WrappedException e) {
throw(MessagingException) e.getCause();
}
diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java
index 494a9449d..9271255e3 100644
--- a/src/com/fsck/k9/mail/store/Pop3Store.java
+++ b/src/com/fsck/k9/mail/store/Pop3Store.java
@@ -24,6 +24,7 @@ import java.util.LinkedList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
public class Pop3Store extends Store {
public static final String STORE_TYPE = "POP3";
@@ -889,7 +890,8 @@ public class Pop3Store extends Store {
}
@Override
- public void appendMessages(Message[] messages) throws MessagingException {
+ public Map appendMessages(Message[] messages) throws MessagingException {
+ return null;
}
@Override
diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java
index 6818ac263..e9c79f9a0 100644
--- a/src/com/fsck/k9/mail/store/WebDavStore.java
+++ b/src/com/fsck/k9/mail/store/WebDavStore.java
@@ -1339,13 +1339,15 @@ public class WebDavStore extends Store {
}
@Override
- public void copyMessages(Message[] messages, Folder folder) throws MessagingException {
+ public Map copyMessages(Message[] messages, Folder folder) throws MessagingException {
moveOrCopyMessages(messages, folder.getName(), false);
+ return null;
}
@Override
- public void moveMessages(Message[] messages, Folder folder) throws MessagingException {
+ public Map moveMessages(Message[] messages, Folder folder) throws MessagingException {
moveOrCopyMessages(messages, folder.getName(), true);
+ return null;
}
@Override
@@ -1920,8 +1922,9 @@ public class WebDavStore extends Store {
}
@Override
- public void appendMessages(Message[] messages) throws MessagingException {
+ public Map appendMessages(Message[] messages) throws MessagingException {
appendWebDavMessages(messages);
+ return null;
}
public Message[] appendWebDavMessages(Message[] messages) throws MessagingException {
diff --git a/src/com/fsck/k9/mail/store/imap/ImapUtility.java b/src/com/fsck/k9/mail/store/imap/ImapUtility.java
new file mode 100644
index 000000000..d0ac2b31a
--- /dev/null
+++ b/src/com/fsck/k9/mail/store/imap/ImapUtility.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 The K-9 Dog Walkers
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fsck.k9.mail.store.imap;
+
+import android.util.Log;
+
+import com.fsck.k9.K9;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility methods for use with IMAP.
+ */
+public class ImapUtility {
+ /**
+ * Gets all of the values in a sequence set per RFC 3501.
+ *
+ *
+ * Any ranges are expanded into a list of individual numbers.
+ *
+ *
+ * @param set
+ * The sequence set string as received by the server.
+ *
+ * @return The list of IDs as strings in this sequence set. If the set is invalid, an empty
+ * list is returned.
+ */
+ public static List getImapSequenceValues(String set) {
+ ArrayList list = new ArrayList();
+ if (set != null) {
+ String[] setItems = set.split(",");
+ for (String item : setItems) {
+ if (item.indexOf(':') == -1) {
+ // simple item
+ try {
+ Integer.parseInt(item); // Don't need the value; just ensure it's valid
+ list.add(item);
+ } catch (NumberFormatException e) {
+ Log.d(K9.LOG_TAG, "Invalid UID value", e);
+ }
+ } else {
+ // range
+ list.addAll(getImapRangeValues(item));
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Expand the given number range into a list of individual numbers.
+ *
+ *