mirror of
https://github.com/moparisthebest/k-9
synced 2025-02-11 12:40:22 -05:00
Merge of remote branch 'origin/master' into ms-eas.
This commit is contained in:
commit
cad8390140
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="15006"
|
||||
android:versionName="4.106" package="com.fsck.k9"
|
||||
android:versionCode="15011"
|
||||
android:versionName="4.112" package="com.fsck.k9"
|
||||
>
|
||||
<uses-sdk
|
||||
android:minSdkVersion="7"
|
||||
@ -43,13 +43,13 @@
|
||||
<uses-permission android:name="com.fsck.k9.permission.REMOTE_CONTROL"/>
|
||||
<permission android:name="com.fsck.k9.permission.READ_MESSAGES"
|
||||
android:permissionGroup="android.permission-group.MESSAGES"
|
||||
android:protectionLevel="normal"
|
||||
android:protectionLevel="dangerous"
|
||||
android:label="@string/read_messages_label"
|
||||
android:description="@string/read_messages_desc"/>
|
||||
<uses-permission android:name="com.fsck.k9.permission.READ_MESSAGES"/>
|
||||
<permission android:name="com.fsck.k9.permission.DELETE_MESSAGES"
|
||||
android:permissionGroup="android.permission-group.MESSAGES"
|
||||
android:protectionLevel="normal"
|
||||
android:protectionLevel="dangerous"
|
||||
android:label="@string/delete_messages_label"
|
||||
android:description="@string/read_messages_desc"/>
|
||||
<uses-permission android:name="com.fsck.k9.permission.DELETE_MESSAGES"/>
|
||||
@ -208,6 +208,10 @@
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="locale"
|
||||
>
|
||||
<intent-filter>
|
||||
<!-- This action is only to allow an entry point for launcher shortcuts -->
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.MessageView"
|
||||
@ -387,5 +391,25 @@ otherwise it would make K-9 start at the wrong time
|
||||
android:readPermission="com.fsck.k9.permission.READ_MESSAGES"
|
||||
android:writePermission="com.fsck.k9.permission.DELETE_MESSAGES"
|
||||
/>
|
||||
|
||||
<receiver
|
||||
android:name=".provider.UnreadWidgetProvider"
|
||||
android:label="@string/unread_widget_label"
|
||||
android:icon="@drawable/icon">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/unread_widget_info" />
|
||||
</receiver>
|
||||
<activity android:name=".activity.UnreadWidgetConfiguration">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.AccountList">
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
20
images/show_more_indicator.svg
Normal file
20
images/show_more_indicator.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.0"
|
||||
width="168.45"
|
||||
height="113.9081"
|
||||
id="svg2393">
|
||||
<defs
|
||||
id="defs2395" />
|
||||
<g
|
||||
transform="translate(-41.490136,-32.550987)"
|
||||
id="layer1">
|
||||
<path
|
||||
d="M 125.71429,102.3114 L 209.93844,32.550987 L 209.9357,76.729057 L 125.71429,146.45909 L 41.492876,76.729057 L 41.490136,32.550987 L 125.71429,102.3114 z"
|
||||
id="path2439"
|
||||
style="fill:#595959;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 735 B |
BIN
res/drawable-hdpi/show_less.png
Normal file
BIN
res/drawable-hdpi/show_less.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 393 B |
BIN
res/drawable-hdpi/show_more.png
Normal file
BIN
res/drawable-hdpi/show_more.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 389 B |
9
res/drawable/rounded_corners.xml
Normal file
9
res/drawable/rounded_corners.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#a0000000"/>
|
||||
<corners android:radius="7dp"/>
|
||||
|
||||
</shape>
|
14
res/drawable/separator_area_background.xml
Normal file
14
res/drawable/separator_area_background.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:drawable="@android:drawable/menuitem_background"
|
||||
android:state_pressed="true" />
|
||||
|
||||
<item android:drawable="@android:drawable/menuitem_background"
|
||||
android:state_focused="true"
|
||||
android:state_enabled="true"
|
||||
android:state_window_focused="true" />
|
||||
|
||||
<item android:drawable="@color/message_view_header_background" />
|
||||
|
||||
</selector>
|
BIN
res/drawable/show_less.png
Normal file
BIN
res/drawable/show_less.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 300 B |
BIN
res/drawable/show_more.png
Normal file
BIN
res/drawable/show_more.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 299 B |
9
res/drawable/unread_count_background.xml
Normal file
9
res/drawable/unread_count_background.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#ffcc0000"/>
|
||||
<corners android:radius="17dp"/>
|
||||
|
||||
</shape>
|
15
res/drawable/unread_widget_background.xml
Normal file
15
res/drawable/unread_widget_background.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- FIXME: find a nicer looking way than using 'menuitem_background' -->
|
||||
<item android:drawable="@android:drawable/menuitem_background"
|
||||
android:state_pressed="true" />
|
||||
|
||||
<item android:drawable="@android:drawable/menuitem_background"
|
||||
android:state_focused="true"
|
||||
android:state_enabled="true"
|
||||
android:state_window_focused="true" />
|
||||
|
||||
<item android:drawable="@android:color/transparent" />
|
||||
|
||||
</selector>
|
@ -1,75 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/scrolling_move_buttons"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<Button
|
||||
android:id="@+id/archive_scrolling"
|
||||
android:text="@string/message_view_archive_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/move_scrolling"
|
||||
android:text="@string/message_view_move_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/spam_scrolling"
|
||||
android:text="@string/message_view_spam_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/scrolling_buttons"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="57dip"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center_vertical">
|
||||
<Button
|
||||
android:id="@+id/previous_scrolling"
|
||||
android:text="@string/message_view_prev_action"
|
||||
android:contentDescription="@string/previous_action"
|
||||
android:textSize="35dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:padding="0dip"
|
||||
android:layout_weight="1" />
|
||||
<Button
|
||||
android:id="@+id/reply_scrolling"
|
||||
android:text="@string/reply_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1" />
|
||||
<Button
|
||||
android:id="@+id/delete_scrolling"
|
||||
android:text="@string/delete_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1" />
|
||||
<Button
|
||||
android:id="@+id/forward_scrolling"
|
||||
android:text="@string/forward_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1" />
|
||||
<Button
|
||||
android:id="@+id/next_scrolling"
|
||||
android:text="@string/message_view_next_action"
|
||||
android:contentDescription="@string/next_action"
|
||||
android:textSize="35dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:padding="0dip"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
</merge>
|
26
res/layout/account_list.xml
Normal file
26
res/layout/account_list.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="fill_parent">
|
||||
|
||||
<ListView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@android:id/empty"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical|center_horizontal">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="fill_parent">
|
||||
<ListView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
/>
|
||||
</LinearLayout>
|
@ -1,226 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.fsck.k9.view.SingleMessageView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/message_view"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<!-- Header area -->
|
||||
<com.fsck.k9.view.MessageHeader
|
||||
android:id="@+id/header_container"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="2dip"
|
||||
android:paddingRight="2dip"
|
||||
>
|
||||
<View
|
||||
android:id="@+id/chip"
|
||||
android:layout_marginTop="1dip"
|
||||
android:layout_marginBottom="1dip"
|
||||
android:layout_width="6dip"
|
||||
android:layout_height="fill_parent" />
|
||||
<LinearLayout
|
||||
android:id="@+id/top_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="2dip"
|
||||
android:paddingLeft="4dip"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:gravity="fill_horizontal"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout android:id="@+id/people"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="4dip"
|
||||
android:layout_weight="5"
|
||||
android:orientation="vertical">
|
||||
<TextView android:id="@+id/subject"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="10sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
>
|
||||
<LinearLayout
|
||||
android:id="@+id/from_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="true" >
|
||||
<TextView
|
||||
android:id="@+id/from"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="6dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/to_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="true" >
|
||||
<TextView
|
||||
android:id="@+id/to_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dip"
|
||||
android:text="@string/message_to_label"
|
||||
android:textSize="10sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
<TextView android:id="@+id/to"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="false"
|
||||
android:ellipsize="none"
|
||||
android:textSize="10sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
<LinearLayout android:id="@+id/cc_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="true">
|
||||
<TextView android:id="@+id/cc_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dip"
|
||||
android:text="@string/message_view_cc_label"
|
||||
android:textSize="10sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
<TextView android:id="@+id/cc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="false"
|
||||
android:ellipsize="none"
|
||||
android:textSize="10sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
<TextView android:id="@+id/additional_headers_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="4dip"
|
||||
android:baselineAligned="true"
|
||||
android:singleLine="false"
|
||||
android:ellipsize="none"
|
||||
android:textSize="10sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" >
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
<LinearLayout android:id="@+id/topright_container"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="0.1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right"
|
||||
>
|
||||
<TextView android:id="@+id/date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="none"
|
||||
android:textSize="10sp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
<TextView android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:textSize="10sp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="none"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
<LinearLayout android:id="@+id/icons_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:paddingTop="4dip"
|
||||
>
|
||||
<View android:id="@+id/answered"
|
||||
android:layout_width="22sp"
|
||||
android:layout_height="22sp"
|
||||
android:layout_centerVertical="true"
|
||||
android:paddingRight="4dip"
|
||||
android:background="@drawable/ic_email_answered_small" />
|
||||
<View android:id="@+id/attachment"
|
||||
android:layout_width="22sp"
|
||||
android:layout_height="22sp"
|
||||
android:layout_centerVertical="true"
|
||||
android:paddingRight="4dip"
|
||||
android:background="@drawable/ic_email_attachment_small" />
|
||||
<CheckBox android:id="@+id/flagged"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:layout_alignParentRight="true"
|
||||
style="?android:attr/starStyle" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</com.fsck.k9.view.MessageHeader>
|
||||
<LinearLayout
|
||||
android:id="@+id/show_pictures_section"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dip"
|
||||
android:paddingLeft="6dip"
|
||||
android:paddingRight="3dip"
|
||||
android:paddingTop="4dip"
|
||||
android:paddingBottom="4dip"
|
||||
android:baselineAligned="false"
|
||||
android:visibility="gone">
|
||||
<TextView
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:text="@string/message_view_show_pictures_instructions"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0" />
|
||||
<Button android:id="@+id/show_pictures"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_view_show_pictures_action" />
|
||||
android:id="@+id/message_view_header_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include layout="@layout/message_view_header"/>
|
||||
|
||||
</LinearLayout>
|
||||
<include layout="@layout/message_view_crypto_layout"/>
|
||||
|
||||
<!-- Content area -->
|
||||
<com.fsck.k9.view.MessageWebView
|
||||
android:id="@+id/message_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent" />
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="fill_parent"/>
|
||||
|
||||
<com.fsck.k9.view.AccessibleWebView
|
||||
android:id="@+id/accessible_message_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent" />
|
||||
android:layout_width="fill_parent"/>
|
||||
|
||||
<!-- Attachments area -->
|
||||
<LinearLayout
|
||||
android:id="@+id/attachments"
|
||||
android:orientation="vertical"
|
||||
<ScrollView
|
||||
android:id="@+id/attachments_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dip" />
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/inside_attachments_container"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/attachments"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dip" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/show_hidden_attachments"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_view_show_more_attachments_action"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/hidden_attachments"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dip"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<Button android:id="@+id/download_remainder"
|
||||
android:text="@string/message_view_download_remainder"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_width="fill_parent" />
|
||||
android:layout_width="fill_parent"/>
|
||||
|
||||
</com.fsck.k9.view.SingleMessageView>
|
||||
|
@ -3,27 +3,12 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
>
|
||||
<com.fsck.k9.view.ToggleScrollView
|
||||
android:id="@+id/top_view"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
android:scrollbarStyle="outsideInset"
|
||||
android:fillViewport="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:fadingEdge="none">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
<include layout="@layout/message" />
|
||||
<include layout="@layout/message_view_scrolling_buttons"/>
|
||||
</LinearLayout>
|
||||
</com.fsck.k9.view.ToggleScrollView>
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<include layout="@layout/message"/>
|
||||
|
||||
<include layout="@layout/message_view_move_buttons"/>
|
||||
|
||||
<include layout="@layout/message_view_bottom_buttons"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
289
res/layout/message_view_header.xml
Normal file
289
res/layout/message_view_header.xml
Normal file
@ -0,0 +1,289 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.fsck.k9.view.MessageHeader
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/header_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Message header area -->
|
||||
<TableLayout
|
||||
android:id="@+id/top_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="1"
|
||||
android:shrinkColumns="1"
|
||||
android:background="@color/message_view_header_background">
|
||||
|
||||
<TableRow>
|
||||
|
||||
<!-- Color chip -->
|
||||
<View
|
||||
android:id="@+id/chip"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_width="6dip"
|
||||
android:layout_height="fill_parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:paddingTop="2dip"
|
||||
android:layout_column="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Subject -->
|
||||
<TextView
|
||||
android:id="@+id/subject"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
android:textSize="10sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
<!-- From -->
|
||||
<LinearLayout
|
||||
android:id="@+id/from_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/from"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="6dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- To -->
|
||||
<LinearLayout
|
||||
android:id="@+id/to_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/to_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dip"
|
||||
android:text="@string/message_to_label"
|
||||
android:textSize="10sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/to"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
android:textSize="10sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- CC -->
|
||||
<LinearLayout
|
||||
android:id="@+id/cc_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cc_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dip"
|
||||
android:text="@string/message_view_cc_label"
|
||||
android:textSize="10sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
android:textSize="10sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/additional_headers_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="false"
|
||||
android:ellipsize="none"
|
||||
android:textSize="10sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<!-- Date/Time + Icons -->
|
||||
<LinearLayout
|
||||
android:id="@+id/topright_container"
|
||||
android:layout_marginTop="2dip"
|
||||
android:layout_marginRight="6dip"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="6dp"
|
||||
android:gravity="right">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/icons_container"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/answered"
|
||||
android:layout_width="22sp"
|
||||
android:layout_height="22sp"
|
||||
android:paddingRight="4dip"
|
||||
android:background="@drawable/ic_email_answered_small"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/flagged"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
style="?android:attr/starStyle"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="none"
|
||||
android:textSize="10sp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="10sp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="none"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</TableRow>
|
||||
|
||||
</TableLayout>
|
||||
|
||||
<!-- Separator -->
|
||||
<!-- This layout has an explicit height because otherwise there will be strange
|
||||
display issues when the additional headers are shown. -->
|
||||
<LinearLayout
|
||||
android:id="@+id/show_additional_headers_area"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="21.5dp"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:background="@drawable/separator_area_background">
|
||||
|
||||
<RelativeLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="20dp">
|
||||
|
||||
<!-- Color chip 2 -->
|
||||
<View
|
||||
android:id="@+id/chip2"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_width="6dip"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_alignParentLeft="true"/>
|
||||
|
||||
<!-- Show more/less indicator -->
|
||||
<ImageView
|
||||
android:id="@+id/show_additional_headers_icon"
|
||||
android:src="@drawable/show_more"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginRight="12dp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1.5dp"
|
||||
android:background="#59000000"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Button area -->
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right"
|
||||
android:paddingLeft="6dip"
|
||||
android:paddingRight="6dip"
|
||||
android:paddingTop="4dip"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<Button
|
||||
android:id="@+id/show_pictures"
|
||||
android:layout_width="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:layout_marginBottom="4dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_view_show_pictures_action"
|
||||
style="?android:attr/buttonStyleSmall"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/show_attachments"
|
||||
android:visibility="gone"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:layout_marginBottom="4dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_view_show_attachments_action"
|
||||
style="?android:attr/buttonStyleSmall"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/show_message"
|
||||
android:visibility="gone"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:layout_marginBottom="4dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_view_show_message_action"
|
||||
style="?android:attr/buttonStyleSmall"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/message_view_crypto_layout"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.fsck.k9.view.MessageHeader>
|
@ -1,88 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<LinearLayout
|
||||
android:id="@+id/extra_buttons"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="2dip"
|
||||
android:gravity="center_vertical">
|
||||
<Button
|
||||
android:id="@+id/reply"
|
||||
android:text="@string/reply_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
<Button
|
||||
android:id="@+id/reply_all"
|
||||
android:text="@string/reply_all_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
<Button
|
||||
android:id="@+id/forward"
|
||||
android:text="@string/forward_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/scrolling_move_buttons"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="2dip"
|
||||
android:gravity="center_vertical">
|
||||
<Button
|
||||
android:id="@+id/archive_scrolling"
|
||||
android:text="@string/message_view_archive_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/move_scrolling"
|
||||
android:text="@string/message_view_move_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/spam_scrolling"
|
||||
android:text="@string/message_view_spam_action"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/scrolling_buttons"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<Button
|
||||
android:id="@+id/previous_scrolling"
|
||||
android:text="@string/message_view_prev_action"
|
||||
android:contentDescription="@string/previous_action"
|
||||
android:textSize="35dip"
|
||||
android:padding="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/delete_scrolling"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_button_delete"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
<Button
|
||||
android:id="@+id/next_scrolling"
|
||||
android:padding="0dip"
|
||||
android:text="@string/message_view_next_action"
|
||||
android:textSize="35dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
</merge>
|
59
res/layout/unread_widget_layout.xml
Normal file
59
res/layout/unread_widget_layout.xml
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/unread_widget_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:orientation="vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/unread_widget_background"
|
||||
android:gravity="bottom|center_horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@android:dimen/app_icon_size"
|
||||
android:layout_height="@android:dimen/app_icon_size"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/unread_count"
|
||||
android:visibility="gone"
|
||||
android:textSize="10dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:paddingTop="0.5dp"
|
||||
android:paddingBottom="0.5dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:background="@drawable/unread_count_background"
|
||||
android:textColor="#ffffff"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/account_name"
|
||||
android:text="@string/app_name"
|
||||
android:ellipsize="marquee"
|
||||
android:textSize="12dp"
|
||||
android:singleLine="true"
|
||||
android:paddingTop="1dp"
|
||||
android:paddingBottom="1dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="3dp"
|
||||
android:background="@drawable/rounded_corners"
|
||||
android:textColor="#ffffff"
|
||||
android:shadowColor="#000000"
|
||||
android:shadowRadius="2.0"/>
|
||||
|
||||
</LinearLayout>
|
@ -542,14 +542,8 @@ Benvingut a la configuració del K-9. El K-9 és un client de codi obert per An
|
||||
<string name="account_settings_notification_unread_count_label">Mostra correu no llegit</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Mostra el nombre de missatges no llegits a la barra de notificació.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Botons de desplaçament</string>
|
||||
<string name="account_settings_hide_buttons_never">Mai</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Quan el teclat estigui disponible</string>
|
||||
<string name="account_settings_hide_buttons_always">Sempre</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Habilita botons emplenat</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Mostra els botons d’Arxiu, Mou, i Brossa.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Navega botons emplenament</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Sempre mostra imatges</string>
|
||||
<string name="account_settings_show_pictures_never">No</string>
|
||||
|
@ -549,14 +549,8 @@ Vítejte v nastavení pošty K-9 Mail. K-9 je open source poštovní klient pro
|
||||
<!-- NEW: <string name="account_settings_notification_unread_count_label">Show unread count</string>-->
|
||||
<!-- NEW: <string name="account_settings_notification_unread_count_summary">Show the number of unread messages in the notification bar.</string>-->
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Posunovat navigační tlačítka</string>
|
||||
<string name="account_settings_hide_buttons_never">Nikdy</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Je-li dostupná klávesnice</string>
|
||||
<string name="account_settings_hide_buttons_always">Vždy</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Povolit přesměrovací tlačítka</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Zobrazit tlačítka Archív, Přesunout a Nevyžádaná.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Posunovat přesměrovací tlačítka</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Vždy zobrazovat obrázky</string>
|
||||
<string name="account_settings_show_pictures_never">Ne</string>
|
||||
|
@ -545,14 +545,8 @@ Velkommen til K-9 Mail opsætning. K-9 er en open source mail klient for Androi
|
||||
<string name="account_settings_notification_unread_count_label">Vis antal ulæste mails</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Vis antallet af ulæste mails i statusbar.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Scroll navigations knapper</string>
|
||||
<string name="account_settings_hide_buttons_never">Aldrig</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Når tastatur er tilgængeligt</string>
|
||||
<string name="account_settings_hide_buttons_always">Altid</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Aktiver Flyt knapper</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Vis Arkiver, Flyt og Spam knapper.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Scroll Flyt knapper</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Vis altid billeder</string>
|
||||
<string name="account_settings_show_pictures_never">Aldrig</string>
|
||||
|
@ -285,6 +285,8 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
|
||||
<string name="message_view_status_attachment_not_saved">Anhang konnte nicht auf SD-Karte gespeichert werden.</string>
|
||||
<string name="message_view_show_pictures_instructions">Wählen Sie \"Bilder anzeigen\", um eingebettete Bilder abzurufen.</string>
|
||||
<string name="message_view_show_pictures_action">Bilder anzeigen</string>
|
||||
<string name="message_view_show_attachments_action">Zeige Anhänge</string>
|
||||
<string name="message_view_show_more_attachments_action">Mehr…</string>
|
||||
<string name="message_view_fetching_attachment_toast">Lade Anhang.</string>
|
||||
<string name="message_view_no_viewer">Es wurde kein Anzeigeprogramm für <xliff:g id="mimetype">%s</xliff:g> gefunden.</string>
|
||||
|
||||
@ -543,14 +545,8 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
|
||||
<string name="account_settings_notification_unread_count_label">Anzahl ungelesener Nachrichten anzeigen</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Zeigt die Anzahl der ungelesenen Nachrichten in der Statuszeile.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Scrolle Navigationsleiste</string>
|
||||
<string name="account_settings_hide_buttons_never">Leiste bleibt eingeblendet</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Nur wenn Tastatur aktiv</string>
|
||||
<string name="account_settings_hide_buttons_always">Immer</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Spam-Leiste</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Zeige Archivieren-, Verschieben- und Spam-Schaltfläche.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Scrolle Spam-Leiste</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Bilder automatisch anzeigen</string>
|
||||
<string name="account_settings_show_pictures_never">Niemals</string>
|
||||
@ -564,15 +560,15 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
|
||||
|
||||
<string name="account_settings_reply_after_quote_label">Antwort unter Zitat</string>
|
||||
<string name="account_settings_reply_after_quote_summary">Die Antwort auf eine Nachricht unterhalb der Original-Nachricht platzieren.</string>
|
||||
|
||||
<string name="account_settings_strip_signature_label">Unterschrift von zitierter Antwort entfernen</string>
|
||||
<string name="account_settings_strip_signature_summary">Beim Antworten von Nachrichten wird die Unterschrift vom zitiertem Textabschnitt entfernt</string>
|
||||
|
||||
<string name="account_settings_strip_signature_label">Entferne Signatur aus zitierter Antwort</string>
|
||||
<string name="account_settings_strip_signature_summary">Beim Antworten wird die Signatur aus dem Zitat entfernt</string>
|
||||
|
||||
<string name="account_settings_message_format_label">Formatierung</string>
|
||||
<string name="account_settings_message_format_text">Einfacher Text (Bilder und Formatierungen werden entfernt)</string>
|
||||
<string name="account_settings_message_format_html">HTML (Bilder und Formatierungen bleiben erhalten)</string>
|
||||
<string name="account_settings_message_format_auto">Automatisch (Einfacher Text, es sei denn bei Antwort auf HTML)</string>
|
||||
|
||||
<string name="account_settings_message_format_auto">Automatisch (Einfacher Text, es sei denn bei Antwort auf HTML)</string>
|
||||
|
||||
<string name="account_settings_message_read_receipt_label">Empfangsbestätigung</string>
|
||||
<string name="account_settings_message_read_receipt_summary">Immer eine Empfangsbestätigung anfordern</string>
|
||||
|
||||
@ -595,11 +591,11 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
|
||||
<string name="account_settings_crypto_auto_signature_summary">Email Adresse des Kontos verwenden um Signaturschlüssel zu schätzen.</string>
|
||||
|
||||
<string name="account_settings_crypto_auto_encrypt">Automatische verschlüsselung</string>
|
||||
<string name="account_settings_crypto_auto_encrypt_summary">Verschlüsselung aktivieren falls für den Empfänger ein öffentlichen Schlüssel abgespeichert ist.</string>
|
||||
|
||||
<string name="account_settings_crypto_auto_encrypt_summary">Verschlüsselung aktivieren falls für den Empfänger ein öffentlichen Schlüssel abgespeichert ist.</string>
|
||||
|
||||
<string name="account_settings_mail_check_frequency_label">Häufigkeit der E-Mail-Abfrage</string>
|
||||
<string name="account_settings_second_class_check_frequency_label">Häufigkeit der Abfrage für Nebenordner</string>
|
||||
|
||||
|
||||
<string name="account_settings_storage_title">Speicher</string>
|
||||
|
||||
<string name="account_settings_color_label">Farbe des Kontos</string>
|
||||
@ -1022,10 +1018,10 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
|
||||
|
||||
<string name="save_or_discard_draft_message_dlg_title">Entwurf speichern?</string>
|
||||
<string name="save_or_discard_draft_message_instructions_fmt">Entwurf speichern oder verwerfen?</string>
|
||||
|
||||
|
||||
<string name="refuse_to_save_draft_marked_encrypted_dlg_title">Speichern des Entwurfs verweigern.</string>
|
||||
<string name="refuse_to_save_draft_marked_encrypted_instructions_fmt">Die Speicherung von als verschlüsselt markierten Entwürfen verweigern.</string>
|
||||
|
||||
|
||||
<string name="continue_without_public_key_dlg_title">Ohne öffentlichen Schlüssel fortfahren?</string>
|
||||
<string name="continue_without_public_key_instructions_fmt">Einer oder mehrere Empfänger besitzen keinen abgespeicherten öffentlichen Schlüssel. Fortfahren?</string>
|
||||
|
||||
@ -1053,7 +1049,7 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
|
||||
<string name="messagelist_sent_to_me_sigil">»</string>
|
||||
<string name="messagelist_sent_cc_me_sigil">›</string>
|
||||
<string name="error_unable_to_connect">Verbindungsfehler.</string>
|
||||
|
||||
|
||||
<string name="import_export_action">Einstellungen Importieren & Exportieren</string>
|
||||
<string name="settings_export_account">Kontoeinstellungen exportieren</string>
|
||||
<string name="settings_export_all">Einstellungen und Konten exportieren</string>
|
||||
@ -1104,5 +1100,5 @@ Willkommen zum \"K-9 Mail\"-Setup. K-9 ist eine quelloffene E-Mail-Anwendung fü
|
||||
<string name="manage_accounts_move_up_action">Nach oben verschieben</string>
|
||||
<string name="manage_accounts_move_down_action">Nach unten verschieben</string>
|
||||
<string name="manage_accounts_moving_message">Konto verschieben...</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
@ -541,14 +541,8 @@ Bienvenido a la Configuración de K-9. K-9 es un cliente de correo OpenSource pa
|
||||
<string name="account_settings_notification_unread_count_label">Mostrar contador de correos sin leer</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Mostrar el número de mensajes sin leer en la barra de notificaciones.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Botones de desplazamiento</string>
|
||||
<string name="account_settings_hide_buttons_never">Nunca</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Cuando el teclado está disponible</string>
|
||||
<string name="account_settings_hide_buttons_always">Siempre</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Habilitar botones de copia</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Mostrar botones de archivar, mover y SPAM</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Botones de copia</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Mostrar imágenes</string>
|
||||
<string name="account_settings_show_pictures_never">Nunca</string>
|
||||
|
@ -537,14 +537,8 @@ Tervetuloa K-9 Mail asennukseen. K-9 on avoimen lähdekoodin sähköpostiasiak
|
||||
<string name="account_settings_notification_unread_count_label">Näytä lukemattomien viestien määrä</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Näytä lukemattomien viestien määrä osoiterivillä.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Selauksen painikkeet</string>
|
||||
<string name="account_settings_hide_buttons_never">Ei koskaan</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Kun näppäimistö on käytettävissä</string>
|
||||
<string name="account_settings_hide_buttons_always">Aina</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Ota käyttöön Siirrä-painikkeet</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Näytä Arkistoi-, Siirrä- ja Roskaposti-painikkeet.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Vieritä Siirrä painikkeet</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Näytä kuvat automaattisesti</string>
|
||||
<string name="account_settings_show_pictures_never">Ei koskaan</string>
|
||||
|
@ -567,14 +567,8 @@ Bienvenue dans la configuration de K-9 Mail. K-9 Mail est un client de messageri
|
||||
<string name="account_settings_notification_unread_count_label">Afficher le nombre de messages non lus</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Afficher le nombre de messages non lus dans la barre de notification</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Défiler les boutons de navigation</string>
|
||||
<string name="account_settings_hide_buttons_never">Jamais</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Lorsque le clavier est disponible</string>
|
||||
<string name="account_settings_hide_buttons_always">Toujours</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Activer les boutons de déplacement</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Afficher les boutons Archiver, Déplacer ou Spam</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Défiler les boutons de déplacement</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Afficher automatiquement les images</string>
|
||||
<string name="account_settings_show_pictures_never">Jamais</string>
|
||||
@ -589,13 +583,13 @@ Bienvenue dans la configuration de K-9 Mail. K-9 Mail est un client de messageri
|
||||
<string name="account_settings_reply_after_quote_label">Réponse après la citation</string>
|
||||
<string name="account_settings_reply_after_quote_summary">Faire précéder la réponse par le texte d\'origine</string>
|
||||
|
||||
<string name="account_settings_strip_signature_label">Retirer signature dans réponse citée</string>
|
||||
<string name="account_settings_strip_signature_label">Retirer signature dans réponse citée</string>
|
||||
<string name="account_settings_strip_signature_summary">Lorsque vous répondez à des messages, la signature du texte cité sera éffacée</string>
|
||||
|
||||
<string name="account_settings_message_format_label">Format du message</string>
|
||||
<string name="account_settings_message_format_html">HTML (formattage et images conservés)</string>
|
||||
<string name="account_settings_message_format_text">Text brut (formattage et images omis)</string>
|
||||
<string name="account_settings_message_format_auto">Automatique (Text brut à moins de répondre à un message HTML)</string>
|
||||
<string name="account_settings_message_format_auto">Automatique (Text brut à moins de répondre à un message HTML)</string>
|
||||
|
||||
<string name="account_settings_message_read_receipt_label">Accusé de réception</string>
|
||||
<string name="account_settings_message_read_receipt_summary">Toujours demander un accusé de réception</string>
|
||||
@ -617,9 +611,9 @@ Bienvenue dans la configuration de K-9 Mail. K-9 Mail est un client de messageri
|
||||
<string name="account_settings_crypto_app_not_available">Non disponible</string>
|
||||
<string name="account_settings_crypto_auto_signature">Signature automatique</string>
|
||||
<string name="account_settings_crypto_auto_signature_summary">Utiliser l\'adresse e-mail du compte pour déduire la clé de signature</string>
|
||||
<string name="account_settings_crypto_auto_encrypt">Cryptation automatique</string>
|
||||
<string name="account_settings_crypto_auto_encrypt_summary">Cryptation automatique si une clé publique correspond au destinataire.</string>
|
||||
|
||||
<string name="account_settings_crypto_auto_encrypt">Cryptation automatique</string>
|
||||
<string name="account_settings_crypto_auto_encrypt_summary">Cryptation automatique si une clé publique correspond au destinataire.</string>
|
||||
|
||||
<string name="account_settings_mail_check_frequency_label">Fréquence de vérification du dossier</string>
|
||||
<string name="account_settings_second_class_check_frequency_label">Fréquence de vérification pour 2ème classe</string>
|
||||
|
||||
@ -1045,12 +1039,12 @@ Bienvenue dans la configuration de K-9 Mail. K-9 Mail est un client de messageri
|
||||
<string name="save_or_discard_draft_message_dlg_title">Enregistrer le brouillon\u00A0?</string>
|
||||
<string name="save_or_discard_draft_message_instructions_fmt">Enregistrer ou abandonner ce message\u00A0?</string>
|
||||
|
||||
<string name="refuse_to_save_draft_marked_encrypted_dlg_title">Refuser d\'enregistrer des brouillons.</string>
|
||||
<string name="refuse_to_save_draft_marked_encrypted_instructions_fmt">Refuser d\'enregistrer un message marqué comme crypté.</string>
|
||||
|
||||
<string name="continue_without_public_key_dlg_title">Continuer sans clé publique?</string>
|
||||
<string name="continue_without_public_key_instructions_fmt">Un ou plusieurs destinataires n\'ont pas de clé publique enregistrée. Continuer?</string>
|
||||
|
||||
<string name="refuse_to_save_draft_marked_encrypted_dlg_title">Refuser d\'enregistrer des brouillons.</string>
|
||||
<string name="refuse_to_save_draft_marked_encrypted_instructions_fmt">Refuser d\'enregistrer un message marqué comme crypté.</string>
|
||||
|
||||
<string name="continue_without_public_key_dlg_title">Continuer sans clé publique?</string>
|
||||
<string name="continue_without_public_key_instructions_fmt">Un ou plusieurs destinataires n\'ont pas de clé publique enregistrée. Continuer?</string>
|
||||
|
||||
<string name="charset_not_found">Ce message ne peut être affiché parce que le jeu de caractères «\u00A0<xliff:g id="charset">%s</xliff:g>\u00A0» n\'a pu être trouvé.</string>
|
||||
|
||||
<string name="select_text_now">Sélectionnez le texte à copier</string>
|
||||
|
@ -541,14 +541,8 @@ Benvido á Configuración de K-9. K-9 é un cliente de correo OpenSource para An
|
||||
<string name="account_settings_notification_unread_count_label">Amosar número de mensaxes non lidos</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Amosar número de mensaxen non lidos na barra de notificacións.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Botóns de desprazamento</string>
|
||||
<string name="account_settings_hide_buttons_never">Nunca</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Cando o teclado esté dispoñible</string>
|
||||
<string name="account_settings_hide_buttons_always">Sempre</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Habilitar botóns de copia</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Amosar botóns de arquivar, mover e SPAM</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Botóns de copia</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Amosar imaxes</string>
|
||||
<string name="account_settings_show_pictures_never">Nunca</string>
|
||||
|
@ -154,7 +154,7 @@
|
||||
<string name="special_mailbox_name_spam_fmt">%s</string>
|
||||
<string name="send_failure_subject">Néhány üzenetet nem sikerült elküldeni</string>
|
||||
<string name="send_failure_body_abbrev">Nézze meg a %s mappát a részletekért.</string>
|
||||
<string name="send_failure_body_fmt">"A K-9 hibát észlelt üzenetküldés közben. A probléma miatt elképzelhető, hogy a levél nem érkezik meg, de az is lehet hogy igen.
|
||||
<string name="send_failure_body_fmt">"A K-9 hibát észlelt üzenetküldés közben. A probléma miatt elképzelhető, hogy a levél nem érkezik meg, de az is lehet hogy igen.
|
||||
|
||||
Az ilyen hibás leveleket csillag jelzi a postázandó mappában. Ha eltávolítja a csillagot a K-9 megpróbálja újból elküldeni a levelet. Hosszan a Postázatlan mappára kattintva válassza az Üzenetek küldését.
|
||||
|
||||
@ -162,26 +162,26 @@ Az ilyen hibás leveleket csillag jelzi a postázandó mappában. Ha eltávolít
|
||||
<string name="alert_header">K-9 riasztás</string>
|
||||
<string name="no_connection_alert">A szinkronizáció és a levélküldés felfüggesztve, hálozati kapcsolat hiánya miatt.</string>
|
||||
<string name="end_of_folder">Nincs több üzenet</string>
|
||||
<string name="accounts_welcome">"Üdvözöljük a K-9 Mail beállításokban. A K-9 egy nyílt forráskódú levelezőprogram Androidra, a sima mail kliens alapjaira helyezve.
|
||||
|
||||
K-9 továbbfejlesztett funkciói:
|
||||
* Push mail using IMAP IDLE
|
||||
* Jobb teljesítmény
|
||||
* Message refiling
|
||||
* E-mail aláírások
|
||||
* Titkos másolat
|
||||
* Mappa-előfizetések
|
||||
* Minden mappa szinkronizálása
|
||||
* Válasz cím beállítása
|
||||
* Gyorsbillentyűk
|
||||
* Jobb IMAP támogatás
|
||||
* Mellékletek mentése a memóriakártyára
|
||||
* Kuka ürítése
|
||||
* Üzenetek válogatás
|
||||
<string name="accounts_welcome">"Üdvözöljük a K-9 Mail beállításokban. A K-9 egy nyílt forráskódú levelezőprogram Androidra, a sima mail kliens alapjaira helyezve.
|
||||
|
||||
K-9 továbbfejlesztett funkciói:
|
||||
* Push mail using IMAP IDLE
|
||||
* Jobb teljesítmény
|
||||
* Message refiling
|
||||
* E-mail aláírások
|
||||
* Titkos másolat
|
||||
* Mappa-előfizetések
|
||||
* Minden mappa szinkronizálása
|
||||
* Válasz cím beállítása
|
||||
* Gyorsbillentyűk
|
||||
* Jobb IMAP támogatás
|
||||
* Mellékletek mentése a memóriakártyára
|
||||
* Kuka ürítése
|
||||
* Üzenetek válogatás
|
||||
* ...és még sok más
|
||||
|
||||
Magyarította: Deák Tamás (maya98) és RootRulez
|
||||
|
||||
Magyarította: Deák Tamás (maya98) és RootRulez
|
||||
|
||||
Vegye figyelembe, hogy a K-9 nem támogatja a legtöbb ingyenes hotmail fiókot és még sok más klienst. Történnek furcsaságok, ha Microsoft Exchange-el kommunikál.
|
||||
Hibajelentéseivel hozzájárul az újabb verziók tökéletesítéséhez, kérdéseit itt teheti fel http://k9mail.googlecode.com/"</string>
|
||||
<string name="debug_version_fmt">Verzió: %s</string>
|
||||
@ -218,7 +218,7 @@ Vegye figyelembe, hogy a K-9 nem támogatja a legtöbb ingyenes hotmail fiókot
|
||||
<string name="message_compose_quoted_text_label">Idézet</string>
|
||||
<string name="message_compose_error_no_recipients">Legalább egy címzetted adjon meg.</string>
|
||||
<string name="error_contact_address_not_found">E-mail cím nem található.</string>
|
||||
<string name="message_compose_downloading_attachments_toast">Néhány melléklet nem lett letöltve. Levélküldés előtt automatikusan letöltődnek.</string>
|
||||
<string name="message_compose_downloading_attachments_toast">Néhány melléklet nem lett letöltve. Levélküldés előtt automatikusan letöltődnek.</string>
|
||||
<string name="message_compose_attachments_skipped_toast">Néhány mellékletet nem lehet továbbítani, mert nem lettek letöltve.</string>
|
||||
<string name="message_view_from_format" formatted="false">Feladó: %s <%s></string>
|
||||
<string name="message_to_label">Címzett:</string>
|
||||
@ -278,7 +278,7 @@ Vegye figyelembe, hogy a K-9 nem támogatja a legtöbb ingyenes hotmail fiókot
|
||||
<string name="global_settings_confirm_action_delete">Törlés (csak üzenetek nézet)</string>
|
||||
<string name="global_settings_confirm_action_spam">Levélszemét</string>
|
||||
<string name="global_settings_confirm_action_mark_all_as_read">Összes megjelölése olvasottként</string>
|
||||
<string name="global_settings_confirm_action_send">Küldés</string>
|
||||
<string name="global_settings_confirm_action_send">Küldés</string>
|
||||
<string name="global_settings_privacy_mode_title">Képernyőzár értesítések</string>
|
||||
<string name="global_settings_privacy_mode_summary">Lezárt képernyőnél ne mutassa a levelek tárgyát a állapotsoron</string>
|
||||
<string name="quiet_time">Csendes mód</string>
|
||||
@ -450,13 +450,8 @@ Levelek letöltése…"</string>
|
||||
<string name="account_settings_notification_opens_unread_summary">Értesítésre kattintva megnyitja az olvasatlan üzeneteket</string>
|
||||
<string name="account_settings_notification_unread_count_label">Olvasatlanok kijelzése</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Olvasatlan levelek száma az állapotsoron.</string>
|
||||
<string name="account_settings_hide_buttons_label">Görgetés iránygombokkal</string>
|
||||
<string name="account_settings_hide_buttons_never">Soha</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Ha van billentyűzet</string>
|
||||
<string name="account_settings_hide_buttons_always">Mindig</string>
|
||||
<string name="account_settings_enable_move_buttons_label">Művelet gombok megjelenítése</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Mutassa a Mozgatás, Archív és Levélszemét gombokat</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Művelet gombok görgetése</string>
|
||||
<string name="account_settings_show_pictures_label">Képek megjelenítése</string>
|
||||
<string name="account_settings_show_pictures_never">Soha</string>
|
||||
<string name="account_settings_show_pictures_only_from_contacts">Csak az ismerősökét</string>
|
||||
@ -611,7 +606,7 @@ Levelek letöltése…"</string>
|
||||
<string name="account_settings_signature_use_label">Aláírás használata</string>
|
||||
<string name="account_settings_signature_label">Aláírás</string>
|
||||
<string name="account_settings_signature_summary">Az aláírás hozzáfűzése minden elküldött levélhez</string>
|
||||
<string name="default_signature">"--
|
||||
<string name="default_signature">"--
|
||||
Ezt a levelet mobiltelefonról küldtem, K-9 Mail-el. Elnézést a levél rövidségéért és az esetleges hibákért."</string>
|
||||
<string name="default_identity_description">Elsődleges személyazonosságom</string>
|
||||
<string name="choose_identity">Személyazonosság választása</string>
|
||||
@ -654,7 +649,7 @@ Ezt a levelet mobiltelefonról küldtem, K-9 Mail-el. Elnézést a levél rövid
|
||||
<string name="provider_note_hanmail">Ha POP3-at vagy IMAP-ot szeretne használni ehhez a szolgáltatóhoz, akkor engedélyeznie kell az IMAP vagy POP3 beállításokat a Hanmail(Daum) oldalán.</string>
|
||||
<string name="provider_note_paran">Ha POP3-at vagy IMAP-ot szeretne használni ehhez a szolgáltatóhoz, akkor engedélyeznie kell az IMAP vagy POP3 beállításokat a Paran oldalán.</string>
|
||||
<string name="provider_note_nate">Ha POP3-at vagy IMAP-ot szeretne használni ehhez a szolgáltatóhoz, akkor engedélyeznie kell az IMAP vagy POP3 beállításokat a Nate oldalán.</string>
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_title">Felismerhetetlen tanúsítvány</string>
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_title">Felismerhetetlen tanúsítvány</string>
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_accept">Kulcs elfogadva</string>
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_reject">Kulcs elutasítva</string>
|
||||
<string name="message_help_key">"Del (or D) - Delete
|
||||
@ -752,7 +747,7 @@ S - Select/deselect"</string>
|
||||
<string name="gestures_summary">Engedélyezi a kézmozdulakkal való vezérlést.</string>
|
||||
<string name="compact_layouts_title">Kompakt elrendezés</string>
|
||||
<string name="compact_layouts_summary">Adjust layouts to display more on each page</string>
|
||||
<string name="volume_navigation_title">Hangerő gomb vezérlés</string>
|
||||
<string name="volume_navigation_title">Hangerő gomb vezérlés</string>
|
||||
<string name="volume_navigation_summary">Léptetés az hangerő gombokkal</string>
|
||||
<string name="volume_navigation_message">Leveleknél</string>
|
||||
<string name="volume_navigation_list">Lista nézetek váltása</string>
|
||||
@ -766,7 +761,7 @@ S - Select/deselect"</string>
|
||||
<string name="count_search_summary">Kikcsapcsolva gyorsabb műkodés</string>
|
||||
<string name="hide_special_accounts_title">Különleges fiókok elrejtése</string>
|
||||
<string name="hide_special_accounts_summary">Egységesen elrejti a fiókok bejövő mappáit</string>
|
||||
<string name="search_title" formatted="false">%s %s</string>
|
||||
<string name="search_title" formatted="false">%s %s</string>
|
||||
<string name="flagged_modifier">- Csillagos</string>
|
||||
<string name="unread_modifier">- Olvasatlan</string>
|
||||
<string name="search_all_messages_title">Minden levél</string>
|
||||
@ -846,11 +841,11 @@ S - Select/deselect"</string>
|
||||
<string name="dialog_confirm_spam_confirm_button">Igen</string>
|
||||
<string name="dialog_confirm_spam_cancel_button">Nem</string>
|
||||
<string name="dialog_attachment_progress_title">Csatolmányok letöltése</string>
|
||||
<string name="debug_logging_enabled">Hibakereső naplózás bekapcsolva</string>
|
||||
<string name="debug_logging_enabled">Hibakereső naplózás bekapcsolva</string>
|
||||
<string name="messagelist_sent_to_me_sigil">»</string>
|
||||
<string name="messagelist_sent_cc_me_sigil">›</string>
|
||||
<string name="error_unable_to_connect">Nem lehet kapcsolódni.</string>
|
||||
<string name="account_unavailable"> \"%s\" fiók nem elérhető ellenőríze a tárhelyet</string>
|
||||
<string name="account_unavailable"> \"%s\" fiók nem elérhető ellenőríze a tárhelyet</string>
|
||||
<string name="settings_attachment_default_path">Csatolményok mentése ide:</string>
|
||||
<string name="attachment_save_title">Csatolményok mentése</string>
|
||||
<string name="attachment_save_desc">Nincs fájlkezelő. Hova szeretné mentni a csatolmányt?</string>
|
||||
|
@ -543,14 +543,8 @@ Benvenuto nella configurazione della posta di K-9. K-9 è un client di posta ope
|
||||
<string name="account_settings_notification_unread_count_label">Vedi numero messaggi non letti</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Mostra il numero dei messaggi non letti nella barra di notifica.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Scorri pulsanti navigazione</string>
|
||||
<string name="account_settings_hide_buttons_never">Mai</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Quando la tastiera è disponibile</string>
|
||||
<string name="account_settings_hide_buttons_always">Sempre</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Abilita pulsanti archiviazione</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Mostra i pulsanti Archivia, Sposta e Spam.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Scorri pulsanti archiviazione</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Mostra sempre le immagini</string>
|
||||
<string name="account_settings_show_pictures_never">No</string>
|
||||
|
@ -71,6 +71,7 @@
|
||||
<string name="send_messages_action">メール送信</string>
|
||||
<string name="list_folders_action">フォルダ一覧</string>
|
||||
<string name="refresh_folders_action">フォルダ再読込</string>
|
||||
<string name="filter_folders_action">フォルダを探す</string>
|
||||
<string name="mark_all_as_read_action">すべてのメールを既読にする</string>
|
||||
<string name="add_account_action">アカウント追加</string>
|
||||
<string name="compose_action">作成</string>
|
||||
@ -544,14 +545,8 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
|
||||
<string name="account_settings_notification_unread_count_label">未読件数の表示</string>
|
||||
<string name="account_settings_notification_unread_count_summary">通知バーに未読メッセージの件数を表示する</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">ナビゲーションボタンのスクロール</string>
|
||||
<string name="account_settings_hide_buttons_never">しない</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">キーボード利用時</string>
|
||||
<string name="account_settings_hide_buttons_always">常に</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">メール整理ボタンを有効にする</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">アーカイブ、移動、迷惑メールボタンを表示</string>
|
||||
<string name="account_settings_hide_move_buttons_label">メール整理ボタンをスクロール</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">画像を自動で表示</string>
|
||||
<string name="account_settings_show_pictures_never">しない</string>
|
||||
@ -822,6 +817,8 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
|
||||
Q - アカウント設定に戻る\u000A
|
||||
S - アカウント設定編集</string>
|
||||
|
||||
<string name="folder_list_filter_hint">フォルダ名に含まれる文字</string>
|
||||
|
||||
<string name="folder_list_display_mode_label">フォルダ表示</string>
|
||||
<string name="folder_list_display_mode_all">全フォルダ表示</string>
|
||||
<string name="folder_list_display_mode_first_class">1st クラスフォルダ表示</string>
|
||||
@ -973,6 +970,9 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
|
||||
<string name="font_size_message_view_date">日付</string>
|
||||
<string name="font_size_message_view_content">本文</string>
|
||||
|
||||
<string name="font_size_message_compose">メッセージ作成</string>
|
||||
<string name="font_size_message_compose_input">入力テキスト</string>
|
||||
|
||||
<string name="font_size_tiniest">極小</string>
|
||||
<string name="font_size_tiny">かなり小</string>
|
||||
<string name="font_size_smaller">やや小</string>
|
||||
@ -1014,6 +1014,9 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
|
||||
<string name="save_or_discard_draft_message_dlg_title">メッセージの下書き保存</string>
|
||||
<string name="save_or_discard_draft_message_instructions_fmt">メッセージを保存しますか?</string>
|
||||
|
||||
<string name="confirm_discard_draft_message_title">メッセージ破棄?</string>
|
||||
<string name="confirm_discard_draft_message">このメッセージを破棄しますか?</string>
|
||||
|
||||
<string name="refuse_to_save_draft_marked_encrypted_dlg_title">下書き保存の拒否</string>
|
||||
<string name="refuse_to_save_draft_marked_encrypted_instructions_fmt">暗号化したメッセージは下書き保存できません</string>
|
||||
|
||||
@ -1102,4 +1105,11 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
|
||||
<string name="manage_accounts_move_down_action">下に移動</string>
|
||||
<string name="manage_accounts_moving_message">アカウントを移動しています</string>
|
||||
|
||||
<string name="unread_widget_label">未読件数</string>
|
||||
<string name="unread_widget_select_account">アカウントの未読件数を表示</string>
|
||||
|
||||
<string name="import_dialog_error_title">ファイルマネージャがありません</string>
|
||||
<string name="import_dialog_error_message">設定をインポートするためのアプリケーションがありません。Androidマーケットからファイルマネージャをインストールしてください。</string>
|
||||
<string name="open_market">マーケット</string>
|
||||
<string name="close">閉じる</string>
|
||||
</resources>
|
||||
|
@ -542,14 +542,8 @@ K-9 메일 설치를 환영합니다. K-9은 표준 안드로이드 메일 클
|
||||
<string name="account_settings_notification_unread_count_label">읽지 않은 메일 수 세기</string>
|
||||
<string name="account_settings_notification_unread_count_summary">읽지 않은 메시지의 수를 상태바에 보여줍니다.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">네비게이션 버튼을 스크롤</string>
|
||||
<string name="account_settings_hide_buttons_never">하지않음</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">키보드를 이용가능할 경우</string>
|
||||
<string name="account_settings_hide_buttons_always">항상</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">재정리(refile) 버튼 활성화</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">보관, 이동, 스팸 버튼을 보이기</string>
|
||||
<string name="account_settings_hide_move_buttons_label">재정리(refile) 보튼을 스크롤</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">항상 그림 보기</string>
|
||||
<string name="account_settings_show_pictures_never">아니오</string>
|
||||
|
@ -541,14 +541,8 @@ Welkom bij K-9 Mail setup. K-9 is een open source mail cliënt voor Android, ge
|
||||
<string name="account_settings_notification_unread_count_label">Toon aantal ongelezen</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Toon het aantal ongelezen berichten in de \'notification bar\'.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Scroll navigatie knoppen</string>
|
||||
<string name="account_settings_hide_buttons_never">Nooit</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Wanneer toetsenbord beschikbaar is</string>
|
||||
<string name="account_settings_hide_buttons_always">Altijd</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Inschakelen verplaats knoppen</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Laat de Archief, Verplaats, en Spam knoppen zien.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Scroll verplaats knoppen</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Laat afbeeldingen automatisch zien</string>
|
||||
<string name="account_settings_show_pictures_never">Nooit</string>
|
||||
|
@ -553,14 +553,8 @@ Witaj w K-9 Mail, darmowym programie pocztowym dla systemu Android. Najistotniej
|
||||
<string name="account_settings_notification_unread_count_label">Pokaż liczbę nieprzeczytanych</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Pokaż liczbę nieprzeczytanych wiadomości w pasku powiadomień.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Przyciski nawigacyjne</string>
|
||||
<string name="account_settings_hide_buttons_never">Przyciski nie są nigdy przesuwalne</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Przesuwalne, gdy jest klawiatura</string>
|
||||
<string name="account_settings_hide_buttons_always">Przyciski są zawsze przesuwalne</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Użyj przycisków refile </string> <!-- FIXME -->
|
||||
<string name="account_settings_enable_move_buttons_summary">Pokaż przyciski Archiwum, Przenieś, Spam.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Przewijaj przyciski refile </string> <!-- FIXME -->
|
||||
|
||||
<string name="account_settings_show_pictures_label">Zawsze pokazuj obrazki</string>
|
||||
<string name="account_settings_show_pictures_never">Nie</string>
|
||||
|
@ -539,14 +539,8 @@ Bem-vindo à configuração do K-9 Mail. K-9 é um cliente de e-mail com código
|
||||
<string name="account_settings_notification_unread_count_label">Mostrar contagem de não lidas</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Mostrar o número de mensagens não lidas na barra de notificação.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Navegação com botões de scroll</string>
|
||||
<string name="account_settings_hide_buttons_never">Nunca</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Quando teclado estiver disponível</string>
|
||||
<string name="account_settings_hide_buttons_always">Sempre</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Habilitar botão de ações</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Mostrar botões de Arquivar, Mover e Span.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Scroll para botões de ação </string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Sempre mostrar imagens</string>
|
||||
<string name="account_settings_show_pictures_never">Não</string>
|
||||
|
@ -533,14 +533,8 @@
|
||||
<string name="account_settings_notification_unread_count_label">Показать количество непрочитанных</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Показать количество непрочитанных в строке уведомлений.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Прокрутка навигационных кнопок</string>
|
||||
<string name="account_settings_hide_buttons_never">Никогда</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Когда присутствует клавиатура</string>
|
||||
<string name="account_settings_hide_buttons_always">Всегда</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Разрешить кнопки переноса сообщений</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Показывает кнопки: Архив, Переместить, Спам.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Спрятать кнопки перемещения</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Показывать изображения</string>
|
||||
<string name="account_settings_show_pictures_never">Никогда</string>
|
||||
|
@ -543,14 +543,8 @@ Välkommen till installationen av K-9 E-post. K-9 är en e-postklient med öppen
|
||||
<string name="account_settings_notification_unread_count_label">Visa antal olästa</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Visar antalet olästa brev i notifieringsytan.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Scrolla navigationsknappar</string>
|
||||
<string name="account_settings_hide_buttons_never">Aldrig</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">När ett tangentbord är tillgängligt</string>
|
||||
<string name="account_settings_hide_buttons_always">Alltid</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Aktivera flyttknappar</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Visa knappar för att arkivera, flytta och spam-markera e-post.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Scrolla flyttknappar</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Visa bilder automatiskt</string>
|
||||
<string name="account_settings_show_pictures_never">Aldrig</string>
|
||||
|
@ -516,14 +516,8 @@
|
||||
<string name="account_settings_notification_unread_count_label">Okunmamış sayısını göster</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Bildirim çubuğunda okunmamış mesaj numarasını göster.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Gezinme tuşlarını kaydırma</string>
|
||||
<string name="account_settings_hide_buttons_never">Asla</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">Klavye olduğu zaman</string>
|
||||
<string name="account_settings_hide_buttons_always">Daima</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">İşaretleme düğmelerini etkinleştir</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Arşiv, Taşıma ve Spam düğmelerini göster.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">İşaretleme düğmelerini kaydır</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Daima imajları göster</string>
|
||||
<string name="account_settings_show_pictures_never">Hayır</string>
|
||||
|
@ -530,14 +530,8 @@
|
||||
<!-- NEW: <string name="account_settings_notification_unread_count_label">Show unread count</string>-->
|
||||
<!-- NEW: <string name="account_settings_notification_unread_count_summary">Show the number of unread messages in the notification bar.</string>-->
|
||||
|
||||
<string name="account_settings_hide_buttons_label">滚动导航按钮</string>
|
||||
<string name="account_settings_hide_buttons_never">从不</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">使用键盘时</string>
|
||||
<string name="account_settings_hide_buttons_always">总是</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">启用整理按钮</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">显示归档、移动和标记为垃圾按钮</string>
|
||||
<string name="account_settings_hide_move_buttons_label">滚动整理按钮</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">显示图片</string>
|
||||
<string name="account_settings_show_pictures_never">从不</string>
|
||||
|
@ -539,14 +539,8 @@
|
||||
<string name="account_settings_notification_unread_count_label">顯示未讀郵件數</string>
|
||||
<string name="account_settings_notification_unread_count_summary">在通知欄上顯示未讀郵件數</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">滾動導航按鈕</string>
|
||||
<string name="account_settings_hide_buttons_never">從不</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">使用鍵盤時</string>
|
||||
<string name="account_settings_hide_buttons_always">總是</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">啟用整理按鈕</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">顯示歸檔、移動和標記為垃圾按鈕</string>
|
||||
<string name="account_settings_hide_move_buttons_label">滾動整理按鈕</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">顯示圖片</string>
|
||||
<string name="account_settings_show_pictures_never">從不</string>
|
||||
@ -799,7 +793,7 @@
|
||||
<string name="provider_note_hanmail">要使用此提供者的IMAP或POP3,請先至Hanmail(Daum)郵箱設置頁設定允許使用IMAP或POP3。</string>
|
||||
<string name="provider_note_paran">要使用此提供者的IMAP或POP3,請先至Paran郵箱設置頁設定允許使用IMAP或POP3。</string>
|
||||
<string name="provider_note_nate">要使用此提供者的IMAP或POP3,請先至Nate郵箱設置頁設定允許使用IMAP或POP3。</string>
|
||||
|
||||
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_title">無法識別的證書訊息</string>
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_accept">接收密鑰</string>
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_reject">拒絕密鑰</string>
|
||||
@ -1060,5 +1054,5 @@
|
||||
|
||||
<string name="manage_accounts_move_up_action">上移</string>
|
||||
<string name="manage_accounts_move_down_action">下移</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
@ -149,30 +149,6 @@
|
||||
<item>NOT_SECOND_CLASS</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="account_settings_hide_buttons_entries">
|
||||
<item>@string/account_settings_hide_buttons_never</item>
|
||||
<item>@string/account_settings_hide_buttons_keyboard_avail</item>
|
||||
<item>@string/account_settings_hide_buttons_always</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="account_settings_hide_buttons_values">
|
||||
<item>NEVER</item>
|
||||
<item>KEYBOARD_AVAILABLE</item>
|
||||
<item>ALWAYS</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="account_settings_hide_move_buttons_entries">
|
||||
<item>@string/account_settings_hide_buttons_never</item>
|
||||
<item>@string/account_settings_hide_buttons_keyboard_avail</item>
|
||||
<item>@string/account_settings_hide_buttons_always</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="account_settings_hide_move_buttons_values">
|
||||
<item>NEVER</item>
|
||||
<item>KEYBOARD_AVAILABLE</item>
|
||||
<item>ALWAYS</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="account_settings_show_pictures_entries">
|
||||
<item>@string/account_settings_show_pictures_never</item>
|
||||
<item>@string/account_settings_show_pictures_only_from_contacts</item>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="message_list_item_background">#ffffff</color>
|
||||
<color name="message_list_item_background">#ffffff</color>
|
||||
<color name="message_list_item_footer_background">#eeeeee</color>
|
||||
<color name="message_view_header_background">#1a080808</color>
|
||||
</resources>
|
||||
|
@ -292,6 +292,9 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
||||
<string name="message_view_status_attachment_not_saved">Unable to save attachment to SD card.</string>
|
||||
<string name="message_view_show_pictures_instructions">Select \"Show pictures\" to display embedded pictures.</string>
|
||||
<string name="message_view_show_pictures_action">Show pictures</string>
|
||||
<string name="message_view_show_message_action">Show message</string>
|
||||
<string name="message_view_show_attachments_action">Show attachments</string>
|
||||
<string name="message_view_show_more_attachments_action">More…</string>
|
||||
<string name="message_view_fetching_attachment_toast">Fetching attachment.</string>
|
||||
<string name="message_view_no_viewer">Unable to find viewer for <xliff:g id="mimetype">%s</xliff:g>.</string>
|
||||
|
||||
@ -552,14 +555,8 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
||||
<string name="account_settings_notification_unread_count_label">Show unread count</string>
|
||||
<string name="account_settings_notification_unread_count_summary">Show the number of unread messages in the notification bar.</string>
|
||||
|
||||
<string name="account_settings_hide_buttons_label">Scroll navigation buttons</string>
|
||||
<string name="account_settings_hide_buttons_never">Never</string>
|
||||
<string name="account_settings_hide_buttons_keyboard_avail">When keyboard is available</string>
|
||||
<string name="account_settings_hide_buttons_always">Always</string>
|
||||
|
||||
<string name="account_settings_enable_move_buttons_label">Enable refile buttons</string>
|
||||
<string name="account_settings_enable_move_buttons_summary">Show the Archive, Move, and Spam buttons.</string>
|
||||
<string name="account_settings_hide_move_buttons_label">Scroll refile buttons</string>
|
||||
|
||||
<string name="account_settings_show_pictures_label">Always show images</string>
|
||||
<string name="account_settings_show_pictures_never">No</string>
|
||||
@ -1036,6 +1033,9 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
||||
<string name="save_or_discard_draft_message_dlg_title">Save draft message?</string>
|
||||
<string name="save_or_discard_draft_message_instructions_fmt">Save or Discard this message?</string>
|
||||
|
||||
<string name="confirm_discard_draft_message_title">Discard message?</string>
|
||||
<string name="confirm_discard_draft_message">Are you sure you want to discard this message?</string>
|
||||
|
||||
<string name="refuse_to_save_draft_marked_encrypted_dlg_title">Refuse to save draft message.</string>
|
||||
<string name="refuse_to_save_draft_marked_encrypted_instructions_fmt">Refuse to save draft message marked encrypted.</string>
|
||||
|
||||
@ -1129,4 +1129,12 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
||||
<string name="manage_accounts_move_down_action">Move down</string>
|
||||
<string name="manage_accounts_moving_message">Moving account...</string>
|
||||
|
||||
<string name="unread_widget_label">K-9 Unread</string>
|
||||
<string name="unread_widget_select_account">Show unread count for…</string>
|
||||
|
||||
<string name="import_dialog_error_title">Missing File Manager Application</string>
|
||||
<string name="import_dialog_error_message">There is no suitable application to handle
|
||||
the import operation. Please install a file manager application from Android Market</string>
|
||||
<string name="open_market">Open Market</string>
|
||||
<string name="close">Close</string>
|
||||
</resources>
|
||||
|
@ -70,14 +70,6 @@
|
||||
android:entryValues="@array/account_settings_show_pictures_values"
|
||||
android:dialogTitle="@string/account_settings_show_pictures_label" />
|
||||
|
||||
<ListPreference
|
||||
android:persistent="false"
|
||||
android:key="hide_buttons_enum"
|
||||
android:title="@string/account_settings_hide_buttons_label"
|
||||
android:entries="@array/account_settings_hide_buttons_entries"
|
||||
android:entryValues="@array/account_settings_hide_buttons_values"
|
||||
android:dialogTitle="@string/account_settings_hide_buttons_label" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:persistent="false"
|
||||
android:key="enable_move_buttons"
|
||||
@ -85,15 +77,6 @@
|
||||
android:defaultValue="true"
|
||||
android:summary="@string/account_settings_enable_move_buttons_summary" />
|
||||
|
||||
<ListPreference
|
||||
android:persistent="false"
|
||||
android:key="hide_move_buttons_enum"
|
||||
android:dependency="enable_move_buttons"
|
||||
android:title="@string/account_settings_hide_move_buttons_label"
|
||||
android:entries="@array/account_settings_hide_move_buttons_entries"
|
||||
android:entryValues="@array/account_settings_hide_move_buttons_values"
|
||||
android:dialogTitle="@string/account_settings_hide_move_buttons_label" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@ -233,10 +216,10 @@
|
||||
android:entryValues="@array/account_settings_message_format_values" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:persistent="false"
|
||||
android:key="message_read_receipt"
|
||||
android:title="@string/account_settings_message_read_receipt_label"
|
||||
android:summary="@string/account_settings_message_read_receipt_summary" />
|
||||
android:persistent="false"
|
||||
android:key="message_read_receipt"
|
||||
android:title="@string/account_settings_message_read_receipt_label"
|
||||
android:summary="@string/account_settings_message_read_receipt_summary" />
|
||||
|
||||
<ListPreference
|
||||
android:persistent="false"
|
||||
|
@ -170,6 +170,14 @@
|
||||
<incoming uri="pop3://incoming.verizon.net" username="$user" />
|
||||
<outgoing uri="smtp://outgoing.verizon.net" username="$user" />
|
||||
</provider>
|
||||
<provider id="montclair.edu" label="MSU" domain="montclair.edu">
|
||||
<incoming uri="imap+ssl+://mail.montclair.edu" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.montclair.edu" username="$user" />
|
||||
</provider>
|
||||
<provider id="gmx.com" label="GMX" domain="gmx.com">
|
||||
<incoming uri="imap+ssl+://imap.gmx.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://mail.gmx.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Yahoo! Mail Variants -->
|
||||
<provider id="yahoo" label="Yahoo" domain="yahoo.com">
|
||||
|
8
res/xml/unread_widget_info.xml
Normal file
8
res/xml/unread_widget_info.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/unread_widget_layout"
|
||||
android:minHeight="72dp"
|
||||
android:minWidth="72dp"
|
||||
android:configure="com.fsck.k9.activity.UnreadWidgetConfiguration"
|
||||
android:updatePeriodMillis="0">
|
||||
</appwidget-provider>
|
@ -120,8 +120,6 @@ public class Account implements BaseAccount {
|
||||
private boolean mSaveAllHeaders;
|
||||
private boolean mPushPollOnConnect;
|
||||
private boolean mNotifySync;
|
||||
private ScrollButtons mScrollMessageViewButtons;
|
||||
private ScrollButtons mScrollMessageViewMoveButtons;
|
||||
private ShowPictures mShowPictures;
|
||||
private boolean mEnableMoveButtons;
|
||||
private boolean mIsSignatureBeforeQuotedText;
|
||||
@ -182,10 +180,6 @@ public class Account implements BaseAccount {
|
||||
NONE, ALL, FIRST_CLASS, FIRST_AND_SECOND_CLASS, NOT_SECOND_CLASS
|
||||
}
|
||||
|
||||
public enum ScrollButtons {
|
||||
NEVER, ALWAYS, KEYBOARD_AVAILABLE
|
||||
}
|
||||
|
||||
public enum ShowPictures {
|
||||
NEVER, ALWAYS, ONLY_FROM_CONTACTS
|
||||
}
|
||||
@ -218,8 +212,6 @@ public class Account implements BaseAccount {
|
||||
mFolderSyncMode = FolderMode.FIRST_CLASS;
|
||||
mFolderPushMode = FolderMode.FIRST_CLASS;
|
||||
mFolderTargetMode = FolderMode.NOT_SECOND_CLASS;
|
||||
mScrollMessageViewButtons = ScrollButtons.NEVER;
|
||||
mScrollMessageViewMoveButtons = ScrollButtons.NEVER;
|
||||
mShowPictures = ShowPictures.NEVER;
|
||||
mEnableMoveButtons = false;
|
||||
mIsSignatureBeforeQuotedText = false;
|
||||
@ -341,20 +333,6 @@ public class Account implements BaseAccount {
|
||||
(random.nextInt(0x70) * 0xffff) +
|
||||
0xff000000);
|
||||
|
||||
try {
|
||||
mScrollMessageViewButtons = ScrollButtons.valueOf(prefs.getString(mUuid + ".hideButtonsEnum",
|
||||
ScrollButtons.NEVER.name()));
|
||||
} catch (Exception e) {
|
||||
mScrollMessageViewButtons = ScrollButtons.NEVER;
|
||||
}
|
||||
|
||||
try {
|
||||
mScrollMessageViewMoveButtons = ScrollButtons.valueOf(prefs.getString(mUuid + ".hideMoveButtonsEnum",
|
||||
ScrollButtons.NEVER.name()));
|
||||
} catch (Exception e) {
|
||||
mScrollMessageViewMoveButtons = ScrollButtons.NEVER;
|
||||
}
|
||||
|
||||
try {
|
||||
mShowPictures = ShowPictures.valueOf(prefs.getString(mUuid + ".showPicturesEnum",
|
||||
ShowPictures.NEVER.name()));
|
||||
@ -617,8 +595,6 @@ public class Account implements BaseAccount {
|
||||
editor.putString(mUuid + ".spamFolderName", mSpamFolderName);
|
||||
editor.putString(mUuid + ".autoExpandFolderName", mAutoExpandFolderName);
|
||||
editor.putInt(mUuid + ".accountNumber", mAccountNumber);
|
||||
editor.putString(mUuid + ".hideButtonsEnum", mScrollMessageViewButtons.name());
|
||||
editor.putString(mUuid + ".hideMoveButtonsEnum", mScrollMessageViewMoveButtons.name());
|
||||
editor.putString(mUuid + ".showPicturesEnum", mShowPictures.name());
|
||||
editor.putBoolean(mUuid + ".enableMoveButtons", mEnableMoveButtons);
|
||||
editor.putString(mUuid + ".folderDisplayMode", mFolderDisplayMode.name());
|
||||
@ -1055,22 +1031,6 @@ public class Account implements BaseAccount {
|
||||
this.mNotifySync = showOngoing;
|
||||
}
|
||||
|
||||
public synchronized ScrollButtons getScrollMessageViewButtons() {
|
||||
return mScrollMessageViewButtons;
|
||||
}
|
||||
|
||||
public synchronized void setScrollMessageViewButtons(ScrollButtons scrollMessageViewButtons) {
|
||||
mScrollMessageViewButtons = scrollMessageViewButtons;
|
||||
}
|
||||
|
||||
public synchronized ScrollButtons getScrollMessageViewMoveButtons() {
|
||||
return mScrollMessageViewMoveButtons;
|
||||
}
|
||||
|
||||
public synchronized void setScrollMessageViewMoveButtons(ScrollButtons scrollMessageViewButtons) {
|
||||
mScrollMessageViewMoveButtons = scrollMessageViewButtons;
|
||||
}
|
||||
|
||||
public synchronized ShowPictures getShowPictures() {
|
||||
return mShowPictures;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class EmailAddressAdapter extends ResourceCursorAdapter {
|
||||
final String name = mContacts.getName(cursor);
|
||||
final String address = mContacts.getEmail(cursor);
|
||||
|
||||
return new Address(address, name).toString();
|
||||
return (address == null) ? "" : new Address(address, name).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,6 +34,7 @@ import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||
import com.fsck.k9.provider.UnreadWidgetProvider;
|
||||
import com.fsck.k9.service.BootReceiver;
|
||||
import com.fsck.k9.service.MailService;
|
||||
import com.fsck.k9.service.ShutdownReceiver;
|
||||
@ -478,19 +479,38 @@ public class K9 extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUnreadWidget() {
|
||||
try {
|
||||
UnreadWidgetProvider.updateUnreadCount(K9.this);
|
||||
} catch (Exception e) {
|
||||
if (K9.DEBUG) {
|
||||
Log.e(LOG_TAG, "Error while updating unread widget(s)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
|
||||
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_DELETED, account, folder, message);
|
||||
updateUnreadWidget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageDeleted(Account account, String folder, Message message) {
|
||||
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_DELETED, account, folder, message);
|
||||
updateUnreadWidget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void synchronizeMailboxNewMessage(Account account, String folder, Message message) {
|
||||
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_RECEIVED, account, folder, message);
|
||||
updateUnreadWidget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void folderStatusChanged(Account account, String folderName,
|
||||
int unreadMessageCount) {
|
||||
updateUnreadWidget();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -12,6 +12,39 @@ import com.fsck.k9.mail.Flag;
|
||||
* is defined by {@link com.fsck.k9.activity.SearchModifier}.
|
||||
*/
|
||||
public class SearchAccount implements BaseAccount, SearchSpecification, Serializable {
|
||||
/**
|
||||
* Create a {@code SearchAccount} instance for the Unified Inbox.
|
||||
*
|
||||
* @param context
|
||||
* A {@link Context} instance that will be used to get localized strings and will be
|
||||
* passed on to the {@code SearchAccount} instance.
|
||||
*
|
||||
* @return The {@link SearchAccount} instance for the Unified Inbox.
|
||||
*/
|
||||
public static SearchAccount createUnifiedInboxAccount(Context context) {
|
||||
SearchAccount unifiedInbox = new SearchAccount(context, true, null, null);
|
||||
unifiedInbox.setDescription(context.getString(R.string.integrated_inbox_title));
|
||||
unifiedInbox.setEmail(context.getString(R.string.integrated_inbox_detail));
|
||||
return unifiedInbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code SearchAccount} instance for the special account "All messages".
|
||||
*
|
||||
* @param context
|
||||
* A {@link Context} instance that will be used to get localized strings and will be
|
||||
* passed on to the {@code SearchAccount} instance.
|
||||
*
|
||||
* @return The {@link SearchAccount} instance for the Unified Inbox.
|
||||
*/
|
||||
public static SearchAccount createAllMessagesAccount(Context context) {
|
||||
SearchAccount allMessages = new SearchAccount(context, false, null, null);
|
||||
allMessages.setDescription(context.getString(R.string.search_all_messages_title));
|
||||
allMessages.setEmail(context.getString(R.string.search_all_messages_detail));
|
||||
return allMessages;
|
||||
}
|
||||
|
||||
|
||||
private static final long serialVersionUID = -4388420303235543976L;
|
||||
private Flag[] mRequiredFlags = null;
|
||||
private Flag[] mForbiddenFlags = null;
|
||||
|
187
src/com/fsck/k9/activity/AccountList.java
Normal file
187
src/com/fsck/k9/activity/AccountList.java
Normal file
@ -0,0 +1,187 @@
|
||||
package com.fsck.k9.activity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.BaseAccount;
|
||||
import com.fsck.k9.FontSizes;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.SearchAccount;
|
||||
|
||||
|
||||
/**
|
||||
* Activity displaying the list of accounts.
|
||||
*
|
||||
* <p>
|
||||
* Classes extending this abstract class have to provide an {@link #onAccountSelected(BaseAccount)}
|
||||
* method to perform an action when an account is selected.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class AccountList extends K9ListActivity implements OnItemClickListener {
|
||||
private FontSizes mFontSizes = K9.getFontSizes();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
setContentView(R.layout.account_list);
|
||||
|
||||
ListView listView = getListView();
|
||||
listView.setOnItemClickListener(this);
|
||||
listView.setItemsCanFocus(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload list of accounts when this activity is resumed.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
new LoadAccounts().execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
BaseAccount account = (BaseAccount) parent.getItemAtPosition(position);
|
||||
onAccountSelected(account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AccountsAdapter} instance and assign it to the {@link ListView}.
|
||||
*
|
||||
* @param realAccounts
|
||||
* An array of accounts to display.
|
||||
*/
|
||||
public void populateListView(Account[] realAccounts) {
|
||||
List<BaseAccount> accounts = new ArrayList<BaseAccount>();
|
||||
|
||||
if (displaySpecialAccounts() && !K9.isHideSpecialAccounts()) {
|
||||
BaseAccount unifiedInboxAccount = SearchAccount.createUnifiedInboxAccount(this);
|
||||
BaseAccount allMessagesAccount = SearchAccount.createAllMessagesAccount(this);
|
||||
|
||||
accounts.add(unifiedInboxAccount);
|
||||
accounts.add(allMessagesAccount);
|
||||
}
|
||||
|
||||
accounts.addAll(Arrays.asList(realAccounts));
|
||||
AccountsAdapter adapter = new AccountsAdapter(accounts);
|
||||
ListView listView = getListView();
|
||||
listView.setAdapter(adapter);
|
||||
listView.invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementing decide whether or not to display special accounts in the list.
|
||||
*
|
||||
* @return {@code true}, if special accounts should be listed. {@code false}, otherwise.
|
||||
*/
|
||||
protected abstract boolean displaySpecialAccounts();
|
||||
|
||||
/**
|
||||
* This method will be called when an account was selected.
|
||||
*
|
||||
* @param account
|
||||
* The account the user selected.
|
||||
*/
|
||||
protected abstract void onAccountSelected(BaseAccount account);
|
||||
|
||||
class AccountsAdapter extends ArrayAdapter<BaseAccount> {
|
||||
public AccountsAdapter(List<BaseAccount> accounts) {
|
||||
super(AccountList.this, 0, accounts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final BaseAccount account = getItem(position);
|
||||
|
||||
final View view;
|
||||
if (convertView != null) {
|
||||
view = convertView;
|
||||
} else {
|
||||
view = getLayoutInflater().inflate(R.layout.accounts_item, parent, false);
|
||||
view.findViewById(R.id.active_icons).setVisibility(View.GONE);
|
||||
view.findViewById(R.id.folders).setVisibility(View.GONE);
|
||||
view.getBackground().setAlpha(0);
|
||||
}
|
||||
|
||||
AccountViewHolder holder = (AccountViewHolder) view.getTag();
|
||||
if (holder == null) {
|
||||
holder = new AccountViewHolder();
|
||||
holder.description = (TextView) view.findViewById(R.id.description);
|
||||
holder.email = (TextView) view.findViewById(R.id.email);
|
||||
holder.chip = view.findViewById(R.id.chip);
|
||||
|
||||
view.setTag(holder);
|
||||
}
|
||||
|
||||
String description = account.getDescription();
|
||||
if (account.getEmail().equals(description)) {
|
||||
holder.email.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.email.setVisibility(View.VISIBLE);
|
||||
holder.email.setText(account.getEmail());
|
||||
}
|
||||
|
||||
if (description == null || description.length() == 0) {
|
||||
description = account.getEmail();
|
||||
}
|
||||
|
||||
holder.description.setText(description);
|
||||
|
||||
if (account instanceof Account) {
|
||||
Account realAccount = (Account) account;
|
||||
holder.chip.setBackgroundColor(realAccount.getChipColor());
|
||||
} else {
|
||||
holder.chip.setBackgroundColor(0xff999999);
|
||||
}
|
||||
|
||||
holder.chip.getBackground().setAlpha(255);
|
||||
|
||||
holder.description.setTextSize(TypedValue.COMPLEX_UNIT_SP,
|
||||
mFontSizes.getAccountName());
|
||||
holder.email.setTextSize(TypedValue.COMPLEX_UNIT_SP,
|
||||
mFontSizes.getAccountDescription());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
class AccountViewHolder {
|
||||
public TextView description;
|
||||
public TextView email;
|
||||
public View chip;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load accounts in a background thread
|
||||
*/
|
||||
class LoadAccounts extends AsyncTask<Void, Void, Account[]> {
|
||||
@Override
|
||||
protected Account[] doInBackground(Void... params) {
|
||||
Account[] accounts = Preferences.getPreferences(getApplicationContext()).getAccounts();
|
||||
return accounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Account[] accounts) {
|
||||
populateListView(accounts);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@ -107,9 +108,21 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
*/
|
||||
private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0];
|
||||
|
||||
/**
|
||||
* URL used to open Android Market application
|
||||
*/
|
||||
private static final String ANDROID_MARKET_URL = "https://market.android.com/search?q=oi+file+manager&c=apps";
|
||||
|
||||
/**
|
||||
* Number of special accounts ('Unified Inbox' and 'All Messages')
|
||||
*/
|
||||
private static final int SPECIAL_ACCOUNTS_COUNT = 2;
|
||||
|
||||
private static final int DIALOG_REMOVE_ACCOUNT = 1;
|
||||
private static final int DIALOG_CLEAR_ACCOUNT = 2;
|
||||
private static final int DIALOG_RECREATE_ACCOUNT = 3;
|
||||
private static final int DIALOG_NO_FILE_MANAGER = 4;
|
||||
|
||||
private ConcurrentHashMap<String, AccountStats> accountStats = new ConcurrentHashMap<String, AccountStats>();
|
||||
|
||||
private ConcurrentHashMap<BaseAccount, String> pendingWork = new ConcurrentHashMap<BaseAccount, String>();
|
||||
@ -326,13 +339,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
super.onCreate(icicle);
|
||||
|
||||
if (!K9.isHideSpecialAccounts()) {
|
||||
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));
|
||||
createSpecialAccounts();
|
||||
}
|
||||
|
||||
Account[] accounts = Preferences.getPreferences(this).getAccounts();
|
||||
@ -375,6 +382,14 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes the special accounts ('Unified Inbox' and 'All Messages')
|
||||
*/
|
||||
private void createSpecialAccounts() {
|
||||
integratedInboxAccount = SearchAccount.createUnifiedInboxAccount(this);
|
||||
unreadAccount = SearchAccount.createAllMessagesAccount(this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void restoreAccountStats(Bundle icicle) {
|
||||
if (icicle != null) {
|
||||
@ -459,9 +474,13 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
accounts = Preferences.getPreferences(this).getAccounts();
|
||||
|
||||
List<BaseAccount> newAccounts;
|
||||
if (!K9.isHideSpecialAccounts()
|
||||
&& accounts.length > 0) {
|
||||
newAccounts = new ArrayList<BaseAccount>(accounts.length + 2);
|
||||
if (!K9.isHideSpecialAccounts() && accounts.length > 0) {
|
||||
if (integratedInboxAccount == null || unreadAccount == null) {
|
||||
createSpecialAccounts();
|
||||
}
|
||||
|
||||
newAccounts = new ArrayList<BaseAccount>(accounts.length +
|
||||
SPECIAL_ACCOUNTS_COUNT);
|
||||
newAccounts.add(integratedInboxAccount);
|
||||
newAccounts.add(unreadAccount);
|
||||
} else {
|
||||
@ -971,6 +990,20 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
}
|
||||
}
|
||||
});
|
||||
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);
|
||||
}
|
||||
@ -992,6 +1025,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
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;
|
||||
}
|
||||
|
||||
super.onPrepareDialog(id, d);
|
||||
@ -1244,7 +1280,16 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
i.setType(MimeUtility.K9_SETTINGS_MIME_TYPE);
|
||||
startActivityForResult(Intent.createChooser(i, null), ACTIVITY_REQUEST_PICK_SETTINGS_FILE);
|
||||
|
||||
PackageManager packageManager = getPackageManager();
|
||||
List<ResolveInfo> infos = packageManager.queryIntentActivities(i, 0);
|
||||
|
||||
if (infos.size() > 0) {
|
||||
startActivityForResult(Intent.createChooser(i, null),
|
||||
ACTIVITY_REQUEST_PICK_SETTINGS_FILE);
|
||||
} else {
|
||||
showDialog(DIALOG_NO_FILE_MANAGER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -733,6 +733,15 @@ public class FolderList extends K9ListActivity {
|
||||
menu.findItem(R.id.expunge).setVisible(false);
|
||||
}
|
||||
|
||||
if (!MessagingController.getInstance(getApplication()).isMoveCapable(mAccount)) {
|
||||
// FIXME: Really we want to do this for all local-only folders
|
||||
if (!mAccount.getInboxFolderName().equals(folder.name)) {
|
||||
menu.findItem(R.id.check_mail).setVisible(false);
|
||||
}
|
||||
|
||||
menu.findItem(R.id.expunge).setVisible(false);
|
||||
}
|
||||
|
||||
menu.setHeaderTitle(folder.displayName);
|
||||
}
|
||||
|
||||
|
@ -6,24 +6,21 @@ import java.util.Locale;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.view.ToggleScrollView;
|
||||
|
||||
|
||||
public class K9Activity extends Activity {
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
protected ToggleScrollView mTopView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
onCreate(icicle, true);
|
||||
@ -144,22 +141,6 @@ public class K9Activity extends Activity {
|
||||
private static final float SWIPE_MAX_OFF_PATH_DIP = 250f;
|
||||
private static final float SWIPE_THRESHOLD_VELOCITY_DIP = 325f;
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent ev) {
|
||||
super.onDoubleTap(ev);
|
||||
if (mTopView != null) {
|
||||
int height = getResources().getDisplayMetrics().heightPixels;
|
||||
if (ev.getRawY() < (height / 4)) {
|
||||
mTopView.fullScroll(View.FOCUS_UP);
|
||||
|
||||
} else if (ev.getRawY() > (height - height / 4)) {
|
||||
mTopView.fullScroll(View.FOCUS_DOWN);
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
// Do fling-detection if gestures are force-enabled or we have system-wide gestures enabled.
|
||||
@ -169,11 +150,11 @@ public class K9Activity extends Activity {
|
||||
final float mGestureScale = getResources().getDisplayMetrics().density;
|
||||
final int minVelocity = (int)(SWIPE_THRESHOLD_VELOCITY_DIP * mGestureScale + 0.5f);
|
||||
final int maxOffPath = (int)(SWIPE_MAX_OFF_PATH_DIP * mGestureScale + 0.5f);
|
||||
|
||||
|
||||
// Calculate how much was actually swiped.
|
||||
final float deltaX = e2.getX() - e1.getX();
|
||||
final float deltaY = e2.getY() - e1.getY();
|
||||
|
||||
|
||||
// Calculate the minimum distance required for this to be considered a swipe.
|
||||
final int minDistance = (int)Math.abs(deltaY * 4);
|
||||
|
||||
@ -216,4 +197,14 @@ public class K9Activity extends Activity {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getThemeBackgroundColor() {
|
||||
TypedArray array = getTheme().obtainStyledAttributes(new int[] {
|
||||
android.R.attr.colorBackground,
|
||||
});
|
||||
int backgroundColor = array.getColor(0, 0xFF00FF);
|
||||
array.recycle();
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,53 +3,42 @@ package com.fsck.k9.activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.FontSizes;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.BaseAccount;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.SearchSpecification;
|
||||
|
||||
public class LauncherShortcuts extends K9ListActivity implements OnItemClickListener {
|
||||
private AccountsAdapter mAdapter;
|
||||
private FontSizes mFontSizes = K9.getFontSizes();
|
||||
|
||||
public class LauncherShortcuts extends AccountList {
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
// finish() immediately if we aren't supposed to be here
|
||||
if (!Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
setContentView(R.layout.launcher_shortcuts);
|
||||
ListView listView = getListView();
|
||||
listView.setOnItemClickListener(this);
|
||||
listView.setItemsCanFocus(false);
|
||||
|
||||
refresh();
|
||||
super.onCreate(icicle);
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
Account[] accounts = Preferences.getPreferences(this).getAccounts();
|
||||
|
||||
mAdapter = new AccountsAdapter(accounts);
|
||||
getListView().setAdapter(mAdapter);
|
||||
@Override
|
||||
protected boolean displaySpecialAccounts() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setupShortcut(Account account) {
|
||||
final Intent shortcutIntent = FolderList.actionHandleAccountIntent(this, account, null, true);
|
||||
@Override
|
||||
protected void onAccountSelected(BaseAccount account) {
|
||||
Intent shortcutIntent = null;
|
||||
|
||||
if (account instanceof SearchSpecification) {
|
||||
shortcutIntent = MessageList.actionHandleAccountIntent(this, account.getDescription(),
|
||||
(SearchSpecification) account);
|
||||
} else {
|
||||
shortcutIntent = FolderList.actionHandleAccountIntent(this, (Account) account, null,
|
||||
true);
|
||||
}
|
||||
|
||||
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
|
||||
String description = account.getDescription();
|
||||
@ -63,66 +52,4 @@ public class LauncherShortcuts extends K9ListActivity implements OnItemClickList
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Account account = (Account) parent.getItemAtPosition(position);
|
||||
setupShortcut(account);
|
||||
}
|
||||
|
||||
class AccountsAdapter extends ArrayAdapter<Account> {
|
||||
public AccountsAdapter(Account[] accounts) {
|
||||
super(LauncherShortcuts.this, 0, accounts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final Account account = getItem(position);
|
||||
|
||||
final View view;
|
||||
if (convertView != null) {
|
||||
view = convertView;
|
||||
} else {
|
||||
view = getLayoutInflater().inflate(R.layout.accounts_item, parent, false);
|
||||
view.findViewById(R.id.active_icons).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
AccountViewHolder holder = (AccountViewHolder) view.getTag();
|
||||
if (holder == null) {
|
||||
holder = new AccountViewHolder();
|
||||
holder.description = (TextView) view.findViewById(R.id.description);
|
||||
holder.email = (TextView) view.findViewById(R.id.email);
|
||||
holder.chip = view.findViewById(R.id.chip);
|
||||
|
||||
view.setTag(holder);
|
||||
}
|
||||
|
||||
String description = account.getDescription();
|
||||
if (account.getEmail().equals(description)) {
|
||||
holder.email.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.email.setVisibility(View.VISIBLE);
|
||||
holder.email.setText(account.getEmail());
|
||||
}
|
||||
|
||||
if (description == null || description.length() == 0) {
|
||||
description = account.getEmail();
|
||||
}
|
||||
|
||||
holder.description.setText(description);
|
||||
|
||||
holder.chip.setBackgroundColor(account.getChipColor());
|
||||
holder.chip.getBackground().setAlpha(255);
|
||||
|
||||
holder.description.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getAccountName());
|
||||
holder.email.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getAccountDescription());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
class AccountViewHolder {
|
||||
public TextView description;
|
||||
public TextView email;
|
||||
public View chip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1;
|
||||
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 long INVALID_DRAFT_ID = MessagingController.INVALID_MESSAGE_ID;
|
||||
|
||||
@ -1609,7 +1610,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
final Account account = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
|
||||
final String folderName = mMessageReference.folderName;
|
||||
final String sourceMessageUid = mMessageReference.uid;
|
||||
MessagingController.getInstance(getApplication()).setFlag(account, folderName, new String[] {sourceMessageUid}, mMessageReference.flag, true);
|
||||
MessagingController.getInstance(getApplication()).setFlag(account, folderName, sourceMessageUid, mMessageReference.flag, true);
|
||||
}
|
||||
|
||||
mDraftNeedsSaving = false;
|
||||
@ -2050,13 +2051,21 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mEncryptCheckbox.isChecked()) {
|
||||
showDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED);
|
||||
} else if (!mDraftNeedsSaving || isDraftsFolderDisabled()) {
|
||||
Toast.makeText(MessageCompose.this, getString(R.string.message_discarded_toast), Toast.LENGTH_LONG).show();
|
||||
super.onBackPressed();
|
||||
if (mDraftNeedsSaving) {
|
||||
if (mEncryptCheckbox.isChecked()) {
|
||||
showDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED);
|
||||
} else if (isDraftsFolderDisabled()) {
|
||||
showDialog(DIALOG_CONFIRM_DISCARD_ON_BACK);
|
||||
} else {
|
||||
showDialog(DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE);
|
||||
}
|
||||
} else {
|
||||
showDialog(DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE);
|
||||
// Check if editing an existing draft.
|
||||
if (mDraftId == INVALID_DRAFT_ID) {
|
||||
onDiscard();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2117,6 +2126,27 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
}
|
||||
})
|
||||
.create();
|
||||
case DIALOG_CONFIRM_DISCARD_ON_BACK:
|
||||
return new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.confirm_discard_draft_message_title)
|
||||
.setMessage(R.string.confirm_discard_draft_message)
|
||||
.setPositiveButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
dismissDialog(DIALOG_CONFIRM_DISCARD_ON_BACK);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.discard_action, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
dismissDialog(DIALOG_CONFIRM_DISCARD_ON_BACK);
|
||||
Toast.makeText(MessageCompose.this,
|
||||
getString(R.string.message_discarded_toast),
|
||||
Toast.LENGTH_LONG).show();
|
||||
onDiscard();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
return super.onCreateDialog(id);
|
||||
}
|
||||
@ -2678,7 +2708,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
if (part != null) {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found.");
|
||||
return HtmlConverter.textToHtml(MimeUtility.getTextFromPart(part));
|
||||
return HtmlConverter.textToHtml(MimeUtility.getTextFromPart(part), true);
|
||||
}
|
||||
} else if (format == MessageFormat.TEXT) {
|
||||
// Text takes precedence, then html.
|
||||
@ -2987,9 +3017,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
}
|
||||
|
||||
MessagingController.getInstance(getApplication()).sendMessage(mAccount, message, null);
|
||||
if (mDraftId != INVALID_DRAFT_ID) {
|
||||
MessagingController.getInstance(getApplication()).deleteDraft(mAccount, mDraftId);
|
||||
long draftId = mDraftId;
|
||||
if (draftId != INVALID_DRAFT_ID) {
|
||||
mDraftId = INVALID_DRAFT_ID;
|
||||
MessagingController.getInstance(getApplication()).deleteDraft(mAccount, draftId);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -52,10 +52,12 @@ import android.widget.Toast;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.AccountStats;
|
||||
import com.fsck.k9.BaseAccount;
|
||||
import com.fsck.k9.FontSizes;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.SearchAccount;
|
||||
import com.fsck.k9.SearchSpecification;
|
||||
import com.fsck.k9.activity.setup.AccountSettings;
|
||||
import com.fsck.k9.activity.setup.FolderSettings;
|
||||
@ -70,6 +72,7 @@ import com.fsck.k9.mail.Folder;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import com.fsck.k9.mail.store.StorageManager;
|
||||
|
||||
|
||||
@ -616,7 +619,11 @@ public class MessageList
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void actionHandle(Context context, String title, SearchSpecification searchSpecification) {
|
||||
/**
|
||||
* Creates and returns an intent that opens Unified Inbox or All Messages screen.
|
||||
*/
|
||||
public static Intent actionHandleAccountIntent(Context context, String title,
|
||||
SearchSpecification searchSpecification) {
|
||||
Intent intent = new Intent(context, MessageList.class);
|
||||
intent.putExtra(EXTRA_QUERY, searchSpecification.getQuery());
|
||||
if (searchSpecification.getRequiredFlags() != null) {
|
||||
@ -632,6 +639,13 @@ public class MessageList
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static void actionHandle(Context context, String title,
|
||||
SearchSpecification searchSpecification) {
|
||||
Intent intent = actionHandleAccountIntent(context, title, searchSpecification);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@ -1388,13 +1402,21 @@ public class MessageList
|
||||
}
|
||||
|
||||
private void onToggleRead(MessageInfoHolder holder) {
|
||||
mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getRemoteName(), new String[] { holder.uid }, Flag.SEEN, !holder.read);
|
||||
LocalMessage message = holder.message;
|
||||
Folder folder = message.getFolder();
|
||||
Account account = folder.getAccount();
|
||||
String folderName = folder.getRemoteName();
|
||||
mController.setFlag(account, folderName, new Message[] { message }, Flag.SEEN, !holder.read);
|
||||
holder.read = !holder.read;
|
||||
mHandler.sortMessages();
|
||||
}
|
||||
|
||||
private void onToggleFlag(MessageInfoHolder holder) {
|
||||
mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getRemoteName(), new String[] { holder.uid }, Flag.FLAGGED, !holder.flagged);
|
||||
LocalMessage message = holder.message;
|
||||
Folder folder = message.getFolder();
|
||||
Account account = folder.getAccount();
|
||||
String folderName = folder.getRemoteName();
|
||||
mController.setFlag(account, folderName, new Message[] { message }, Flag.FLAGGED, !holder.flagged);
|
||||
holder.flagged = !holder.flagged;
|
||||
mHandler.sortMessages();
|
||||
}
|
||||
@ -1593,6 +1615,19 @@ public class MessageList
|
||||
if (K9.FOLDER_NONE.equalsIgnoreCase(mAccount.getSpamFolderName())) {
|
||||
menu.findItem(R.id.batch_spam_op).setVisible(false);
|
||||
}
|
||||
|
||||
if (!mController.isMoveCapable(mAccount)) {
|
||||
// FIXME: Really we want to do this for all local-only folders
|
||||
if (mCurrentFolder != null &&
|
||||
!mAccount.getInboxFolderName().equals(mCurrentFolder.name)) {
|
||||
menu.findItem(R.id.check_mail).setVisible(false);
|
||||
}
|
||||
menu.findItem(R.id.batch_archive_op).setVisible(false);
|
||||
menu.findItem(R.id.batch_spam_op).setVisible(false);
|
||||
menu.findItem(R.id.batch_move_op).setVisible(false);
|
||||
menu.findItem(R.id.batch_copy_op).setVisible(false);
|
||||
menu.findItem(R.id.expunge).setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
boolean newFlagState = computeBatchDirection(true);
|
||||
|
@ -4,11 +4,9 @@ import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.view.View.OnClickListener;
|
||||
@ -20,9 +18,9 @@ import com.fsck.k9.crypto.PgpData;
|
||||
import com.fsck.k9.helper.FileBrowserHelper;
|
||||
import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import com.fsck.k9.mail.store.StorageManager;
|
||||
import com.fsck.k9.view.AttachmentView;
|
||||
import com.fsck.k9.view.ToggleScrollView;
|
||||
import com.fsck.k9.view.SingleMessageView;
|
||||
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
|
||||
|
||||
@ -34,8 +32,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
private static final String EXTRA_MESSAGE_REFERENCES = "com.fsck.k9.MessageView_messageReferences";
|
||||
private static final String EXTRA_NEXT = "com.fsck.k9.MessageView_next";
|
||||
private static final String EXTRA_MESSAGE_LIST_EXTRAS = "com.fsck.k9.MessageView_messageListExtras";
|
||||
private static final String EXTRA_SCROLL_PERCENTAGE = "com.fsck.k9.MessageView_scrollPercentage";
|
||||
private static final String SHOW_PICTURES = "showPictures";
|
||||
private static final String STATE_PGP_DATA = "pgpData";
|
||||
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
|
||||
private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
|
||||
@ -45,7 +41,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
private PgpData mPgpData;
|
||||
|
||||
|
||||
private View mNext;
|
||||
private View mPrevious;
|
||||
private View mDelete;
|
||||
@ -106,24 +101,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_UP) {
|
||||
// Text selection is finished. Allow scrolling again.
|
||||
mTopView.setScrolling(true);
|
||||
} else if (K9.zoomControlsEnabled()) {
|
||||
// If we have system zoom controls enabled, disable scrolling so the screen isn't wiggling around while
|
||||
// trying to zoom.
|
||||
if (ev.getAction() == MotionEvent.ACTION_POINTER_2_DOWN) {
|
||||
mTopView.setScrolling(false);
|
||||
} else if (ev.getAction() == MotionEvent.ACTION_POINTER_2_UP) {
|
||||
mTopView.setScrolling(true);
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
boolean ret = false;
|
||||
@ -167,15 +144,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyEvent.KEYCODE_SHIFT_LEFT:
|
||||
case KeyEvent.KEYCODE_SHIFT_RIGHT: {
|
||||
/*
|
||||
* Selecting text started via shift key. Disable scrolling as
|
||||
* this causes problems when selecting text.
|
||||
*/
|
||||
mTopView.setScrolling(false);
|
||||
break;
|
||||
}
|
||||
case KeyEvent.KEYCODE_DEL: {
|
||||
onDelete();
|
||||
return true;
|
||||
@ -326,7 +294,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.message_view);
|
||||
|
||||
mTopView = (ToggleScrollView) findViewById(R.id.top_view);
|
||||
mMessageView = (SingleMessageView) findViewById(R.id.message_view);
|
||||
|
||||
//set a callback for the attachment view. With this callback the attachmentview
|
||||
@ -358,10 +325,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
mMessageView.initialize(this);
|
||||
|
||||
// Register the ScrollView's listener to handle scrolling to last known location on resume.
|
||||
mController.addListener(mTopView.getListener());
|
||||
mMessageView.setListeners(mController.getListeners());
|
||||
|
||||
setTitle("");
|
||||
final Intent intent = getIntent();
|
||||
|
||||
@ -433,57 +396,29 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
setOnClickListener(R.id.archive);
|
||||
setOnClickListener(R.id.move);
|
||||
setOnClickListener(R.id.spam);
|
||||
// To show full header
|
||||
setOnClickListener(R.id.header_container);
|
||||
setOnClickListener(R.id.reply_scrolling);
|
||||
// setOnClickListener(R.id.reply_all_scrolling);
|
||||
setOnClickListener(R.id.delete_scrolling);
|
||||
setOnClickListener(R.id.forward_scrolling);
|
||||
setOnClickListener(R.id.next_scrolling);
|
||||
setOnClickListener(R.id.previous_scrolling);
|
||||
setOnClickListener(R.id.archive_scrolling);
|
||||
setOnClickListener(R.id.move_scrolling);
|
||||
setOnClickListener(R.id.spam_scrolling);
|
||||
setOnClickListener(R.id.show_pictures);
|
||||
setOnClickListener(R.id.download_remainder);
|
||||
|
||||
|
||||
// Perhaps the ScrollButtons should be global, instead of account-specific
|
||||
Account.ScrollButtons scrollButtons = mAccount.getScrollMessageViewButtons();
|
||||
if ((Account.ScrollButtons.ALWAYS == scrollButtons)
|
||||
|| (Account.ScrollButtons.KEYBOARD_AVAILABLE == scrollButtons &&
|
||||
(this.getResources().getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO))) {
|
||||
scrollButtons();
|
||||
} else { // never or the keyboard is open
|
||||
staticButtons();
|
||||
}
|
||||
Account.ScrollButtons scrollMoveButtons = mAccount.getScrollMessageViewMoveButtons();
|
||||
if ((Account.ScrollButtons.ALWAYS == scrollMoveButtons)
|
||||
|| (Account.ScrollButtons.KEYBOARD_AVAILABLE == scrollMoveButtons &&
|
||||
(this.getResources().getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO))) {
|
||||
scrollMoveButtons();
|
||||
} else {
|
||||
staticMoveButtons();
|
||||
}
|
||||
mNext = findViewById(R.id.next);
|
||||
mPrevious = findViewById(R.id.previous);
|
||||
mDelete = findViewById(R.id.delete);
|
||||
|
||||
mArchive = findViewById(R.id.archive);
|
||||
mMove = findViewById(R.id.move);
|
||||
mSpam = findViewById(R.id.spam);
|
||||
|
||||
if (!mAccount.getEnableMoveButtons()) {
|
||||
View buttons = findViewById(R.id.move_buttons);
|
||||
if (buttons != null) {
|
||||
buttons.setVisibility(View.GONE);
|
||||
}
|
||||
buttons = findViewById(R.id.scrolling_move_buttons);
|
||||
if (buttons != null) {
|
||||
buttons.setVisibility(View.GONE);
|
||||
}
|
||||
buttons.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelable(EXTRA_MESSAGE_REFERENCE, mMessageReference);
|
||||
outState.putParcelableArrayList(EXTRA_MESSAGE_REFERENCES, mMessageReferences);
|
||||
outState.putSerializable(STATE_PGP_DATA, mPgpData);
|
||||
outState.putBoolean(SHOW_PICTURES, mMessageView.showPictures());
|
||||
outState.putDouble(EXTRA_SCROLL_PERCENTAGE, mTopView.getScrollPercentage());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -491,8 +426,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
mPgpData = (PgpData) savedInstanceState.getSerializable(STATE_PGP_DATA);
|
||||
mMessageView.updateCryptoLayout(mAccount.getCryptoProvider(), mPgpData, mMessage);
|
||||
mMessageView.setLoadPictures(savedInstanceState.getBoolean(SHOW_PICTURES));
|
||||
mTopView.setScrollPercentage(savedInstanceState.getDouble(EXTRA_SCROLL_PERCENTAGE));
|
||||
}
|
||||
|
||||
private void displayMessage(MessageReference ref) {
|
||||
@ -500,22 +433,18 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "MessageView displaying message " + mMessageReference);
|
||||
mAccount = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
|
||||
clearMessageDisplay();
|
||||
findSurroundingMessagesUid();
|
||||
// start with fresh, empty PGP data
|
||||
mPgpData = new PgpData();
|
||||
mTopView.setVisibility(View.VISIBLE);
|
||||
|
||||
// Clear previous message
|
||||
mMessageView.resetView();
|
||||
mMessageView.resetHeaderView();
|
||||
|
||||
mController.loadMessageForView(mAccount, mMessageReference.folderName, mMessageReference.uid, mListener);
|
||||
setupDisplayMessageButtons();
|
||||
}
|
||||
|
||||
private void clearMessageDisplay() {
|
||||
mTopView.setVisibility(View.GONE);
|
||||
mTopView.scrollTo(0, 0);
|
||||
mMessageView.resetView();
|
||||
|
||||
}
|
||||
|
||||
private void setupDisplayMessageButtons() {
|
||||
mDelete.setEnabled(true);
|
||||
mNext.setEnabled(mNextMessage != null);
|
||||
@ -533,45 +462,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
disableMoveButtons();
|
||||
}
|
||||
}
|
||||
private void staticButtons() {
|
||||
View buttons = findViewById(R.id.scrolling_buttons);
|
||||
if (buttons != null) {
|
||||
buttons.setVisibility(View.GONE);
|
||||
}
|
||||
mNext = findViewById(R.id.next);
|
||||
mPrevious = findViewById(R.id.previous);
|
||||
mDelete = findViewById(R.id.delete);
|
||||
}
|
||||
|
||||
private void scrollButtons() {
|
||||
View buttons = findViewById(R.id.bottom_buttons);
|
||||
if (buttons != null) {
|
||||
buttons.setVisibility(View.GONE);
|
||||
}
|
||||
mNext = findViewById(R.id.next_scrolling);
|
||||
mPrevious = findViewById(R.id.previous_scrolling);
|
||||
mDelete = findViewById(R.id.delete_scrolling);
|
||||
}
|
||||
|
||||
private void staticMoveButtons() {
|
||||
View buttons = findViewById(R.id.scrolling_move_buttons);
|
||||
if (buttons != null) {
|
||||
buttons.setVisibility(View.GONE);
|
||||
}
|
||||
mArchive = findViewById(R.id.archive);
|
||||
mMove = findViewById(R.id.move);
|
||||
mSpam = findViewById(R.id.spam);
|
||||
}
|
||||
|
||||
private void scrollMoveButtons() {
|
||||
View buttons = findViewById(R.id.move_buttons);
|
||||
if (buttons != null) {
|
||||
buttons.setVisibility(View.GONE);
|
||||
}
|
||||
mArchive = findViewById(R.id.archive_scrolling);
|
||||
mMove = findViewById(R.id.move_scrolling);
|
||||
mSpam = findViewById(R.id.spam_scrolling);
|
||||
}
|
||||
|
||||
private void disableButtons() {
|
||||
mMessageView.setLoadPictures(false);
|
||||
@ -612,13 +502,11 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
onAccountUnavailable();
|
||||
return;
|
||||
}
|
||||
mController.addListener(mTopView.getListener());
|
||||
StorageManager.getInstance(getApplication()).addListener(mStorageListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
mController.removeListener(mTopView.getListener());
|
||||
StorageManager.getInstance(getApplication()).removeListener(mStorageListener);
|
||||
super.onPause();
|
||||
}
|
||||
@ -728,14 +616,10 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
private void onFlag() {
|
||||
if (mMessage != null) {
|
||||
mController.setFlag(mAccount,
|
||||
mMessage.getFolder().getRemoteName(), new String[] {mMessage.getUid()}, Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
|
||||
try {
|
||||
mMessage.setFlag(Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
|
||||
mMessageView.setHeaders(mMessage, mAccount);
|
||||
} catch (MessagingException me) {
|
||||
Log.e(K9.LOG_TAG, "Could not set flag on local message", me);
|
||||
}
|
||||
boolean newState = !mMessage.isSet(Flag.FLAGGED);
|
||||
mController.setFlag(mAccount, mMessage.getFolder().getRemoteName(),
|
||||
new Message[] { mMessage }, Flag.FLAGGED, newState);
|
||||
mMessageView.setHeaders(mMessage, mAccount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -846,7 +730,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
protected void onNext() {
|
||||
// Reset scroll percentage when we change messages
|
||||
mTopView.setScrollPercentage(0);
|
||||
if (mNextMessage == null) {
|
||||
Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
@ -854,7 +737,7 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
mLastDirection = NEXT;
|
||||
disableButtons();
|
||||
if (K9.showAnimations()) {
|
||||
mTopView.startAnimation(outToLeftAnimation());
|
||||
mMessageView.startAnimation(outToLeftAnimation());
|
||||
}
|
||||
displayMessage(mNextMessage);
|
||||
mNext.requestFocus();
|
||||
@ -862,7 +745,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
protected void onPrevious() {
|
||||
// Reset scroll percentage when we change messages
|
||||
mTopView.setScrollPercentage(0);
|
||||
if (mPreviousMessage == null) {
|
||||
Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
@ -870,7 +752,7 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
mLastDirection = PREVIOUS;
|
||||
disableButtons();
|
||||
if (K9.showAnimations()) {
|
||||
mTopView.startAnimation(inFromRightAnimation());
|
||||
mMessageView.startAnimation(inFromRightAnimation());
|
||||
}
|
||||
displayMessage(mPreviousMessage);
|
||||
mPrevious.requestFocus();
|
||||
@ -878,15 +760,11 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
private void onMarkAsUnread() {
|
||||
if (mMessage != null) {
|
||||
// (Issue 3319) mController.setFlag(mAccount, mMessageReference.folderName, new String[] { mMessage.getUid() }, Flag.SEEN, false);
|
||||
try {
|
||||
mMessage.setFlag(Flag.SEEN, false);
|
||||
mMessageView.setHeaders(mMessage, mAccount);
|
||||
String subject = mMessage.getSubject();
|
||||
setTitle(subject);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to unset SEEN flag on message", e);
|
||||
}
|
||||
mController.setFlag(mAccount, mMessage.getFolder().getName(),
|
||||
new Message[] { mMessage }, Flag.SEEN, false);
|
||||
mMessageView.setHeaders(mMessage, mAccount);
|
||||
String subject = mMessage.getSubject();
|
||||
setTitle(subject);
|
||||
}
|
||||
}
|
||||
|
||||
@ -903,46 +781,35 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.reply:
|
||||
case R.id.reply_scrolling:
|
||||
onReply();
|
||||
break;
|
||||
case R.id.reply_all:
|
||||
onReplyAll();
|
||||
break;
|
||||
case R.id.delete:
|
||||
case R.id.delete_scrolling:
|
||||
onDelete();
|
||||
break;
|
||||
case R.id.forward:
|
||||
case R.id.forward_scrolling:
|
||||
onForward();
|
||||
break;
|
||||
case R.id.archive:
|
||||
case R.id.archive_scrolling:
|
||||
onRefile(mAccount.getArchiveFolderName());
|
||||
break;
|
||||
case R.id.spam:
|
||||
case R.id.spam_scrolling:
|
||||
onRefile(mAccount.getSpamFolderName());
|
||||
break;
|
||||
case R.id.move:
|
||||
case R.id.move_scrolling:
|
||||
onMove();
|
||||
break;
|
||||
case R.id.next:
|
||||
case R.id.next_scrolling:
|
||||
onNext();
|
||||
break;
|
||||
case R.id.previous:
|
||||
case R.id.previous_scrolling:
|
||||
onPrevious();
|
||||
break;
|
||||
case R.id.download:
|
||||
((AttachmentView)view).saveFile();
|
||||
break;
|
||||
case R.id.show_pictures:
|
||||
mMessageView.setLoadPictures(true);
|
||||
break;
|
||||
case R.id.download_remainder:
|
||||
onDownloadRemainder();
|
||||
break;
|
||||
@ -994,7 +861,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
});
|
||||
break;
|
||||
case R.id.select_text:
|
||||
mTopView.setScrolling(false);
|
||||
mMessageView.beginSelectingText();
|
||||
break;
|
||||
default:
|
||||
@ -1083,29 +949,7 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
}
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
public void displayMessageBody(final Account account, final String folder, final String uid, final Message message) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
mTopView.scrollTo(0, 0);
|
||||
try {
|
||||
if (MessageView.this.mMessage != null
|
||||
&& MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)
|
||||
&& message.isSet(Flag.X_DOWNLOADED_FULL)) {
|
||||
mMessageView.setHeaders(message, account);
|
||||
}
|
||||
MessageView.this.mMessage = message;
|
||||
mMessageView.displayMessageBody(account, folder, uid, message, mPgpData);
|
||||
mMessageView.renderAttachments(mMessage, 0, mMessage, mAccount, mController, mListener);
|
||||
} catch (MessagingException e) {
|
||||
if (Config.LOGV) {
|
||||
Log.v(K9.LOG_TAG, "loadMessageForViewBodyAvailable", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
class Listener extends MessagingListener {
|
||||
@Override
|
||||
public void loadMessageForViewHeadersAvailable(final Account account, String folder, String uid,
|
||||
@ -1114,7 +958,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|| !mMessageReference.accountUuid.equals(account.getUuid())) {
|
||||
return;
|
||||
}
|
||||
MessageView.this.mMessage = message;
|
||||
|
||||
/*
|
||||
* Clone the message object because the original could be modified by
|
||||
@ -1148,17 +991,28 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMessageForViewBodyAvailable(Account account, String folder, String uid,
|
||||
Message message) {
|
||||
if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder)
|
||||
|| !mMessageReference.accountUuid.equals(account.getUuid())) {
|
||||
public void loadMessageForViewBodyAvailable(final Account account, String folder,
|
||||
String uid, final Message message) {
|
||||
if (!mMessageReference.uid.equals(uid) ||
|
||||
!mMessageReference.folderName.equals(folder) ||
|
||||
!mMessageReference.accountUuid.equals(account.getUuid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
displayMessageBody(account, folder, uid, message);
|
||||
}//loadMessageForViewBodyAvailable
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mMessage = message;
|
||||
mMessageView.setMessage(account, (LocalMessage) message, mPgpData,
|
||||
mController, mListener);
|
||||
|
||||
} catch (MessagingException e) {
|
||||
Log.v(K9.LOG_TAG, "loadMessageForViewBodyAvailable", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMessageForViewFailed(Account account, String folder, String uid, final Throwable t) {
|
||||
@ -1263,8 +1117,14 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
// This REALLY should be in MessageCryptoView
|
||||
public void onDecryptDone(PgpData pgpData) {
|
||||
// TODO: this might not be enough if the orientation was changed while in APG,
|
||||
// sometimes shows the original encrypted content
|
||||
mMessageView.loadBodyFromText(mAccount.getCryptoProvider(), mPgpData, mMessage, mPgpData.getDecryptedData(), "text/plain");
|
||||
Account account = mAccount;
|
||||
LocalMessage message = (LocalMessage) mMessage;
|
||||
MessagingController controller = mController;
|
||||
Listener listener = mListener;
|
||||
try {
|
||||
mMessageView.setMessage(account, message, pgpData, controller, listener);
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "displayMessageBody failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
106
src/com/fsck/k9/activity/UnreadWidgetConfiguration.java
Normal file
106
src/com/fsck/k9/activity/UnreadWidgetConfiguration.java
Normal file
@ -0,0 +1,106 @@
|
||||
package com.fsck.k9.activity;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.BaseAccount;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.provider.UnreadWidgetProvider;
|
||||
|
||||
|
||||
/**
|
||||
* Activity to select an account for the unread widget.
|
||||
*/
|
||||
public class UnreadWidgetConfiguration extends AccountList {
|
||||
/**
|
||||
* Name of the preference file to store the widget configuration.
|
||||
*/
|
||||
private static final String PREFS_NAME = "unread_widget_configuration.xml";
|
||||
|
||||
/**
|
||||
* Prefix for the preference keys.
|
||||
*/
|
||||
private static final String PREF_PREFIX_KEY = "unread_widget.";
|
||||
|
||||
|
||||
/**
|
||||
* The ID of the widget we are configuring.
|
||||
*/
|
||||
private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
// Find the widget ID from the intent.
|
||||
Intent intent = getIntent();
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||
AppWidgetManager.INVALID_APPWIDGET_ID);
|
||||
}
|
||||
|
||||
// If they gave us an intent without the widget ID, just bail.
|
||||
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
setTitle(R.string.unread_widget_select_account);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean displaySpecialAccounts() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAccountSelected(BaseAccount baseAccount) {
|
||||
if (!(baseAccount instanceof Account)) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Account account = (Account) baseAccount;
|
||||
|
||||
// Save widget configuration
|
||||
String accountUuid = account.getUuid();
|
||||
saveAccountUuid(this, mAppWidgetId, accountUuid);
|
||||
|
||||
// Update widget
|
||||
Context context = getApplicationContext();
|
||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||
UnreadWidgetProvider.updateWidget(context, appWidgetManager, mAppWidgetId, accountUuid);
|
||||
|
||||
// Let the caller know that the configuration was successful
|
||||
Intent resultValue = new Intent();
|
||||
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
|
||||
setResult(RESULT_OK, resultValue);
|
||||
finish();
|
||||
}
|
||||
|
||||
private static void saveAccountUuid(Context context, int appWidgetId, String accountUuid) {
|
||||
SharedPreferences.Editor editor =
|
||||
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit();
|
||||
editor.putString(PREF_PREFIX_KEY + appWidgetId, accountUuid);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public static String getAccountUuid(Context context, int appWidgetId) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
String accountUuid = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null);
|
||||
return accountUuid;
|
||||
}
|
||||
|
||||
public static void deleteWidgetConfiguration(Context context, int appWidgetId) {
|
||||
Editor editor = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit();
|
||||
editor.remove(PREF_PREFIX_KEY + appWidgetId);
|
||||
editor.commit();
|
||||
}
|
||||
}
|
@ -18,7 +18,6 @@ import java.util.List;
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.Account.FolderMode;
|
||||
import com.fsck.k9.Account.QuoteStyle;
|
||||
import com.fsck.k9.Account.ScrollButtons;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.NotificationSetting;
|
||||
import com.fsck.k9.Preferences;
|
||||
@ -54,8 +53,6 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
private static final String PREFERENCE_FREQUENCY = "account_check_frequency";
|
||||
private static final String PREFERENCE_DISPLAY_COUNT = "account_display_count";
|
||||
private static final String PREFERENCE_DEFAULT = "account_default";
|
||||
private static final String PREFERENCE_HIDE_BUTTONS = "hide_buttons_enum";
|
||||
private static final String PREFERENCE_HIDE_MOVE_BUTTONS = "hide_move_buttons_enum";
|
||||
private static final String PREFERENCE_SHOW_PICTURES = "show_pictures_enum";
|
||||
private static final String PREFERENCE_ENABLE_MOVE_BUTTONS = "enable_move_buttons";
|
||||
private static final String PREFERENCE_NOTIFY = "account_notify";
|
||||
@ -100,7 +97,7 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
|
||||
private static final String PREFERENCE_LOCAL_STORAGE_PROVIDER = "local_storage_provider";
|
||||
|
||||
|
||||
private static final String PREFERENCE_CATEGORY_FOLDERS = "folders";
|
||||
private static final String PREFERENCE_ARCHIVE_FOLDER = "archive_folder";
|
||||
private static final String PREFERENCE_DRAFTS_FOLDER = "drafts_folder";
|
||||
private static final String PREFERENCE_SENT_FOLDER = "sent_folder";
|
||||
@ -124,8 +121,6 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
private CheckBoxPreference mAccountDefault;
|
||||
private CheckBoxPreference mAccountNotify;
|
||||
private CheckBoxPreference mAccountNotifySelf;
|
||||
private ListPreference mAccountScrollButtons;
|
||||
private ListPreference mAccountScrollMoveButtons;
|
||||
private ListPreference mAccountShowPictures;
|
||||
private CheckBoxPreference mAccountEnableMoveButtons;
|
||||
private CheckBoxPreference mAccountNotifySync;
|
||||
@ -428,37 +423,10 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
mAccountDefault.setChecked(
|
||||
mAccount.equals(Preferences.getPreferences(this).getDefaultAccount()));
|
||||
|
||||
mAccountScrollButtons = (ListPreference) findPreference(PREFERENCE_HIDE_BUTTONS);
|
||||
mAccountScrollButtons.setValue("" + mAccount.getScrollMessageViewButtons());
|
||||
mAccountScrollButtons.setSummary(mAccountScrollButtons.getEntry());
|
||||
mAccountScrollButtons.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final String summary = newValue.toString();
|
||||
int index = mAccountScrollButtons.findIndexOfValue(summary);
|
||||
mAccountScrollButtons.setSummary(mAccountScrollButtons.getEntries()[index]);
|
||||
mAccountScrollButtons.setValue(summary);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mAccountEnableMoveButtons = (CheckBoxPreference) findPreference(PREFERENCE_ENABLE_MOVE_BUTTONS);
|
||||
mAccountEnableMoveButtons.setEnabled(mIsMoveCapable);
|
||||
mAccountEnableMoveButtons.setChecked(mAccount.getEnableMoveButtons());
|
||||
|
||||
mAccountScrollMoveButtons = (ListPreference) findPreference(PREFERENCE_HIDE_MOVE_BUTTONS);
|
||||
mAccountScrollMoveButtons.setEnabled(mIsMoveCapable);
|
||||
mAccountScrollMoveButtons.setValue("" + mAccount.getScrollMessageViewMoveButtons());
|
||||
mAccountScrollMoveButtons.setSummary(mAccountScrollMoveButtons.getEntry());
|
||||
mAccountScrollMoveButtons.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final String summary = newValue.toString();
|
||||
int index = mAccountScrollMoveButtons.findIndexOfValue(summary);
|
||||
mAccountScrollMoveButtons.setSummary(mAccountScrollMoveButtons.getEntries()[index]);
|
||||
mAccountScrollMoveButtons.setValue(summary);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mAccountShowPictures = (ListPreference) findPreference(PREFERENCE_SHOW_PICTURES);
|
||||
mAccountShowPictures.setValue("" + mAccount.getShowPictures());
|
||||
mAccountShowPictures.setSummary(mAccountShowPictures.getEntry());
|
||||
@ -745,11 +713,13 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
else
|
||||
mAccount.setAutoExpandFolderName(reverseTranslateFolder(mAutoExpandFolder.getValue()));
|
||||
|
||||
mAccount.setArchiveFolderName(mArchiveFolder.getValue());
|
||||
mAccount.setDraftsFolderName(mDraftsFolder.getValue());
|
||||
mAccount.setSentFolderName(mSentFolder.getValue());
|
||||
mAccount.setSpamFolderName(mSpamFolder.getValue());
|
||||
mAccount.setTrashFolderName(mTrashFolder.getValue());
|
||||
if (mIsMoveCapable) {
|
||||
mAccount.setArchiveFolderName(mArchiveFolder.getValue());
|
||||
mAccount.setDraftsFolderName(mDraftsFolder.getValue());
|
||||
mAccount.setSentFolderName(mSentFolder.getValue());
|
||||
mAccount.setSpamFolderName(mSpamFolder.getValue());
|
||||
mAccount.setTrashFolderName(mTrashFolder.getValue());
|
||||
}
|
||||
|
||||
|
||||
if (mIsPushCapable) {
|
||||
@ -760,10 +730,8 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
|
||||
if (!mIsMoveCapable) {
|
||||
mAccount.setEnableMoveButtons(false);
|
||||
mAccount.setScrollMessageViewMoveButtons(ScrollButtons.NEVER);
|
||||
} else {
|
||||
mAccount.setEnableMoveButtons(mAccountEnableMoveButtons.isChecked());
|
||||
mAccount.setScrollMessageViewMoveButtons(Account.ScrollButtons.valueOf(mAccountScrollMoveButtons.getValue()));
|
||||
}
|
||||
|
||||
boolean needsRefresh = mAccount.setAutomaticCheckIntervalMinutes(Integer.parseInt(mCheckFrequency.getValue()));
|
||||
@ -782,24 +750,23 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
}
|
||||
}
|
||||
|
||||
mAccount.setScrollMessageViewButtons(Account.ScrollButtons.valueOf(mAccountScrollButtons.getValue()));
|
||||
mAccount.setShowPictures(Account.ShowPictures.valueOf(mAccountShowPictures.getValue()));
|
||||
|
||||
if (mIsPushCapable) {
|
||||
boolean needsPushRestart = mAccount.setFolderPushMode(Account.FolderMode.valueOf(mPushMode.getValue()));
|
||||
if (mAccount.getFolderPushMode() != FolderMode.NONE) {
|
||||
needsPushRestart |= displayModeChanged;
|
||||
needsPushRestart |= mIncomingChanged;
|
||||
}
|
||||
|
||||
if (needsRefresh && needsPushRestart) {
|
||||
MailService.actionReset(this, null);
|
||||
} else if (needsRefresh) {
|
||||
MailService.actionReschedulePoll(this, null);
|
||||
} else if (needsPushRestart) {
|
||||
MailService.actionRestartPushers(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (mIsPushCapable) {
|
||||
boolean needsPushRestart = mAccount.setFolderPushMode(Account.FolderMode.valueOf(mPushMode.getValue()));
|
||||
if (mAccount.getFolderPushMode() != FolderMode.NONE) {
|
||||
needsPushRestart |= displayModeChanged;
|
||||
needsPushRestart |= mIncomingChanged;
|
||||
}
|
||||
|
||||
if (needsRefresh && needsPushRestart) {
|
||||
MailService.actionReset(this, null);
|
||||
} else if (needsRefresh) {
|
||||
MailService.actionReschedulePoll(this, null);
|
||||
} else if (needsPushRestart) {
|
||||
MailService.actionRestartPushers(this, null);
|
||||
}
|
||||
}
|
||||
// TODO: refresh folder list here
|
||||
mAccount.save(Preferences.getPreferences(this));
|
||||
}
|
||||
@ -946,22 +913,33 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
mTrashFolder = (ListPreference)findPreference(PREFERENCE_TRASH_FOLDER);
|
||||
mTrashFolder.setEnabled(false);
|
||||
|
||||
if (!mIsMoveCapable) {
|
||||
PreferenceScreen foldersCategory =
|
||||
(PreferenceScreen) findPreference(PREFERENCE_CATEGORY_FOLDERS);
|
||||
foldersCategory.removePreference(mArchiveFolder);
|
||||
foldersCategory.removePreference(mSpamFolder);
|
||||
foldersCategory.removePreference(mDraftsFolder);
|
||||
foldersCategory.removePreference(mSentFolder);
|
||||
foldersCategory.removePreference(mTrashFolder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void res) {
|
||||
initListPreference(mAutoExpandFolder, mAccount.getAutoExpandFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mArchiveFolder, mAccount.getArchiveFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mDraftsFolder, mAccount.getDraftsFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mSentFolder, mAccount.getSentFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mSpamFolder, mAccount.getSpamFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mTrashFolder, mAccount.getTrashFolderName(), allFolderLabels, allFolderValues);
|
||||
mAutoExpandFolder.setEnabled(true);
|
||||
mArchiveFolder.setEnabled(true);
|
||||
mDraftsFolder.setEnabled(true);
|
||||
mSentFolder.setEnabled(true);
|
||||
mSpamFolder.setEnabled(true);
|
||||
mTrashFolder.setEnabled(true);
|
||||
if (mIsMoveCapable) {
|
||||
initListPreference(mArchiveFolder, mAccount.getArchiveFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mDraftsFolder, mAccount.getDraftsFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mSentFolder, mAccount.getSentFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mSpamFolder, mAccount.getSpamFolderName(), allFolderLabels, allFolderValues);
|
||||
initListPreference(mTrashFolder, mAccount.getTrashFolderName(), allFolderLabels, allFolderValues);
|
||||
mArchiveFolder.setEnabled(true);
|
||||
mSpamFolder.setEnabled(true);
|
||||
mDraftsFolder.setEnabled(true);
|
||||
mSentFolder.setEnabled(true);
|
||||
mTrashFolder.setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,6 +232,11 @@ public class AccountSetupBasics extends K9Activity
|
||||
mAccount.setSpamFolderName(getString(R.string.special_mailbox_name_spam));
|
||||
}
|
||||
mAccount.setSentFolderName(getString(R.string.special_mailbox_name_sent));
|
||||
if (incomingUri.toString().startsWith("imap")) {
|
||||
mAccount.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE);
|
||||
} else if (incomingUri.toString().startsWith("pop3")) {
|
||||
mAccount.setDeletePolicy(Account.DELETE_POLICY_NEVER);
|
||||
}
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, true);
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This really shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
@ -310,6 +315,13 @@ public class AccountSetupBasics extends K9Activity
|
||||
mAccount.setDraftsFolderName(getString(R.string.special_mailbox_name_drafts));
|
||||
mAccount.setTrashFolderName(getString(R.string.special_mailbox_name_trash));
|
||||
mAccount.setSentFolderName(getString(R.string.special_mailbox_name_sent));
|
||||
mAccount.setArchiveFolderName(getString(R.string.special_mailbox_name_archive));
|
||||
// Yahoo! has a special folder for Spam, called "Bulk Mail".
|
||||
if (domain.endsWith(".yahoo.com")) {
|
||||
mAccount.setSpamFolderName("Bulk Mail");
|
||||
} else {
|
||||
mAccount.setSpamFolderName(getString(R.string.special_mailbox_name_spam));
|
||||
}
|
||||
|
||||
AccountSetupAccountType.actionSelectAccountType(this, mAccount, mDefaultView.isChecked());
|
||||
finish();
|
||||
|
@ -117,6 +117,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
private static final String PENDING_COMMAND_MOVE_OR_COPY = "com.fsck.k9.MessagingController.moveOrCopy";
|
||||
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK = "com.fsck.k9.MessagingController.moveOrCopyBulk";
|
||||
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW = "com.fsck.k9.MessagingController.moveOrCopyBulkNew";
|
||||
private static final String PENDING_COMMAND_EMPTY_TRASH = "com.fsck.k9.MessagingController.emptyTrash";
|
||||
private static final String PENDING_COMMAND_SET_FLAG_BULK = "com.fsck.k9.MessagingController.setFlagBulk";
|
||||
private static final String PENDING_COMMAND_SET_FLAG = "com.fsck.k9.MessagingController.setFlag";
|
||||
@ -1845,9 +1846,7 @@ public class MessagingController implements Runnable {
|
||||
* right now, attachments will be left for later.
|
||||
*/
|
||||
|
||||
ArrayList<Part> viewables = new ArrayList<Part>();
|
||||
ArrayList<Part> attachments = new ArrayList<Part>();
|
||||
MimeUtility.collectParts(message, viewables, attachments);
|
||||
Set<Part> viewables = MimeUtility.collectTextParts(message);
|
||||
|
||||
/*
|
||||
* Now download the parts we're interested in storing.
|
||||
@ -2056,6 +2055,8 @@ public class MessagingController implements Runnable {
|
||||
} else if (PENDING_COMMAND_MARK_ALL_AS_READ.equals(command.command)) {
|
||||
processPendingMarkAllAsRead(command, account);
|
||||
} else if (PENDING_COMMAND_MOVE_OR_COPY_BULK.equals(command.command)) {
|
||||
processPendingMoveOrCopyOld2(command, account);
|
||||
} else if (PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW.equals(command.command)) {
|
||||
processPendingMoveOrCopy(command, account);
|
||||
} else if (PENDING_COMMAND_MOVE_OR_COPY.equals(command.command)) {
|
||||
processPendingMoveOrCopyOld(command, account);
|
||||
@ -2235,16 +2236,72 @@ public class MessagingController implements Runnable {
|
||||
return;
|
||||
}
|
||||
PendingCommand command = new PendingCommand();
|
||||
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK;
|
||||
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||
|
||||
int length = 3 + uids.length;
|
||||
command.arguments = new String[length];
|
||||
command.arguments[0] = srcFolder;
|
||||
command.arguments[1] = destFolder;
|
||||
command.arguments[2] = Boolean.toString(isCopy);
|
||||
System.arraycopy(uids, 0, command.arguments, 3, uids.length);
|
||||
command.arguments[3] = Boolean.toString(false);
|
||||
System.arraycopy(uids, 0, command.arguments, 4, uids.length);
|
||||
queuePendingCommand(account, command);
|
||||
}
|
||||
|
||||
private void queueMoveOrCopy(Account account, String srcFolder, String destFolder, boolean isCopy, String uids[], Map<String, String> uidMap) {
|
||||
if (uidMap == null || uidMap.isEmpty()) {
|
||||
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, uids);
|
||||
} else {
|
||||
if (account.getErrorFolderName().equals(srcFolder)) {
|
||||
return;
|
||||
}
|
||||
PendingCommand command = new PendingCommand();
|
||||
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||
|
||||
int length = 4 + uidMap.keySet().size() + uidMap.values().size();
|
||||
command.arguments = new String[length];
|
||||
command.arguments[0] = srcFolder;
|
||||
command.arguments[1] = destFolder;
|
||||
command.arguments[2] = Boolean.toString(isCopy);
|
||||
command.arguments[3] = Boolean.toString(true);
|
||||
System.arraycopy(uidMap.keySet().toArray(), 0, command.arguments, 4, uidMap.keySet().size());
|
||||
System.arraycopy(uidMap.values().toArray(), 0, command.arguments, 4 + uidMap.keySet().size(), uidMap.values().size());
|
||||
queuePendingCommand(account, command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert pending command to new format and call
|
||||
* {@link #processPendingMoveOrCopy(PendingCommand, Account)}.
|
||||
*
|
||||
* <p>
|
||||
* TODO: This method is obsolete and is only for transition from K-9 4.0 to K-9 4.2
|
||||
* Eventually, it should be removed.
|
||||
* </p>
|
||||
*
|
||||
* @param command
|
||||
* Pending move/copy command in old format.
|
||||
* @param account
|
||||
* The account the pending command belongs to.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
private void processPendingMoveOrCopyOld2(PendingCommand command, Account account)
|
||||
throws MessagingException {
|
||||
PendingCommand newCommand = new PendingCommand();
|
||||
int len = command.arguments.length;
|
||||
newCommand.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||
newCommand.arguments = new String[len + 1];
|
||||
newCommand.arguments[0] = command.arguments[0];
|
||||
newCommand.arguments[1] = command.arguments[1];
|
||||
newCommand.arguments[2] = command.arguments[2];
|
||||
newCommand.arguments[3] = Boolean.toString(false);
|
||||
System.arraycopy(command.arguments, 3, newCommand.arguments, 4, len - 3);
|
||||
|
||||
processPendingMoveOrCopy(newCommand, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a pending trash message command.
|
||||
*
|
||||
@ -2256,6 +2313,7 @@ public class MessagingController implements Runnable {
|
||||
throws MessagingException {
|
||||
Folder remoteSrcFolder = null;
|
||||
Folder remoteDestFolder = null;
|
||||
LocalFolder localDestFolder = null;
|
||||
try {
|
||||
String srcFolder = command.arguments[0];
|
||||
if (account.getErrorFolderName().equals(srcFolder)) {
|
||||
@ -2263,14 +2321,42 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
String destFolder = command.arguments[1];
|
||||
String isCopyS = command.arguments[2];
|
||||
String hasNewUidsS = command.arguments[3];
|
||||
|
||||
boolean hasNewUids = false;
|
||||
if (hasNewUidsS != null) {
|
||||
hasNewUids = Boolean.parseBoolean(hasNewUidsS);
|
||||
}
|
||||
|
||||
Store remoteStore = account.getRemoteStore();
|
||||
remoteSrcFolder = remoteStore.getFolder(srcFolder);
|
||||
|
||||
Store localStore = account.getLocalStore();
|
||||
localDestFolder = (LocalFolder) localStore.getFolder(destFolder);
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
for (int i = 3; i < command.arguments.length; i++) {
|
||||
String uid = command.arguments[i];
|
||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
||||
messages.add(remoteSrcFolder.getMessage(uid));
|
||||
|
||||
/*
|
||||
* We split up the localUidMap into two parts while sending the command, here we assemble it back.
|
||||
*/
|
||||
Map<String, String> localUidMap = new HashMap<String, String>();
|
||||
if (hasNewUids) {
|
||||
int offset = (command.arguments.length - 4) / 2;
|
||||
|
||||
for (int i = 4; i < 4 + offset; i++) {
|
||||
localUidMap.put(command.arguments[i], command.arguments[i + offset]);
|
||||
|
||||
String uid = command.arguments[i];
|
||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
||||
messages.add(remoteSrcFolder.getMessage(uid));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
for (int i = 4; i < command.arguments.length; i++) {
|
||||
String uid = command.arguments[i];
|
||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
||||
messages.add(remoteSrcFolder.getMessage(uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2291,6 +2377,8 @@ public class MessagingController implements Runnable {
|
||||
Log.d(K9.LOG_TAG, "processingPendingMoveOrCopy: source folder = " + srcFolder
|
||||
+ ", " + messages.size() + " messages, destination folder = " + destFolder + ", isCopy = " + isCopy);
|
||||
|
||||
Map <String, String> remoteUidMap = null;
|
||||
|
||||
if (!isCopy && destFolder.equals(account.getTrashFolderName())) {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "processingPendingMoveOrCopy doing special case for deleting message");
|
||||
@ -2304,9 +2392,9 @@ public class MessagingController implements Runnable {
|
||||
remoteDestFolder = remoteStore.getFolder(destFolder);
|
||||
|
||||
if (isCopy) {
|
||||
remoteSrcFolder.copyMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
||||
remoteUidMap = remoteSrcFolder.copyMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
||||
} else {
|
||||
remoteSrcFolder.moveMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
||||
remoteUidMap = remoteSrcFolder.moveMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
||||
}
|
||||
}
|
||||
if (!isCopy && Account.EXPUNGE_IMMEDIATELY.equals(account.getExpungePolicy())) {
|
||||
@ -2315,12 +2403,32 @@ public class MessagingController implements Runnable {
|
||||
|
||||
remoteSrcFolder.expunge();
|
||||
}
|
||||
|
||||
/*
|
||||
* This next part is used to bring the local UIDs of the local destination folder
|
||||
* upto speed with the remote UIDs of remote destionation folder.
|
||||
*/
|
||||
if (!localUidMap.isEmpty() && remoteUidMap != null && !remoteUidMap.isEmpty()) {
|
||||
Set<String> remoteSrcUids = remoteUidMap.keySet();
|
||||
Iterator<String> remoteSrcUidsIterator = remoteSrcUids.iterator();
|
||||
|
||||
while (remoteSrcUidsIterator.hasNext()) {
|
||||
String remoteSrcUid = remoteSrcUidsIterator.next();
|
||||
String localDestUid = localUidMap.get(remoteSrcUid);
|
||||
String newUid = remoteUidMap.get(remoteSrcUid);
|
||||
|
||||
Message localDestMessage = localDestFolder.getMessage(localDestUid);
|
||||
localDestMessage.setUid(newUid);
|
||||
localDestFolder.changeUid((LocalMessage)localDestMessage);
|
||||
for (MessagingListener l : getListeners()) {
|
||||
l.messageUidChanged(account, destFolder, localDestUid, newUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closeFolder(remoteSrcFolder);
|
||||
closeFolder(remoteDestFolder);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void queueSetFlag(final Account account, final String folderName, final String newState, final String flag, final String[] uids) {
|
||||
@ -2673,65 +2781,117 @@ public class MessagingController implements Runnable {
|
||||
@Override
|
||||
public void act(final Account account, final Folder folder,
|
||||
final List<Message> messages) {
|
||||
String[] uids = new String[messages.size()];
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
setFlag(account, folder.getRemoteName(), uids, flag, newState);
|
||||
setFlag(account, folder.getRemoteName(), messages.toArray(EMPTY_MESSAGE_ARRAY), flag,
|
||||
newState);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void setFlag(
|
||||
final Account account,
|
||||
final String folderName,
|
||||
final String[] uids,
|
||||
final Flag flag,
|
||||
final boolean newState) {
|
||||
// TODO: put this into the background, but right now that causes odd behavior
|
||||
// because the FolderMessageList doesn't have its own cache of the flag states
|
||||
/**
|
||||
* Set or remove a flag for a set of messages in a specific folder.
|
||||
*
|
||||
* <p>
|
||||
* The {@link Message} objects passed in are updated to reflect the new flag state.
|
||||
* </p>
|
||||
*
|
||||
* @param account
|
||||
* The account the folder containing the messages belongs to.
|
||||
* @param folderName
|
||||
* The name of the folder.
|
||||
* @param messages
|
||||
* The messages to change the flag for.
|
||||
* @param flag
|
||||
* The flag to change.
|
||||
* @param newState
|
||||
* {@code true}, if the flag should be set. {@code false} if it should be removed.
|
||||
*/
|
||||
public void setFlag(Account account, String folderName, Message[] messages, Flag flag,
|
||||
boolean newState) {
|
||||
// TODO: Put this into the background, but right now some callers depend on the message
|
||||
// objects being modified right after this method returns.
|
||||
Folder localFolder = null;
|
||||
try {
|
||||
Store localStore = account.getLocalStore();
|
||||
localFolder = localStore.getFolder(folderName);
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
ArrayList<Message> messages = new ArrayList<Message>();
|
||||
for (String uid : uids) {
|
||||
// Allows for re-allowing sending of messages that could not be sent
|
||||
if (flag == Flag.FLAGGED && !newState
|
||||
&& uid != null
|
||||
&& account.getOutboxFolderName().equals(folderName)) {
|
||||
sendCount.remove(uid);
|
||||
}
|
||||
Message msg = localFolder.getMessage(uid);
|
||||
if (msg != null) {
|
||||
messages.add(msg);
|
||||
|
||||
// Allows for re-allowing sending of messages that could not be sent
|
||||
if (flag == Flag.FLAGGED && !newState &&
|
||||
account.getOutboxFolderName().equals(folderName)) {
|
||||
for (Message message : messages) {
|
||||
String uid = message.getUid();
|
||||
if (uid != null) {
|
||||
sendCount.remove(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
localFolder.setFlags(messages.toArray(EMPTY_MESSAGE_ARRAY), new Flag[] {flag}, newState);
|
||||
|
||||
// Update the messages in the local store
|
||||
localFolder.setFlags(messages, new Flag[] {flag}, newState);
|
||||
|
||||
for (MessagingListener l : getListeners()) {
|
||||
l.folderStatusChanged(account, folderName, localFolder.getUnreadMessageCount());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle the remote side
|
||||
*/
|
||||
|
||||
// The error folder is always a local folder
|
||||
// TODO: Skip the remote part for all local-only folders
|
||||
if (account.getErrorFolderName().equals(folderName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] uids = new String[messages.length];
|
||||
for (int i = 0, end = uids.length; i < end; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
}
|
||||
|
||||
queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids);
|
||||
processPendingCommands(account);
|
||||
} catch (MessagingException me) {
|
||||
addErrorMessage(account, null, me);
|
||||
|
||||
throw new RuntimeException(me);
|
||||
} finally {
|
||||
closeFolder(localFolder);
|
||||
}
|
||||
}//setMesssageFlag
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or remove a flag for a message referenced by message UID.
|
||||
*
|
||||
* @param account
|
||||
* The account the folder containing the message belongs to.
|
||||
* @param folderName
|
||||
* The name of the folder.
|
||||
* @param uid
|
||||
* The UID of the message to change the flag for.
|
||||
* @param flag
|
||||
* The flag to change.
|
||||
* @param newState
|
||||
* {@code true}, if the flag should be set. {@code false} if it should be removed.
|
||||
*/
|
||||
public void setFlag(Account account, String folderName, String uid, Flag flag,
|
||||
boolean newState) {
|
||||
Folder localFolder = null;
|
||||
try {
|
||||
LocalStore localStore = account.getLocalStore();
|
||||
localFolder = localStore.getFolder(folderName);
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
|
||||
Message message = localFolder.getMessage(uid);
|
||||
if (message != null) {
|
||||
setFlag(account, folderName, new Message[] { message }, flag, newState);
|
||||
}
|
||||
} catch (MessagingException me) {
|
||||
addErrorMessage(account, null, me);
|
||||
throw new RuntimeException(me);
|
||||
} finally {
|
||||
closeFolder(localFolder);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearAllPending(final Account account) {
|
||||
try {
|
||||
@ -2916,9 +3076,7 @@ public class MessagingController implements Runnable {
|
||||
try {
|
||||
LocalStore localStore = account.getLocalStore();
|
||||
|
||||
ArrayList<Part> viewables = new ArrayList<Part>();
|
||||
ArrayList<Part> attachments = new ArrayList<Part>();
|
||||
MimeUtility.collectParts(message, viewables, attachments);
|
||||
List<Part> attachments = MimeUtility.collectAttachments(message);
|
||||
for (Part attachment : attachments) {
|
||||
attachment.setBody(null);
|
||||
}
|
||||
@ -3385,6 +3543,7 @@ public class MessagingController implements Runnable {
|
||||
private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder, final Message[] inMessages,
|
||||
final String destFolder, final boolean isCopy, MessagingListener listener) {
|
||||
try {
|
||||
Map<String, String> uidMap = new HashMap<String, String>();
|
||||
Store localStore = account.getLocalStore();
|
||||
Store remoteStore = account.getRemoteStore();
|
||||
if (!isCopy && (!remoteStore.isMoveCapable() || !localStore.isMoveCapable())) {
|
||||
@ -3397,12 +3556,17 @@ public class MessagingController implements Runnable {
|
||||
Folder localSrcFolder = localStore.getFolder(srcFolder);
|
||||
Folder localDestFolder = localStore.getFolder(destFolder);
|
||||
|
||||
boolean unreadCountAffected = false;
|
||||
List<String> uids = new LinkedList<String>();
|
||||
for (Message message : inMessages) {
|
||||
String uid = message.getUid();
|
||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
||||
uids.add(uid);
|
||||
}
|
||||
|
||||
if (!unreadCountAffected && !message.isSet(Flag.SEEN)) {
|
||||
unreadCountAffected = true;
|
||||
}
|
||||
}
|
||||
|
||||
Message[] messages = localSrcFolder.getMessages(uids.toArray(EMPTY_STRING_ARRAY), null);
|
||||
@ -3422,9 +3586,18 @@ public class MessagingController implements Runnable {
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
localSrcFolder.fetch(messages, fp, null);
|
||||
localSrcFolder.copyMessages(messages, localDestFolder);
|
||||
uidMap = localSrcFolder.copyMessages(messages, localDestFolder);
|
||||
|
||||
if (unreadCountAffected) {
|
||||
// If this copy operation changes the unread count in the destination
|
||||
// folder, notify the listeners.
|
||||
int unreadMessageCount = localDestFolder.getUnreadMessageCount();
|
||||
for (MessagingListener l : getListeners()) {
|
||||
l.folderStatusChanged(account, destFolder, unreadMessageCount);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
localSrcFolder.moveMessages(messages, localDestFolder);
|
||||
uidMap = localSrcFolder.moveMessages(messages, localDestFolder);
|
||||
for (Map.Entry<String, Message> entry : origUidMap.entrySet()) {
|
||||
String origUid = entry.getKey();
|
||||
Message message = entry.getValue();
|
||||
@ -3433,9 +3606,20 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
unsuppressMessage(account, srcFolder, origUid);
|
||||
}
|
||||
|
||||
if (unreadCountAffected) {
|
||||
// If this move operation changes the unread count, notify the listeners
|
||||
// that the unread count changed in both the source and destination folder.
|
||||
int unreadMessageCountSrc = localSrcFolder.getUnreadMessageCount();
|
||||
int unreadMessageCountDest = localDestFolder.getUnreadMessageCount();
|
||||
for (MessagingListener l : getListeners()) {
|
||||
l.folderStatusChanged(account, srcFolder, unreadMessageCountSrc);
|
||||
l.folderStatusChanged(account, destFolder, unreadMessageCountDest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidMap.keySet().toArray(EMPTY_STRING_ARRAY));
|
||||
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidMap.keySet().toArray(EMPTY_STRING_ARRAY), uidMap);
|
||||
}
|
||||
|
||||
processPendingCommands(account);
|
||||
@ -3465,9 +3649,11 @@ public class MessagingController implements Runnable {
|
||||
localFolder = localStore.getFolder(account.getDraftsFolderName());
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
String uid = localFolder.getMessageUidById(id);
|
||||
Message message = localFolder.getMessage(uid);
|
||||
if (message != null) {
|
||||
deleteMessages(new Message[] { message }, null);
|
||||
if (uid != null) {
|
||||
Message message = localFolder.getMessage(uid);
|
||||
if (message != null) {
|
||||
deleteMessages(new Message[] { message }, null);
|
||||
}
|
||||
}
|
||||
} catch (MessagingException me) {
|
||||
addErrorMessage(account, null, me);
|
||||
@ -3513,6 +3699,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
Store localStore = account.getLocalStore();
|
||||
localFolder = localStore.getFolder(folder);
|
||||
Map<String, String> uidMap = null;
|
||||
if (folder.equals(account.getTrashFolderName()) || K9.FOLDER_NONE.equals(account.getTrashFolderName())) {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "Deleting messages in trash folder or trash set to -None-, not copying");
|
||||
@ -3527,7 +3714,7 @@ public class MessagingController implements Runnable {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "Deleting messages in normal folder, moving");
|
||||
|
||||
localFolder.moveMessages(messages, localTrashFolder);
|
||||
uidMap = localFolder.moveMessages(messages, localTrashFolder);
|
||||
|
||||
}
|
||||
}
|
||||
@ -3560,7 +3747,7 @@ public class MessagingController implements Runnable {
|
||||
if (folder.equals(account.getTrashFolderName())) {
|
||||
queueSetFlag(account, folder, Boolean.toString(true), Flag.DELETED.toString(), uids);
|
||||
} else {
|
||||
queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids);
|
||||
queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids, uidMap);
|
||||
}
|
||||
processPendingCommands(account);
|
||||
} else if (account.getDeletePolicy() == Account.DELETE_POLICY_MARK_AS_READ) {
|
||||
@ -3622,12 +3809,14 @@ public class MessagingController implements Runnable {
|
||||
putBackground("emptyTrash", listener, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Folder localFolder = null;
|
||||
LocalFolder localFolder = null;
|
||||
try {
|
||||
Store localStore = account.getLocalStore();
|
||||
localFolder = localStore.getFolder(account.getTrashFolderName());
|
||||
localFolder = (LocalFolder) localStore.getFolder(account.getTrashFolderName());
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
localFolder.setFlags(new Flag[] { Flag.DELETED }, true);
|
||||
localFolder.setUnreadMessageCount(0);
|
||||
localFolder.setFlaggedMessageCount(0);
|
||||
|
||||
for (MessagingListener l : getListeners()) {
|
||||
l.emptyTrashCompleted(account);
|
||||
|
@ -12,191 +12,154 @@ import com.fsck.k9.mail.Part;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Defines the interface that MessagingController will use to callback to requesters. This class
|
||||
* is defined as non-abstract so that someone who wants to receive only a few messages can
|
||||
* do so without implementing the entire interface. It is highly recommended that users of
|
||||
* this interface use the @Override annotation in their implementations to avoid being caught by
|
||||
* Defines the interface that {@link MessagingController} will use to callback to requesters.
|
||||
*
|
||||
* <p>
|
||||
* This class is defined as non-abstract so that someone who wants to receive only a few messages
|
||||
* can do so without implementing the entire interface. It is highly recommended that users of this
|
||||
* interface use the {@code @Override} annotation in their implementations to avoid being caught by
|
||||
* changes in this class.
|
||||
* </p>
|
||||
*/
|
||||
public class MessagingListener {
|
||||
public void searchStats(AccountStats stats) {}
|
||||
|
||||
public void accountStatusChanged(BaseAccount account, AccountStats stats) {
|
||||
}
|
||||
|
||||
public void accountSizeChanged(Account account, long oldSize, long newSize) {
|
||||
}
|
||||
public void accountStatusChanged(BaseAccount account, AccountStats stats) {}
|
||||
|
||||
public void listFoldersStarted(Account account) {
|
||||
}
|
||||
public void accountSizeChanged(Account account, long oldSize, long newSize) {}
|
||||
|
||||
public void listFolders(Account account, Folder[] folders) {
|
||||
}
|
||||
|
||||
public void listFoldersFailed(Account account, String message) {
|
||||
}
|
||||
public void listFoldersStarted(Account account) {}
|
||||
|
||||
public void listFoldersFinished(Account account) {
|
||||
}
|
||||
public void listFolders(Account account, Folder[] folders) {}
|
||||
|
||||
public void listLocalMessagesStarted(Account account, String folder) {
|
||||
}
|
||||
public void listFoldersFinished(Account account) {}
|
||||
|
||||
public void listLocalMessages(Account account, String folder, Message[] messages) {
|
||||
}
|
||||
public void listFoldersFailed(Account account, String message) {}
|
||||
|
||||
public void listLocalMessagesAddMessages(Account account, String folder, List<Message> messages) {
|
||||
}
|
||||
|
||||
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {
|
||||
}
|
||||
public void listLocalMessagesStarted(Account account, String folder) {}
|
||||
|
||||
public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {
|
||||
}
|
||||
public void listLocalMessages(Account account, String folder, Message[] messages) {}
|
||||
|
||||
public void listLocalMessagesFailed(Account account, String folder, String message) {
|
||||
}
|
||||
public void listLocalMessagesAddMessages(Account account, String folder,
|
||||
List<Message> messages) {}
|
||||
|
||||
public void listLocalMessagesFinished(Account account, String folder) {
|
||||
}
|
||||
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {}
|
||||
|
||||
public void synchronizeMailboxStarted(Account account, String folder) {
|
||||
}
|
||||
public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {}
|
||||
|
||||
public void synchronizeMailboxHeadersStarted(Account account, String folder) {
|
||||
}
|
||||
public void listLocalMessagesFinished(Account account, String folder) {}
|
||||
|
||||
public void synchronizeMailboxHeadersProgress(Account account, String folder, int completed, int total) {
|
||||
}
|
||||
public void listLocalMessagesFailed(Account account, String folder, String message) {}
|
||||
|
||||
|
||||
public void synchronizeMailboxStarted(Account account, String folder) {}
|
||||
|
||||
public void synchronizeMailboxHeadersStarted(Account account, String folder) {}
|
||||
|
||||
public void synchronizeMailboxHeadersProgress(Account account, String folder,
|
||||
int completed, int total) {}
|
||||
|
||||
public void synchronizeMailboxHeadersFinished(Account account, String folder,
|
||||
int totalMessagesInMailbox, int numNewMessages) {
|
||||
}
|
||||
int totalMessagesInMailbox, int numNewMessages) {}
|
||||
|
||||
public void synchronizeMailboxProgress(Account account, String folder, int completed,
|
||||
int total) {}
|
||||
|
||||
public void synchronizeMailboxProgress(Account account, String folder, int completed, int total)
|
||||
{}
|
||||
public void synchronizeMailboxNewMessage(Account account, String folder, Message message) {}
|
||||
|
||||
public void synchronizeMailboxNewMessage(Account account, String folder, Message message) {
|
||||
}
|
||||
public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder,
|
||||
Message message) {}
|
||||
|
||||
public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) {
|
||||
}
|
||||
|
||||
public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
|
||||
}
|
||||
public void synchronizeMailboxRemovedMessage(Account account, String folder,
|
||||
Message message) {}
|
||||
|
||||
public void synchronizeMailboxFinished(Account account, String folder,
|
||||
int totalMessagesInMailbox, int numNewMessages) {
|
||||
}
|
||||
int totalMessagesInMailbox, int numNewMessages) {}
|
||||
|
||||
public void synchronizeMailboxFailed(Account account, String folder,
|
||||
String message) {
|
||||
}
|
||||
public void synchronizeMailboxFailed(Account account, String folder, String message) {}
|
||||
|
||||
public void loadMessageForViewStarted(Account account, String folder, String uid) {
|
||||
}
|
||||
|
||||
public void loadMessageForViewStarted(Account account, String folder, String uid) {}
|
||||
|
||||
public void loadMessageForViewHeadersAvailable(Account account, String folder, String uid,
|
||||
Message message) {
|
||||
}
|
||||
Message message) {}
|
||||
|
||||
public void loadMessageForViewBodyAvailable(Account account, String folder, String uid,
|
||||
Message message) {
|
||||
}
|
||||
Message message) {}
|
||||
|
||||
public void loadMessageForViewFinished(Account account, String folder, String uid,
|
||||
Message message) {
|
||||
}
|
||||
Message message) {}
|
||||
|
||||
public void loadMessageForViewFailed(Account account, String folder, String uid, Throwable t) {
|
||||
}
|
||||
public void loadMessageForViewFailed(Account account, String folder, String uid,
|
||||
Throwable t) {}
|
||||
|
||||
/**
|
||||
* Called when a message for view has been fully displayed on the screen.
|
||||
*/
|
||||
public void messageViewFinished() {}
|
||||
|
||||
public void checkMailStarted(Context context, Account account) {
|
||||
}
|
||||
|
||||
public void checkMailFinished(Context context, Account account) {
|
||||
}
|
||||
public void checkMailStarted(Context context, Account account) {}
|
||||
|
||||
public void checkMailFailed(Context context, Account account, String reason) {
|
||||
}
|
||||
public void checkMailFinished(Context context, Account account) {}
|
||||
|
||||
public void sendPendingMessagesStarted(Account account) {
|
||||
}
|
||||
public void checkMailFailed(Context context, Account account, String reason) {}
|
||||
|
||||
public void sendPendingMessagesCompleted(Account account) {
|
||||
}
|
||||
|
||||
public void sendPendingMessagesFailed(Account account) {
|
||||
}
|
||||
public void sendPendingMessagesStarted(Account account) {}
|
||||
|
||||
public void messageDeleted(Account account, String folder, Message message) {
|
||||
public void sendPendingMessagesCompleted(Account account) {}
|
||||
|
||||
}
|
||||
public void emptyTrashCompleted(Account account) {
|
||||
}
|
||||
public void sendPendingMessagesFailed(Account account) {}
|
||||
|
||||
public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) {
|
||||
}
|
||||
|
||||
public void folderStatusChanged(Account account, String folderName) {
|
||||
}
|
||||
public void emptyTrashCompleted(Account account) {}
|
||||
|
||||
public void systemStatusChanged() {
|
||||
}
|
||||
|
||||
public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {
|
||||
public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) {}
|
||||
|
||||
}
|
||||
|
||||
public void setPushActive(Account account, String folderName, boolean enabled) {
|
||||
public void systemStatusChanged() {}
|
||||
|
||||
}
|
||||
|
||||
public void loadAttachmentStarted(
|
||||
Account account,
|
||||
Message message,
|
||||
Part part,
|
||||
Object tag,
|
||||
boolean requiresDownload) {
|
||||
}
|
||||
public void messageDeleted(Account account, String folder, Message message) {}
|
||||
|
||||
public void loadAttachmentFinished(
|
||||
Account account,
|
||||
Message message,
|
||||
Part part,
|
||||
Object tag) {
|
||||
}
|
||||
public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {}
|
||||
|
||||
public void loadAttachmentFailed(
|
||||
Account account,
|
||||
Message message,
|
||||
Part part,
|
||||
Object tag,
|
||||
String reason) {
|
||||
}
|
||||
|
||||
public void setPushActive(Account account, String folderName, boolean enabled) {}
|
||||
|
||||
|
||||
public void loadAttachmentStarted(Account account, Message message, Part part, Object tag,
|
||||
boolean requiresDownload) {}
|
||||
|
||||
public void loadAttachmentFinished(Account account, Message message, Part part, Object tag) {}
|
||||
|
||||
public void loadAttachmentFailed(Account account, Message message, Part part, Object tag,
|
||||
String reason) {}
|
||||
|
||||
|
||||
|
||||
public void pendingCommandStarted(Account account, String commandTitle) {}
|
||||
|
||||
public void pendingCommandsProcessing(Account account) {}
|
||||
public void pendingCommandsFinished(Account account) {}
|
||||
public void pendingCommandStarted(Account account, String commandTitle)
|
||||
{}
|
||||
|
||||
public void pendingCommandCompleted(Account account, String commandTitle)
|
||||
{}
|
||||
public void pendingCommandCompleted(Account account, String commandTitle) {}
|
||||
|
||||
public void pendingCommandsFinished(Account account) {}
|
||||
|
||||
|
||||
/**
|
||||
* General notification messages subclasses can override to be notified that the controller
|
||||
* has completed a command. This is useful for turning off progress indicators that may have
|
||||
* been left over from previous commands.
|
||||
* @param moreCommandsToRun True if the controller will continue on to another command
|
||||
* immediately.
|
||||
*
|
||||
* @param moreCommandsToRun
|
||||
* {@code true} if the controller will continue on to another command immediately.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public void controllerCommandCompleted(boolean moreCommandsToRun) {
|
||||
|
||||
}
|
||||
public void controllerCommandCompleted(boolean moreCommandsToRun) {}
|
||||
}
|
||||
|
@ -125,19 +125,41 @@ public class HtmlConverter {
|
||||
|
||||
private static final int MAX_SMART_HTMLIFY_MESSAGE_LENGTH = 1024 * 256 ;
|
||||
|
||||
public static final String getHtmlHeader() {
|
||||
return "<html><head/><body>";
|
||||
}
|
||||
|
||||
public static final String getHtmlFooter() {
|
||||
return "</body></html>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Naively convert a text string into an HTML document. This method avoids using regular expressions on the entire
|
||||
* message body to save memory.
|
||||
* @param text Plain text string.
|
||||
* Naively convert a text string into an HTML document.
|
||||
*
|
||||
* <p>
|
||||
* This method avoids using regular expressions on the entire message body to save memory.
|
||||
* </p>
|
||||
*
|
||||
* @param text
|
||||
* Plain text string.
|
||||
* @param useHtmlTag
|
||||
* If {@code true} this method adds headers and footers to create a proper HTML
|
||||
* document.
|
||||
*
|
||||
* @return HTML string.
|
||||
*/
|
||||
private static String simpleTextToHtml(String text) {
|
||||
private static String simpleTextToHtml(String text, boolean useHtmlTag) {
|
||||
// Encode HTML entities to make sure we don't display something evil.
|
||||
text = TextUtils.htmlEncode(text);
|
||||
|
||||
StringReader reader = new StringReader(text);
|
||||
StringBuilder buff = new StringBuilder(text.length() + TEXT_TO_HTML_EXTRA_BUFFER_LENGTH);
|
||||
buff.append("<html><head/><body>");
|
||||
|
||||
if (useHtmlTag) {
|
||||
buff.append(getHtmlHeader());
|
||||
}
|
||||
|
||||
buff.append(htmlifyMessageHeader());
|
||||
|
||||
int c;
|
||||
try {
|
||||
@ -159,25 +181,39 @@ public class HtmlConverter {
|
||||
Log.e(K9.LOG_TAG, "Could not read string to convert text to HTML:", e);
|
||||
}
|
||||
|
||||
buff.append("</body></html>");
|
||||
buff.append(htmlifyMessageFooter());
|
||||
|
||||
if (useHtmlTag) {
|
||||
buff.append(getHtmlFooter());
|
||||
}
|
||||
|
||||
return buff.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a text string into an HTML document. Attempts to do smart replacement for large
|
||||
* documents to prevent OOM errors. This method adds headers and footers to create a proper HTML
|
||||
* document. To convert to a fragment, use {@link #textToHtmlFragment(String)}.
|
||||
* @param text Plain text string.
|
||||
* Convert a text string into an HTML document.
|
||||
*
|
||||
* <p>
|
||||
* Attempts to do smart replacement for large documents to prevent OOM errors. This method
|
||||
* optionally adds headers and footers to create a proper HTML document. To convert to a
|
||||
* fragment, use {@link #textToHtmlFragment(String)}.
|
||||
* </p>
|
||||
*
|
||||
* @param text
|
||||
* Plain text string.
|
||||
* @param useHtmlTag
|
||||
* If {@code true} this method adds headers and footers to create a proper HTML
|
||||
* document.
|
||||
*
|
||||
* @return HTML string.
|
||||
*/
|
||||
public static String textToHtml(String text) {
|
||||
public static String textToHtml(String text, boolean useHtmlTag) {
|
||||
// Our HTMLification code is somewhat memory intensive
|
||||
// and was causing lots of OOM errors on the market
|
||||
// if the message is big and plain text, just do
|
||||
// a trivial htmlification
|
||||
if (text.length() > MAX_SMART_HTMLIFY_MESSAGE_LENGTH) {
|
||||
return simpleTextToHtml(text);
|
||||
return simpleTextToHtml(text, useHtmlTag);
|
||||
}
|
||||
StringReader reader = new StringReader(text);
|
||||
StringBuilder buff = new StringBuilder(text.length() + TEXT_TO_HTML_EXTRA_BUFFER_LENGTH);
|
||||
@ -221,11 +257,19 @@ public class HtmlConverter {
|
||||
text = text.replaceAll("(?m)(\r\n|\n|\r){4,}", "\n\n");
|
||||
|
||||
StringBuffer sb = new StringBuffer(text.length() + TEXT_TO_HTML_EXTRA_BUFFER_LENGTH);
|
||||
sb.append("<html><head></head><body>");
|
||||
|
||||
if (useHtmlTag) {
|
||||
sb.append(getHtmlHeader());
|
||||
}
|
||||
|
||||
sb.append(htmlifyMessageHeader());
|
||||
linkifyText(text, sb);
|
||||
sb.append(htmlifyMessageFooter());
|
||||
sb.append("</body></html>");
|
||||
|
||||
if (useHtmlTag) {
|
||||
sb.append(getHtmlFooter());
|
||||
}
|
||||
|
||||
text = sb.toString();
|
||||
|
||||
return text;
|
||||
|
@ -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;
|
||||
@ -102,11 +103,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<String, String> appendMessages(Message[] messages) throws MessagingException;
|
||||
|
||||
public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {}
|
||||
public Map<String, String> copyMessages(Message[] msgs, Folder folder) throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void moveMessages(Message[] msgs, Folder folder) throws MessagingException {}
|
||||
public Map<String, String> moveMessages(Message[] msgs, Folder folder) throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {
|
||||
for (Message message : msgs) {
|
||||
|
@ -1,9 +1,14 @@
|
||||
|
||||
package com.fsck.k9.mail.internet;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.helper.HtmlConverter;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.Message.RecipientType;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.james.mime4j.codec.Base64InputStream;
|
||||
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
|
||||
@ -12,7 +17,11 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.IllegalCharsetNameException;
|
||||
@ -23,6 +32,9 @@ public class MimeUtility {
|
||||
|
||||
public static final String K9_SETTINGS_MIME_TYPE = "application/x-k9settings";
|
||||
|
||||
private static final String TEXT_DIVIDER =
|
||||
"------------------------------------------------------------------------";
|
||||
|
||||
/*
|
||||
* http://www.w3schools.com/media/media_mimeref.asp
|
||||
* +
|
||||
@ -1100,49 +1112,867 @@ public class MimeUtility {
|
||||
return tempBody;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An unfortunately named method that makes decisions about a Part (usually a Message)
|
||||
* as to which of it's children will be "viewable" and which will be attachments.
|
||||
* The method recursively sorts the viewables and attachments into seperate
|
||||
* lists for further processing.
|
||||
* @param part
|
||||
* @param viewables
|
||||
* @param attachments
|
||||
* @throws MessagingException
|
||||
* Empty base class for the class hierarchy used by
|
||||
* {@link MimeUtility#extractTextAndAttachments(Context, Message)}.
|
||||
*
|
||||
* @see Text
|
||||
* @see Html
|
||||
* @see MessageHeader
|
||||
* @see Alternative
|
||||
*/
|
||||
public static void collectParts(Part part, ArrayList<Part> viewables,
|
||||
ArrayList<Part> attachments) throws MessagingException {
|
||||
/*
|
||||
* If the part is Multipart but not alternative it's either mixed or
|
||||
* something we don't know about, which means we treat it as mixed
|
||||
* per the spec. We just process it's pieces recursively.
|
||||
static abstract class Viewable { /* empty */ }
|
||||
|
||||
/**
|
||||
* Class representing textual parts of a message that aren't marked as attachments.
|
||||
*
|
||||
* @see MimeUtility#isPartTextualBody(Part)
|
||||
*/
|
||||
static abstract class Textual extends Viewable {
|
||||
private Part mPart;
|
||||
|
||||
public Textual(Part part) {
|
||||
mPart = part;
|
||||
}
|
||||
|
||||
public Part getPart() {
|
||||
return mPart;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a {@code text/plain} part of a message.
|
||||
*/
|
||||
static class Text extends Textual {
|
||||
public Text(Part part) {
|
||||
super(part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a {@code text/html} part of a message.
|
||||
*/
|
||||
static class Html extends Textual {
|
||||
public Html(Part part) {
|
||||
super(part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a {@code message/rfc822} part of a message.
|
||||
*
|
||||
* <p>
|
||||
* This is used to extract basic header information when the message contents are displayed
|
||||
* inline.
|
||||
* </p>
|
||||
*/
|
||||
static class MessageHeader extends Viewable {
|
||||
private Part mContainerPart;
|
||||
private Message mMessage;
|
||||
|
||||
public MessageHeader(Part containerPart, Message message) {
|
||||
mContainerPart = containerPart;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
public Part getContainerPart() {
|
||||
return mContainerPart;
|
||||
}
|
||||
|
||||
public Message getMessage() {
|
||||
return mMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a {@code multipart/alternative} part of a message.
|
||||
*
|
||||
* <p>
|
||||
* Only relevant {@code text/plain} and {@code text/html} children are stored in this container
|
||||
* class.
|
||||
* </p>
|
||||
*/
|
||||
static class Alternative extends Viewable {
|
||||
private List<Viewable> mText;
|
||||
private List<Viewable> mHtml;
|
||||
|
||||
public Alternative(List<Viewable> text, List<Viewable> html) {
|
||||
mText = text;
|
||||
mHtml = html;
|
||||
}
|
||||
|
||||
public List<Viewable> getText() {
|
||||
return mText;
|
||||
}
|
||||
|
||||
public List<Viewable> getHtml() {
|
||||
return mHtml;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store viewable text of a message as plain text and HTML, and the parts considered
|
||||
* attachments.
|
||||
*
|
||||
* @see MimeUtility#extractTextAndAttachments(Context, Message)
|
||||
*/
|
||||
public static class ViewableContainer {
|
||||
/**
|
||||
* The viewable text of the message in plain text.
|
||||
*/
|
||||
if (part.getBody() instanceof Multipart) {
|
||||
Multipart mp = (Multipart)part.getBody();
|
||||
for (int i = 0; i < mp.getCount(); i++) {
|
||||
collectParts(mp.getBodyPart(i), viewables, attachments);
|
||||
public final String text;
|
||||
|
||||
/**
|
||||
* The viewable text of the message in HTML.
|
||||
*/
|
||||
public final String html;
|
||||
|
||||
/**
|
||||
* The parts of the message considered attachments (everything not viewable).
|
||||
*/
|
||||
public final List<Part> attachments;
|
||||
|
||||
ViewableContainer(String text, String html, List<Part> attachments) {
|
||||
this.text = text;
|
||||
this.html = html;
|
||||
this.attachments = attachments;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect attachment parts of a message.
|
||||
*
|
||||
* @param message
|
||||
* The message to collect the attachment parts from.
|
||||
*
|
||||
* @return A list of parts regarded as attachments.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
public static List<Part> collectAttachments(Message message)
|
||||
throws MessagingException {
|
||||
try {
|
||||
List<Part> attachments = new ArrayList<Part>();
|
||||
getViewables(message, attachments);
|
||||
|
||||
return attachments;
|
||||
} catch (Exception e) {
|
||||
throw new MessagingException("Couldn't collect attachment parts", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the viewable textual parts of a message.
|
||||
*
|
||||
* @param message
|
||||
* The message to extract the viewable parts from.
|
||||
*
|
||||
* @return A set of viewable parts of the message.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
public static Set<Part> collectTextParts(Message message)
|
||||
throws MessagingException {
|
||||
try {
|
||||
List<Part> attachments = new ArrayList<Part>();
|
||||
|
||||
// Collect all viewable parts
|
||||
List<Viewable> viewables = getViewables(message, attachments);
|
||||
|
||||
// Extract the Part references
|
||||
return getParts(viewables);
|
||||
} catch (Exception e) {
|
||||
throw new MessagingException("Couldn't extract viewable parts", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the viewable textual parts of a message and return the rest as attachments.
|
||||
*
|
||||
* @param context
|
||||
* A {@link Context} instance that will be used to get localized strings.
|
||||
* @param message
|
||||
* The message to extract the text and attachments from.
|
||||
*
|
||||
* @return A {@link ViewableContainer} instance containing the textual parts of the message as
|
||||
* plain text and HTML, and a list of message parts considered attachments.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
public static ViewableContainer extractTextAndAttachments(Context context, Message message)
|
||||
throws MessagingException {
|
||||
try {
|
||||
List<Part> attachments = new ArrayList<Part>();
|
||||
|
||||
// Collect all viewable parts
|
||||
List<Viewable> viewables = getViewables(message, attachments);
|
||||
|
||||
/*
|
||||
* Convert the tree of viewable parts into text and HTML
|
||||
*/
|
||||
|
||||
// Used to suppress the divider for the first viewable part
|
||||
boolean hideDivider = true;
|
||||
|
||||
StringBuilder text = new StringBuilder();
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append(HtmlConverter.getHtmlHeader());
|
||||
|
||||
for (Viewable viewable : viewables) {
|
||||
if (viewable instanceof Textual) {
|
||||
// This is either a text/plain or text/html part. Fill the variables 'text' and
|
||||
// 'html', converting between plain text and HTML as necessary.
|
||||
text.append(buildText(viewable, !hideDivider));
|
||||
html.append(buildHtml(viewable, !hideDivider));
|
||||
hideDivider = false;
|
||||
} else if (viewable instanceof MessageHeader) {
|
||||
MessageHeader header = (MessageHeader) viewable;
|
||||
Part containerPart = header.getContainerPart();
|
||||
Message innerMessage = header.getMessage();
|
||||
|
||||
addTextDivider(text, containerPart, !hideDivider);
|
||||
addMessageHeaderText(context, text, innerMessage);
|
||||
|
||||
addHtmlDivider(html, containerPart, !hideDivider);
|
||||
addMessageHeaderHtml(context, html, innerMessage);
|
||||
|
||||
hideDivider = true;
|
||||
} else if (viewable instanceof Alternative) {
|
||||
// Handle multipart/alternative contents
|
||||
Alternative alternative = (Alternative) viewable;
|
||||
|
||||
/*
|
||||
* We made sure at least one of text/plain or text/html is present when
|
||||
* creating the Alternative object. If one part is not present we convert the
|
||||
* other one to make sure 'text' and 'html' always contain the same text.
|
||||
*/
|
||||
List<Viewable> textAlternative = alternative.getText().isEmpty() ?
|
||||
alternative.getHtml() : alternative.getText();
|
||||
List<Viewable> htmlAlternative = alternative.getHtml().isEmpty() ?
|
||||
alternative.getText() : alternative.getHtml();
|
||||
|
||||
// Fill the 'text' variable
|
||||
boolean divider = !hideDivider;
|
||||
for (Viewable textViewable : textAlternative) {
|
||||
text.append(buildText(textViewable, divider));
|
||||
divider = true;
|
||||
}
|
||||
|
||||
// Fill the 'html' variable
|
||||
divider = !hideDivider;
|
||||
for (Viewable htmlViewable : htmlAlternative) {
|
||||
html.append(buildHtml(htmlViewable, divider));
|
||||
divider = true;
|
||||
}
|
||||
hideDivider = false;
|
||||
}
|
||||
}
|
||||
|
||||
html.append(HtmlConverter.getHtmlFooter());
|
||||
|
||||
return new ViewableContainer(text.toString(), html.toString(), attachments);
|
||||
} catch (Exception e) {
|
||||
throw new MessagingException("Couldn't extract viewable parts", e);
|
||||
}
|
||||
/*
|
||||
* If the part is an embedded message we just continue to process
|
||||
* it, pulling any viewables or attachments into the running list.
|
||||
*/
|
||||
else if (part.getBody() instanceof Message) {
|
||||
Message message = (Message)part.getBody();
|
||||
collectParts(message, viewables, attachments);
|
||||
}
|
||||
/*
|
||||
* If the part is HTML and it got this far it's part of a mixed (et
|
||||
* al) and should be rendered inline.
|
||||
*/
|
||||
else if (isPartTextualBody(part)) {
|
||||
viewables.add(part);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the MIME tree of a message an extract viewable parts.
|
||||
*
|
||||
* @param part
|
||||
* The message part to start from.
|
||||
* @param attachments
|
||||
* A list that will receive the parts that are considered attachments.
|
||||
*
|
||||
* @return A list of {@link Viewable}s.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
public static List<Viewable> getViewables(Part part, List<Part> attachments) throws MessagingException {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
Body body = part.getBody();
|
||||
if (body instanceof Multipart) {
|
||||
Multipart multipart = (Multipart) body;
|
||||
if (part.getMimeType().equalsIgnoreCase("multipart/alternative")) {
|
||||
/*
|
||||
* For multipart/alternative parts we try to find a text/plain and a text/html
|
||||
* child. Everything else we find is put into 'attachments'.
|
||||
*/
|
||||
List<Viewable> text = findTextPart(multipart, true);
|
||||
|
||||
Set<Part> knownTextParts = getParts(text);
|
||||
List<Viewable> html = findHtmlPart(multipart, knownTextParts, attachments, true);
|
||||
|
||||
if (!text.isEmpty() || !html.isEmpty()) {
|
||||
Alternative alternative = new Alternative(text, html);
|
||||
viewables.add(alternative);
|
||||
}
|
||||
} else {
|
||||
// For all other multipart parts we recurse to grab all viewable children.
|
||||
int childCount = multipart.getCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Part bodyPart = multipart.getBodyPart(i);
|
||||
viewables.addAll(getViewables(bodyPart, attachments));
|
||||
}
|
||||
}
|
||||
} else if (body instanceof Message &&
|
||||
!("attachment".equalsIgnoreCase(getContentDisposition(part)))) {
|
||||
/*
|
||||
* We only care about message/rfc822 parts whose Content-Disposition header has a value
|
||||
* other than "attachment".
|
||||
*/
|
||||
Message message = (Message) body;
|
||||
|
||||
// We add the Message object so we can extract the filename later.
|
||||
viewables.add(new MessageHeader(part, message));
|
||||
|
||||
// Recurse to grab all viewable parts and attachments from that message.
|
||||
viewables.addAll(getViewables(message, attachments));
|
||||
} else if (isPartTextualBody(part)) {
|
||||
/*
|
||||
* Save text/plain and text/html
|
||||
*/
|
||||
String mimeType = part.getMimeType();
|
||||
if (mimeType.equalsIgnoreCase("text/plain")) {
|
||||
Text text = new Text(part);
|
||||
viewables.add(text);
|
||||
} else {
|
||||
Html html = new Html(part);
|
||||
viewables.add(html);
|
||||
}
|
||||
} else {
|
||||
// Everything else is treated as attachment.
|
||||
attachments.add(part);
|
||||
}
|
||||
|
||||
return viewables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the children of a {@link Multipart} for {@code text/plain} parts.
|
||||
*
|
||||
* @param multipart
|
||||
* The {@code Multipart} to search through.
|
||||
* @param directChild
|
||||
* If {@code true}, this method will return after the first {@code text/plain} was
|
||||
* found.
|
||||
*
|
||||
* @return A list of {@link Text} viewables.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
private static List<Viewable> findTextPart(Multipart multipart, boolean directChild)
|
||||
throws MessagingException {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
int childCount = multipart.getCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
Body body = part.getBody();
|
||||
if (body instanceof Multipart) {
|
||||
Multipart innerMultipart = (Multipart) body;
|
||||
|
||||
/*
|
||||
* Recurse to find text parts. Since this is a multipart that is a child of a
|
||||
* multipart/alternative we don't want to stop after the first text/plain part
|
||||
* we find. This will allow to get all text parts for constructions like this:
|
||||
*
|
||||
* 1. multipart/alternative
|
||||
* 1.1. multipart/mixed
|
||||
* 1.1.1. text/plain
|
||||
* 1.1.2. text/plain
|
||||
* 1.2. text/html
|
||||
*/
|
||||
List<Viewable> textViewables = findTextPart(innerMultipart, false);
|
||||
|
||||
if (!textViewables.isEmpty()) {
|
||||
viewables.addAll(textViewables);
|
||||
if (directChild) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (isPartTextualBody(part) && part.getMimeType().equalsIgnoreCase("text/plain")) {
|
||||
Text text = new Text(part);
|
||||
viewables.add(text);
|
||||
if (directChild) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return viewables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the children of a {@link Multipart} for {@code text/html} parts.
|
||||
*
|
||||
* <p>
|
||||
* Every part that is not a {@code text/html} we want to display, we add to 'attachments'.
|
||||
* </p>
|
||||
*
|
||||
* @param multipart
|
||||
* The {@code Multipart} to search through.
|
||||
* @param knownTextParts
|
||||
* A set of {@code text/plain} parts that shouldn't be added to 'attachments'.
|
||||
* @param attachments
|
||||
* A list that will receive the parts that are considered attachments.
|
||||
* @param directChild
|
||||
* If {@code true}, this method will add all {@code text/html} parts except the first
|
||||
* found to 'attachments'.
|
||||
*
|
||||
* @return A list of {@link Text} viewables.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
private static List<Viewable> findHtmlPart(Multipart multipart, Set<Part> knownTextParts,
|
||||
List<Part> attachments, boolean directChild) throws MessagingException {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
boolean partFound = false;
|
||||
int childCount = multipart.getCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
Body body = part.getBody();
|
||||
if (body instanceof Multipart) {
|
||||
Multipart innerMultipart = (Multipart) body;
|
||||
|
||||
if (directChild && partFound) {
|
||||
// We already found our text/html part. Now we're only looking for attachments.
|
||||
findAttachments(innerMultipart, knownTextParts, attachments);
|
||||
} else {
|
||||
/*
|
||||
* Recurse to find HTML parts. Since this is a multipart that is a child of a
|
||||
* multipart/alternative we don't want to stop after the first text/html part
|
||||
* we find. This will allow to get all text parts for constructions like this:
|
||||
*
|
||||
* 1. multipart/alternative
|
||||
* 1.1. text/plain
|
||||
* 1.2. multipart/mixed
|
||||
* 1.2.1. text/html
|
||||
* 1.2.2. text/html
|
||||
* 1.3. image/jpeg
|
||||
*/
|
||||
List<Viewable> htmlViewables = findHtmlPart(innerMultipart, knownTextParts,
|
||||
attachments, false);
|
||||
|
||||
if (!htmlViewables.isEmpty()) {
|
||||
partFound = true;
|
||||
viewables.addAll(htmlViewables);
|
||||
}
|
||||
}
|
||||
} else if (!(directChild && partFound) && isPartTextualBody(part) &&
|
||||
part.getMimeType().equalsIgnoreCase("text/html")) {
|
||||
Html html = new Html(part);
|
||||
viewables.add(html);
|
||||
partFound = true;
|
||||
} else if (!knownTextParts.contains(part)) {
|
||||
// Only add this part as attachment if it's not a viewable text/plain part found
|
||||
// earlier.
|
||||
attachments.add(part);
|
||||
}
|
||||
}
|
||||
|
||||
return viewables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of message parts for fast lookups.
|
||||
*
|
||||
* @param viewables
|
||||
* A list of {@link Viewable}s containing references to the message parts to include in
|
||||
* the set.
|
||||
*
|
||||
* @return The set of viewable {@code Part}s.
|
||||
*
|
||||
* @see MimeUtility#findHtmlPart(Multipart, Set, List, boolean)
|
||||
* @see MimeUtility#findAttachments(Multipart, Set, List)
|
||||
*/
|
||||
private static Set<Part> getParts(List<Viewable> viewables) {
|
||||
Set<Part> parts = new HashSet<Part>();
|
||||
|
||||
for (Viewable viewable : viewables) {
|
||||
if (viewable instanceof Textual) {
|
||||
parts.add(((Textual) viewable).getPart());
|
||||
} else if (viewable instanceof Alternative) {
|
||||
Alternative alternative = (Alternative) viewable;
|
||||
parts.addAll(getParts(alternative.getText()));
|
||||
parts.addAll(getParts(alternative.getHtml()));
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the MIME tree and add everything that's not a known text part to 'attachments'.
|
||||
*
|
||||
* @param multipart
|
||||
* The {@link Multipart} to start from.
|
||||
* @param knownTextParts
|
||||
* A set of known text parts we don't want to end up in 'attachments'.
|
||||
* @param attachments
|
||||
* A list that will receive the parts that are considered attachments.
|
||||
*/
|
||||
private static void findAttachments(Multipart multipart, Set<Part> knownTextParts,
|
||||
List<Part> attachments) {
|
||||
int childCount = multipart.getCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
Body body = part.getBody();
|
||||
if (body instanceof Multipart) {
|
||||
Multipart innerMultipart = (Multipart) body;
|
||||
findAttachments(innerMultipart, knownTextParts, attachments);
|
||||
} else if (!knownTextParts.contains(part)) {
|
||||
attachments.add(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract important header values from a message to display inline (plain text version).
|
||||
*
|
||||
* @param context
|
||||
* A {@link Context} instance that will be used to get localized strings.
|
||||
* @param text
|
||||
* The {@link StringBuilder} that will receive the (plain text) output.
|
||||
* @param message
|
||||
* The message to extract the header values from.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
private static void addMessageHeaderText(Context context, StringBuilder text, Message message)
|
||||
throws MessagingException {
|
||||
// From: <sender>
|
||||
Address[] from = message.getFrom();
|
||||
if (from != null && from.length > 0) {
|
||||
text.append(context.getString(R.string.message_compose_quote_header_from));
|
||||
text.append(' ');
|
||||
text.append(Address.toString(from));
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
// To: <recipients>
|
||||
Address[] to = message.getRecipients(RecipientType.TO);
|
||||
if (to != null && to.length > 0) {
|
||||
text.append(context.getString(R.string.message_compose_quote_header_to));
|
||||
text.append(' ');
|
||||
text.append(Address.toString(to));
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
// Cc: <recipients>
|
||||
Address[] cc = message.getRecipients(RecipientType.CC);
|
||||
if (cc != null && cc.length > 0) {
|
||||
text.append(context.getString(R.string.message_compose_quote_header_cc));
|
||||
text.append(' ');
|
||||
text.append(Address.toString(cc));
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
// Date: <date>
|
||||
Date date = message.getSentDate();
|
||||
if (date != null) {
|
||||
text.append(context.getString(R.string.message_compose_quote_header_send_date));
|
||||
text.append(' ');
|
||||
text.append(date.toString());
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
// Subject: <subject>
|
||||
String subject = message.getSubject();
|
||||
text.append(context.getString(R.string.message_compose_quote_header_subject));
|
||||
text.append(' ');
|
||||
if (subject == null) {
|
||||
text.append(context.getString(R.string.general_no_subject));
|
||||
} else {
|
||||
text.append(subject);
|
||||
}
|
||||
text.append("\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract important header values from a message to display inline (HTML version).
|
||||
*
|
||||
* @param context
|
||||
* A {@link Context} instance that will be used to get localized strings.
|
||||
* @param html
|
||||
* The {@link StringBuilder} that will receive the (HTML) output.
|
||||
* @param message
|
||||
* The message to extract the header values from.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
private static void addMessageHeaderHtml(Context context, StringBuilder html, Message message)
|
||||
throws MessagingException {
|
||||
|
||||
html.append("<table style=\"border: 0\">");
|
||||
|
||||
// From: <sender>
|
||||
Address[] from = message.getFrom();
|
||||
if (from != null && from.length > 0) {
|
||||
addTableRow(html, context.getString(R.string.message_compose_quote_header_from),
|
||||
Address.toString(from));
|
||||
}
|
||||
|
||||
// To: <recipients>
|
||||
Address[] to = message.getRecipients(RecipientType.TO);
|
||||
if (to != null && to.length > 0) {
|
||||
addTableRow(html, context.getString(R.string.message_compose_quote_header_to),
|
||||
Address.toString(to));
|
||||
}
|
||||
|
||||
// Cc: <recipients>
|
||||
Address[] cc = message.getRecipients(RecipientType.CC);
|
||||
if (cc != null && cc.length > 0) {
|
||||
addTableRow(html, context.getString(R.string.message_compose_quote_header_cc),
|
||||
Address.toString(cc));
|
||||
}
|
||||
|
||||
// Date: <date>
|
||||
Date date = message.getSentDate();
|
||||
if (date != null) {
|
||||
addTableRow(html, context.getString(R.string.message_compose_quote_header_send_date),
|
||||
date.toString());
|
||||
}
|
||||
|
||||
// Subject: <subject>
|
||||
String subject = message.getSubject();
|
||||
addTableRow(html, context.getString(R.string.message_compose_quote_header_subject),
|
||||
(subject == null) ? context.getString(R.string.general_no_subject) : subject);
|
||||
|
||||
html.append("</table>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an HTML table two column row with some hardcoded style.
|
||||
*
|
||||
* @param html
|
||||
* The {@link StringBuilder} that will receive the output.
|
||||
* @param header
|
||||
* The string to be put in the {@code TH} element.
|
||||
* @param value
|
||||
* The string to be put in the {@code TD} element.
|
||||
*/
|
||||
private static void addTableRow(StringBuilder html, String header, String value) {
|
||||
html.append("<tr><th style=\"text-align: left; vertical-align: top;\">");
|
||||
html.append(header);
|
||||
html.append("</th>");
|
||||
html.append("<td>");
|
||||
html.append(value);
|
||||
html.append("</td></tr>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the contents of a {@link Viewable} to create the plain text to be displayed.
|
||||
*
|
||||
* <p>
|
||||
* This will use {@link HtmlConverter#htmlToText(String)} to convert HTML parts to plain text
|
||||
* if necessary.
|
||||
* </p>
|
||||
*
|
||||
* @param viewable
|
||||
* The viewable part to build the text from.
|
||||
* @param prependDivider
|
||||
* {@code true}, if the text divider should be inserted as first element.
|
||||
* {@code false}, otherwise.
|
||||
*
|
||||
* @return The contents of the supplied viewable instance as plain text.
|
||||
*/
|
||||
private static StringBuilder buildText(Viewable viewable, boolean prependDivider)
|
||||
{
|
||||
StringBuilder text = new StringBuilder();
|
||||
if (viewable instanceof Textual) {
|
||||
Part part = ((Textual)viewable).getPart();
|
||||
addTextDivider(text, part, prependDivider);
|
||||
|
||||
String t = getTextFromPart(part);
|
||||
if (t == null) {
|
||||
t = "";
|
||||
} else if (viewable instanceof Html) {
|
||||
t = HtmlConverter.htmlToText(t);
|
||||
}
|
||||
text.append(t);
|
||||
} else if (viewable instanceof Alternative) {
|
||||
// That's odd - an Alternative as child of an Alternative; go ahead and try to use the
|
||||
// text/plain child; fall-back to the text/html part.
|
||||
Alternative alternative = (Alternative) viewable;
|
||||
|
||||
List<Viewable> textAlternative = alternative.getText().isEmpty() ?
|
||||
alternative.getHtml() : alternative.getText();
|
||||
|
||||
boolean divider = prependDivider;
|
||||
for (Viewable textViewable : textAlternative) {
|
||||
text.append(buildText(textViewable, divider));
|
||||
divider = true;
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some constants that are used by addTextDivider() below.
|
||||
*/
|
||||
private static final int TEXT_DIVIDER_LENGTH = TEXT_DIVIDER.length();
|
||||
private static final String FILENAME_PREFIX = "----- ";
|
||||
private static final int FILENAME_PREFIX_LENGTH = FILENAME_PREFIX.length();
|
||||
private static final String FILENAME_SUFFIX = " ";
|
||||
private static final int FILENAME_SUFFIX_LENGTH = FILENAME_SUFFIX.length();
|
||||
|
||||
/**
|
||||
* Add a plain text divider between two plain text message parts.
|
||||
*
|
||||
* @param text
|
||||
* The {@link StringBuilder} to append the divider to.
|
||||
* @param part
|
||||
* The message part that will follow after the divider. This is used to extract the
|
||||
* part's name.
|
||||
* @param prependDivider
|
||||
* {@code true}, if the divider should be appended. {@code false}, otherwise.
|
||||
*/
|
||||
private static void addTextDivider(StringBuilder text, Part part, boolean prependDivider) {
|
||||
if (prependDivider) {
|
||||
String filename = getPartName(part);
|
||||
|
||||
text.append("\n\n");
|
||||
int len = filename.length();
|
||||
if (len > 0) {
|
||||
if (len > TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - FILENAME_SUFFIX_LENGTH) {
|
||||
filename = filename.substring(0, TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH -
|
||||
FILENAME_SUFFIX_LENGTH - 3) + "...";
|
||||
}
|
||||
text.append(FILENAME_PREFIX);
|
||||
text.append(filename);
|
||||
text.append(FILENAME_SUFFIX);
|
||||
text.append(TEXT_DIVIDER.substring(0, TEXT_DIVIDER_LENGTH -
|
||||
FILENAME_PREFIX_LENGTH - filename.length() - FILENAME_SUFFIX_LENGTH));
|
||||
} else {
|
||||
text.append(TEXT_DIVIDER);
|
||||
}
|
||||
text.append("\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the contents of a {@link Viewable} to create the HTML to be displayed.
|
||||
*
|
||||
* <p>
|
||||
* This will use {@link HtmlConverter#textToHtml(String, boolean)} to convert plain text parts
|
||||
* to HTML if necessary.
|
||||
* </p>
|
||||
*
|
||||
* @param viewable
|
||||
* The viewable part to build the HTML from.
|
||||
* @param prependDivider
|
||||
* {@code true}, if the HTML divider should be inserted as first element.
|
||||
* {@code false}, otherwise.
|
||||
*
|
||||
* @return The contents of the supplied viewable instance as HTML.
|
||||
*/
|
||||
private static StringBuilder buildHtml(Viewable viewable, boolean prependDivider)
|
||||
{
|
||||
StringBuilder html = new StringBuilder();
|
||||
if (viewable instanceof Textual) {
|
||||
Part part = ((Textual)viewable).getPart();
|
||||
addHtmlDivider(html, part, prependDivider);
|
||||
|
||||
String t = getTextFromPart(part);
|
||||
if (t == null) {
|
||||
t = "";
|
||||
} else if (viewable instanceof Text) {
|
||||
t = HtmlConverter.textToHtml(t, false);
|
||||
}
|
||||
html.append(t);
|
||||
} else if (viewable instanceof Alternative) {
|
||||
// That's odd - an Alternative as child of an Alternative; go ahead and try to use the
|
||||
// text/html child; fall-back to the text/plain part.
|
||||
Alternative alternative = (Alternative) viewable;
|
||||
|
||||
List<Viewable> htmlAlternative = alternative.getHtml().isEmpty() ?
|
||||
alternative.getText() : alternative.getHtml();
|
||||
|
||||
boolean divider = prependDivider;
|
||||
for (Viewable htmlViewable : htmlAlternative) {
|
||||
html.append(buildHtml(htmlViewable, divider));
|
||||
divider = true;
|
||||
}
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an HTML divider between two HTML message parts.
|
||||
*
|
||||
* @param html
|
||||
* The {@link StringBuilder} to append the divider to.
|
||||
* @param part
|
||||
* The message part that will follow after the divider. This is used to extract the
|
||||
* part's name.
|
||||
* @param prependDivider
|
||||
* {@code true}, if the divider should be appended. {@code false}, otherwise.
|
||||
*/
|
||||
private static void addHtmlDivider(StringBuilder html, Part part, boolean prependDivider) {
|
||||
if (prependDivider) {
|
||||
String filename = getPartName(part);
|
||||
|
||||
html.append("<p style=\"margin-top: 2.5em; margin-bottom: 1em; border-bottom: 1px solid #000\">");
|
||||
html.append(filename);
|
||||
html.append("</p>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the message part.
|
||||
*
|
||||
* @param part
|
||||
* The part to get the name for.
|
||||
*
|
||||
* @return The (file)name of the part if available. An empty string, otherwise.
|
||||
*/
|
||||
private static String getPartName(Part part) {
|
||||
try {
|
||||
String disposition = part.getDisposition();
|
||||
if (disposition != null) {
|
||||
String name = MimeUtility.getHeaderParameter(disposition, "filename");
|
||||
return (name == null) ? "" : name;
|
||||
}
|
||||
}
|
||||
catch (MessagingException e) { /* ignore */ }
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the {@code Content-Disposition} header.
|
||||
*
|
||||
* @param part
|
||||
* The message part to read the header from.
|
||||
*
|
||||
* @return The value of the {@code Content-Disposition} header if available. {@code null},
|
||||
* otherwise.
|
||||
*/
|
||||
private static String getContentDisposition(Part part) {
|
||||
try {
|
||||
String disposition = part.getDisposition();
|
||||
if (disposition != null) {
|
||||
return MimeUtility.getHeaderParameter(disposition, null);
|
||||
}
|
||||
}
|
||||
catch (MessagingException e) { /* ignore */ }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Boolean isPartTextualBody(Part part) throws MessagingException {
|
||||
String disposition = part.getDisposition();
|
||||
@ -1210,6 +2040,17 @@ public class MimeUtility {
|
||||
return DEFAULT_ATTACHMENT_MIME_TYPE;
|
||||
}
|
||||
|
||||
public static String getExtensionByMimeType(String mimeType) {
|
||||
String lowerCaseMimeType = mimeType.toLowerCase(Locale.US);
|
||||
for (String[] contentTypeMapEntry : MIME_TYPE_BY_EXTENSION_MAP) {
|
||||
if (contentTypeMapEntry[1].equals(lowerCaseMimeType)) {
|
||||
return contentTypeMapEntry[0];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert some wrong MIME types encountered in the wild to canonical MIME types.
|
||||
*
|
||||
|
@ -17,6 +17,7 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -1088,13 +1089,15 @@ public class EasStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
public Map<String, String> copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
moveOrCopyMessages(messages, folder.getRemoteName(), false);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
public Map<String, String> moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
moveOrCopyMessages(messages, folder.getRemoteName(), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1464,8 +1467,9 @@ public class EasStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendMessages(Message[] messages) throws MessagingException {
|
||||
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||
// EASTODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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;
|
||||
@ -1064,53 +1066,127 @@ public class ImapStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given messages to the specified folder.
|
||||
*
|
||||
* <p>
|
||||
* <strong>Note:</strong>
|
||||
* Only the UIDs of the given {@link Message} instances are used. It is assumed that all
|
||||
* UIDs represent valid messages in this folder.
|
||||
* </p>
|
||||
*
|
||||
* @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<String, String> 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()));
|
||||
|
||||
if (!exists(remoteDestName)) {
|
||||
/*
|
||||
* If the remote trash folder doesn't exist we try to create it.
|
||||
*/
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "IMAPMessage.copyMessages: attempting to create remote '" + remoteDestName + "' folder for " + getLogId());
|
||||
// If the remote trash folder doesn't exist we try to create it.
|
||||
if (K9.DEBUG) {
|
||||
Log.i(K9.LOG_TAG, "Attempting to create remote folder '" + remoteDestName +
|
||||
"' for " + getLogId());
|
||||
}
|
||||
iFolder.create(FolderType.HOLDS_MESSAGES);
|
||||
}
|
||||
|
||||
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);
|
||||
//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<String, String> uidMap = null;
|
||||
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<String> srcUids = ImapUtility.getImapSequenceValues(
|
||||
copyList.getString(2));
|
||||
List<String> destUids = ImapUtility.getImapSequenceValues(
|
||||
copyList.getString(3));
|
||||
|
||||
if (srcUids != null && destUids != null) {
|
||||
if (srcUids.size() == destUids.size()) {
|
||||
Iterator<String> srcUidsIterator = srcUids.iterator();
|
||||
Iterator<String> destUidsIterator = destUids.iterator();
|
||||
uidMap = new HashMap<String, String>();
|
||||
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<String, String> moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
if (messages.length == 0)
|
||||
return;
|
||||
copyMessages(messages, folder);
|
||||
return null;
|
||||
Map<String, String> uidMap = copyMessages(messages, folder);
|
||||
setFlags(messages, new Flag[] { Flag.DELETED }, true);
|
||||
return uidMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1854,20 +1930,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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @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<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||
checkOpen();
|
||||
try {
|
||||
Map<String, String> uidMap = new HashMap<String, String>();
|
||||
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();
|
||||
@ -1881,16 +1967,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);
|
||||
}
|
||||
@ -1916,7 +2040,7 @@ public class ImapStore extends Store {
|
||||
|
||||
List<ImapResponse> responses =
|
||||
executeSimpleCommand(
|
||||
String.format("UID SEARCH HEADER MESSAGE-ID %s", messageId));
|
||||
String.format("UID SEARCH HEADER MESSAGE-ID %s", encodeString(messageId)));
|
||||
for (ImapResponse response1 : responses) {
|
||||
if (response1.mTag == null && ImapResponseParser.equalsIgnoreCase(response1.get(0), "SEARCH")
|
||||
&& response1.size() > 1) {
|
||||
|
@ -55,6 +55,7 @@ import com.fsck.k9.mail.internet.MimeHeader;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.internet.MimeUtility.ViewableContainer;
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
import com.fsck.k9.mail.store.LockableDatabase.DbCallback;
|
||||
import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
|
||||
@ -1720,6 +1721,12 @@ public class LocalStore extends Store implements Serializable {
|
||||
contentDisposition,
|
||||
name, // TODO: Should use encoded word defined in RFC 2231.
|
||||
size));
|
||||
} else {
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE, type);
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION,
|
||||
String.format("%s;\n size=%d",
|
||||
contentDisposition,
|
||||
size));
|
||||
}
|
||||
|
||||
bp.setHeader(MimeHeader.HEADER_CONTENT_ID, contentId);
|
||||
@ -1950,21 +1957,23 @@ public class LocalStore extends Store implements Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {
|
||||
public Map<String, String> 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<String, String> 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<String, String> uidMap = new HashMap<String, String>();
|
||||
|
||||
try {
|
||||
database.execute(false, new DbCallback<Void>() {
|
||||
@Override
|
||||
@ -1990,7 +1999,10 @@ public class LocalStore extends Store implements Serializable {
|
||||
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(),
|
||||
@ -1998,6 +2010,11 @@ public class LocalStore extends Store implements Serializable {
|
||||
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);
|
||||
@ -2009,6 +2026,7 @@ public class LocalStore extends Store implements Serializable {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return uidMap;
|
||||
} catch (WrappedException e) {
|
||||
throw(MessagingException) e.getCause();
|
||||
}
|
||||
@ -2054,8 +2072,8 @@ public class LocalStore extends Store implements Serializable {
|
||||
* 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<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||
return appendMessages(messages, false);
|
||||
}
|
||||
|
||||
public void destroyMessages(final Message[] messages) throws MessagingException {
|
||||
@ -2091,10 +2109,12 @@ public class LocalStore extends Store implements Serializable {
|
||||
* message, retrieve the appropriate local message instance first (if it already exists).
|
||||
* @param messages
|
||||
* @param copy
|
||||
* @return Map<String, String> uidMap of srcUids -> destUids
|
||||
*/
|
||||
private void appendMessages(final Message[] messages, final boolean copy) throws MessagingException {
|
||||
private Map<String, String> appendMessages(final Message[] messages, final boolean copy) throws MessagingException {
|
||||
open(OpenMode.READ_WRITE);
|
||||
try {
|
||||
final Map<String, String> uidMap = new HashMap<String, String>();
|
||||
database.execute(true, new DbCallback<Void>() {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||
@ -2107,11 +2127,26 @@ public class LocalStore extends Store implements Serializable {
|
||||
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) {
|
||||
@ -2128,45 +2163,14 @@ public class LocalStore extends Store implements Serializable {
|
||||
deleteAttachments(message.getUid());
|
||||
}
|
||||
|
||||
ArrayList<Part> viewables = new ArrayList<Part>();
|
||||
ArrayList<Part> attachments = new ArrayList<Part>();
|
||||
MimeUtility.collectParts(message, viewables, attachments);
|
||||
ViewableContainer container =
|
||||
MimeUtility.extractTextAndAttachments(mApplication, message);
|
||||
|
||||
StringBuilder sbHtml = new StringBuilder();
|
||||
StringBuilder sbText = new StringBuilder();
|
||||
for (Part viewable : viewables) {
|
||||
try {
|
||||
String text = MimeUtility.getTextFromPart(viewable);
|
||||
List<Part> attachments = container.attachments;
|
||||
String text = container.text;
|
||||
String html = HtmlConverter.convertEmoji2Img(container.html);
|
||||
|
||||
/*
|
||||
* Small hack to make sure the string "null" doesn't end up
|
||||
* in one of the StringBuilders.
|
||||
*/
|
||||
if (text == null) {
|
||||
text = "";
|
||||
}
|
||||
|
||||
/*
|
||||
* Anything with MIME type text/html will be stored as such. Anything
|
||||
* else will be stored as text/plain.
|
||||
*/
|
||||
if (viewable.getMimeType().equalsIgnoreCase("text/html")) {
|
||||
sbHtml.append(text);
|
||||
} else {
|
||||
sbText.append(text);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new MessagingException("Unable to get text for message part", e);
|
||||
}
|
||||
}
|
||||
|
||||
String text = sbText.toString();
|
||||
String html = markupContent(text, sbHtml.toString());
|
||||
String preview = calculateContentPreview(text);
|
||||
// If we couldn't generate a reasonable preview from the text part, try doing it with the HTML part.
|
||||
if (preview == null || preview.length() == 0) {
|
||||
preview = calculateContentPreview(HtmlConverter.htmlToText(html));
|
||||
}
|
||||
|
||||
try {
|
||||
ContentValues cv = new ContentValues();
|
||||
@ -2222,6 +2226,7 @@ public class LocalStore extends Store implements Serializable {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return uidMap;
|
||||
} catch (WrappedException e) {
|
||||
throw(MessagingException) e.getCause();
|
||||
}
|
||||
@ -2244,49 +2249,17 @@ public class LocalStore extends Store implements Serializable {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||
try {
|
||||
ArrayList<Part> viewables = new ArrayList<Part>();
|
||||
ArrayList<Part> attachments = new ArrayList<Part>();
|
||||
|
||||
message.buildMimeRepresentation();
|
||||
|
||||
MimeUtility.collectParts(message, viewables, attachments);
|
||||
ViewableContainer container =
|
||||
MimeUtility.extractTextAndAttachments(mApplication, message);
|
||||
|
||||
StringBuilder sbHtml = new StringBuilder();
|
||||
StringBuilder sbText = new StringBuilder();
|
||||
for (int i = 0, count = viewables.size(); i < count; i++) {
|
||||
Part viewable = viewables.get(i);
|
||||
try {
|
||||
String text = MimeUtility.getTextFromPart(viewable);
|
||||
List<Part> attachments = container.attachments;
|
||||
String text = container.text;
|
||||
String html = HtmlConverter.convertEmoji2Img(container.html);
|
||||
|
||||
/*
|
||||
* Small hack to make sure the string "null" doesn't end up
|
||||
* in one of the StringBuilders.
|
||||
*/
|
||||
if (text == null) {
|
||||
text = "";
|
||||
}
|
||||
|
||||
/*
|
||||
* Anything with MIME type text/html will be stored as such. Anything
|
||||
* else will be stored as text/plain.
|
||||
*/
|
||||
if (viewable.getMimeType().equalsIgnoreCase("text/html")) {
|
||||
sbHtml.append(text);
|
||||
} else {
|
||||
sbText.append(text);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new MessagingException("Unable to get text for message part", e);
|
||||
}
|
||||
}
|
||||
|
||||
String text = sbText.toString();
|
||||
String html = markupContent(text, sbHtml.toString());
|
||||
String preview = calculateContentPreview(text);
|
||||
// If we couldn't generate a reasonable preview from the text part, try doing it with the HTML part.
|
||||
if (preview == null || preview.length() == 0) {
|
||||
preview = calculateContentPreview(HtmlConverter.htmlToText(html));
|
||||
}
|
||||
|
||||
try {
|
||||
db.execSQL("UPDATE messages SET "
|
||||
+ "uid = ?, subject = ?, sender_list = ?, date = ?, flags = ?, "
|
||||
@ -2420,6 +2393,18 @@ public class LocalStore extends Store implements Serializable {
|
||||
Body body = attachment.getBody();
|
||||
if (body instanceof LocalAttachmentBody) {
|
||||
contentUri = ((LocalAttachmentBody) body).getContentUri();
|
||||
} else if (body instanceof Message) {
|
||||
// It's a message, so use Message.writeTo() to output the
|
||||
// message including all children.
|
||||
Message message = (Message) body;
|
||||
tempAttachmentFile = File.createTempFile("att", null, attachmentDirectory);
|
||||
FileOutputStream out = new FileOutputStream(tempAttachmentFile);
|
||||
try {
|
||||
message.writeTo(out);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
size = (int) (tempAttachmentFile.length() & 0x7FFFFFFFL);
|
||||
} else {
|
||||
/*
|
||||
* If the attachment has a body we're expected to save it into the local store
|
||||
@ -2647,7 +2632,7 @@ public class LocalStore extends Store implements Serializable {
|
||||
setVisibleLimit(mAccount.getDisplayCount());
|
||||
}
|
||||
|
||||
private void resetUnreadAndFlaggedCounts() {
|
||||
public void resetUnreadAndFlaggedCounts() {
|
||||
try {
|
||||
int newUnread = 0;
|
||||
int newFlagged = 0;
|
||||
@ -2718,22 +2703,34 @@ public class LocalStore extends Store implements Serializable {
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||
Cursor attachmentsCursor = null;
|
||||
try {
|
||||
attachmentsCursor = db.query("attachments", new String[]
|
||||
{ "id" }, "message_id = ?", new String[]
|
||||
{ Long.toString(messageId) }, null, null, null);
|
||||
String accountUuid = mAccount.getUuid();
|
||||
Context context = mApplication;
|
||||
|
||||
// Get attachment IDs
|
||||
String[] whereArgs = new String[] { Long.toString(messageId) };
|
||||
attachmentsCursor = db.query("attachments", new String[] { "id" },
|
||||
"message_id = ?", whereArgs, null, null, null);
|
||||
|
||||
final File attachmentDirectory = StorageManager.getInstance(mApplication)
|
||||
.getAttachmentDirectory(uUid, database.getStorageProviderId());
|
||||
.getAttachmentDirectory(uUid, database.getStorageProviderId());
|
||||
|
||||
while (attachmentsCursor.moveToNext()) {
|
||||
long attachmentId = attachmentsCursor.getLong(0);
|
||||
String attachmentId = Long.toString(attachmentsCursor.getLong(0));
|
||||
try {
|
||||
File file = new File(attachmentDirectory, Long.toString(attachmentId));
|
||||
// Delete stored attachment
|
||||
File file = new File(attachmentDirectory, attachmentId);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
// Delete thumbnail file
|
||||
AttachmentProvider.deleteThumbnail(context, accountUuid,
|
||||
attachmentId);
|
||||
} catch (Exception e) { /* ignore */ }
|
||||
}
|
||||
|
||||
// Delete attachment metadata from the database
|
||||
db.delete("attachments", "message_id = ?", whereArgs);
|
||||
} finally {
|
||||
Utility.closeQuietly(attachmentsCursor);
|
||||
}
|
||||
@ -2817,17 +2814,7 @@ public class LocalStore extends Store implements Serializable {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String markupContent(String text, String html) {
|
||||
if (text.length() > 0 && html.length() == 0) {
|
||||
html = HtmlConverter.textToHtml(text);
|
||||
}
|
||||
|
||||
html = HtmlConverter.convertEmoji2Img(html);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isInTopGroup() {
|
||||
return mInTopGroup;
|
||||
|
@ -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";
|
||||
@ -893,7 +894,8 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendMessages(Message[] messages) throws MessagingException {
|
||||
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1333,13 +1333,15 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
public Map<String, String> copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
moveOrCopyMessages(messages, folder.getRemoteName(), false);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
public Map<String, String> moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
moveOrCopyMessages(messages, folder.getRemoteName(), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1919,8 +1921,9 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendMessages(Message[] messages) throws MessagingException {
|
||||
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||
appendWebDavMessages(messages);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Message[] appendWebDavMessages(Message[] messages) throws MessagingException {
|
||||
|
113
src/com/fsck/k9/mail/store/imap/ImapUtility.java
Normal file
113
src/com/fsck/k9/mail/store/imap/ImapUtility.java
Normal file
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* Any ranges are expanded into a list of individual numbers.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* sequence-number = nz-number / "*"
|
||||
* sequence-range = sequence-number ":" sequence-number
|
||||
* sequence-set = (sequence-number / sequence-range) *("," sequence-set)
|
||||
* </pre>
|
||||
*
|
||||
* @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<String> getImapSequenceValues(String set) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
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.
|
||||
*
|
||||
* <pre>
|
||||
* sequence-number = nz-number / "*"
|
||||
* sequence-range = sequence-number ":" sequence-number
|
||||
* sequence-set = (sequence-number / sequence-range) *("," sequence-set)
|
||||
* </pre>
|
||||
*
|
||||
* @param range
|
||||
* The range string as received by the server.
|
||||
*
|
||||
* @return The list of IDs as strings in this range. If the range is not valid, an empty list
|
||||
* is returned.
|
||||
*/
|
||||
public static List<String> getImapRangeValues(String range) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
try {
|
||||
if (range != null) {
|
||||
int colonPos = range.indexOf(':');
|
||||
if (colonPos > 0) {
|
||||
int first = Integer.parseInt(range.substring(0, colonPos));
|
||||
int second = Integer.parseInt(range.substring(colonPos + 1));
|
||||
if (first < second) {
|
||||
for (int i = first; i <= second; i++) {
|
||||
list.add(Integer.toString(i));
|
||||
}
|
||||
} else {
|
||||
for (int i = first; i >= second; i--) {
|
||||
list.add(Integer.toString(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
Log.d(K9.LOG_TAG, "Invalid range value", e);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.Account.FolderMode;
|
||||
import com.fsck.k9.Account.ScrollButtons;
|
||||
import com.fsck.k9.crypto.Apg;
|
||||
import com.fsck.k9.mail.store.StorageManager;
|
||||
import com.fsck.k9.preferences.Settings.*;
|
||||
@ -82,12 +81,6 @@ public class AccountSettings {
|
||||
s.put("goToUnreadMessageSearch", Settings.versions(
|
||||
new V(1, new BooleanSetting(false))
|
||||
));
|
||||
s.put("hideButtonsEnum", Settings.versions(
|
||||
new V(1, new EnumSetting(ScrollButtons.class, ScrollButtons.NEVER))
|
||||
));
|
||||
s.put("hideMoveButtonsEnum", Settings.versions(
|
||||
new V(1, new EnumSetting(ScrollButtons.class, ScrollButtons.NEVER))
|
||||
));
|
||||
s.put("idleRefreshMinutes", Settings.versions(
|
||||
new V(1, new IntegerResourceSetting(24, R.array.idle_refresh_period_values))
|
||||
));
|
||||
|
@ -35,7 +35,7 @@ public class Settings {
|
||||
*
|
||||
* @see SettingsExporter
|
||||
*/
|
||||
public static final int VERSION = 5;
|
||||
public static final int VERSION = 6;
|
||||
|
||||
public static Map<String, Object> validate(int version, Map<String,
|
||||
TreeMap<Integer, SettingsDescription>> settings,
|
||||
|
@ -23,8 +23,12 @@ import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple ContentProvider that allows file access to Email's attachments.<br/>
|
||||
* Warning! We make heavy assumptions about the Uris used by the {@link LocalStore} for an {@link Account} here.
|
||||
* A simple ContentProvider that allows file access to attachments.
|
||||
*
|
||||
* <p>
|
||||
* Warning! We make heavy assumptions about the Uris used by the {@link LocalStore} for an
|
||||
* {@link Account} here.
|
||||
* </p>
|
||||
*/
|
||||
public class AttachmentProvider extends ContentProvider {
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://com.fsck.k9.attachmentprovider");
|
||||
@ -33,6 +37,11 @@ public class AttachmentProvider extends ContentProvider {
|
||||
private static final String FORMAT_VIEW = "VIEW";
|
||||
private static final String FORMAT_THUMBNAIL = "THUMBNAIL";
|
||||
|
||||
private static final String[] DEFAULT_PROJECTION = new String[] {
|
||||
AttachmentProviderColumns._ID,
|
||||
AttachmentProviderColumns.DATA,
|
||||
};
|
||||
|
||||
public static class AttachmentProviderColumns {
|
||||
public static final String _ID = "_id";
|
||||
public static final String DATA = "_data";
|
||||
@ -40,6 +49,7 @@ public class AttachmentProvider extends ContentProvider {
|
||||
public static final String SIZE = "_size";
|
||||
}
|
||||
|
||||
|
||||
public static Uri getAttachmentUri(Account account, long id) {
|
||||
return getAttachmentUri(account.getUuid(), id, true);
|
||||
}
|
||||
@ -66,6 +76,47 @@ public class AttachmentProvider extends ContentProvider {
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void clear(Context context) {
|
||||
/*
|
||||
* We use the cache dir as a temporary directory (since Android doesn't give us one) so
|
||||
* on startup we'll clean up any .tmp files from the last run.
|
||||
*/
|
||||
File[] files = context.getCacheDir().listFiles();
|
||||
for (File file : files) {
|
||||
try {
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "Deleting file " + file.getCanonicalPath());
|
||||
}
|
||||
} catch (IOException ioe) { /* No need to log failure to log */ }
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the thumbnail of an attachment.
|
||||
*
|
||||
* @param context
|
||||
* The application context.
|
||||
* @param accountUuid
|
||||
* The UUID of the account the attachment belongs to.
|
||||
* @param attachmentId
|
||||
* The ID of the attachment the thumbnail was created for.
|
||||
*/
|
||||
public static void deleteThumbnail(Context context, String accountUuid, String attachmentId) {
|
||||
File file = getThumbnailFile(context, accountUuid, attachmentId);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static File getThumbnailFile(Context context, String accountUuid,
|
||||
String attachmentId) {
|
||||
String filename = "thmb_" + accountUuid + "_" + attachmentId + ".tmp";
|
||||
File dir = context.getCacheDir();
|
||||
return new File(dir, filename);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
/*
|
||||
@ -89,21 +140,6 @@ public class AttachmentProvider extends ContentProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void clear(Context lContext) {
|
||||
/*
|
||||
* We use the cache dir as a temporary directory (since Android doesn't give us one) so
|
||||
* on startup we'll clean up any .tmp files from the last run.
|
||||
*/
|
||||
File[] files = lContext.getCacheDir().listFiles();
|
||||
for (File file : files) {
|
||||
try {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "Deleting file " + file.getCanonicalPath());
|
||||
} catch (IOException ioe) {} // No need to log failure to log
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
List<String> segments = uri.getPathSegments();
|
||||
@ -114,67 +150,24 @@ public class AttachmentProvider extends ContentProvider {
|
||||
return getType(dbName, id, format);
|
||||
}
|
||||
|
||||
private String getType(String dbName, String id, String format) {
|
||||
if (FORMAT_THUMBNAIL.equals(format)) {
|
||||
return "image/png";
|
||||
} else {
|
||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
|
||||
try {
|
||||
final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app);
|
||||
|
||||
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
|
||||
if (FORMAT_VIEW.equals(format)) {
|
||||
return MimeUtility.getMimeTypeForViewing(attachmentInfo.type, attachmentInfo.name);
|
||||
} else {
|
||||
// When accessing the "raw" message we deliver the original MIME type.
|
||||
return attachmentInfo.type;
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to retrieve LocalStore for " + account, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File getFile(String dbName, String id)
|
||||
throws FileNotFoundException {
|
||||
try {
|
||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
final File attachmentsDir;
|
||||
attachmentsDir = StorageManager.getInstance(K9.app).getAttachmentDirectory(dbName,
|
||||
account.getLocalStorageProviderId());
|
||||
final File file = new File(attachmentsDir, id);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(file.getAbsolutePath());
|
||||
}
|
||||
return file;
|
||||
} catch (IOException e) {
|
||||
Log.w(K9.LOG_TAG, null, e);
|
||||
throw new FileNotFoundException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||
File file;
|
||||
|
||||
List<String> segments = uri.getPathSegments();
|
||||
String dbName = segments.get(0); // "/sdcard/..." is URL-encoded and makes up only 1 segment
|
||||
String id = segments.get(1);
|
||||
String accountUuid = segments.get(0);
|
||||
String attachmentId = segments.get(1);
|
||||
String format = segments.get(2);
|
||||
|
||||
if (FORMAT_THUMBNAIL.equals(format)) {
|
||||
int width = Integer.parseInt(segments.get(3));
|
||||
int height = Integer.parseInt(segments.get(4));
|
||||
String filename = "thmb_" + dbName + "_" + id + ".tmp";
|
||||
int index = dbName.lastIndexOf('/');
|
||||
if (index >= 0) {
|
||||
filename = /*dbName.substring(0, index + 1) + */"thmb_" + dbName.substring(index + 1) + "_" + id + ".tmp";
|
||||
}
|
||||
File dir = getContext().getCacheDir();
|
||||
File file = new File(dir, filename);
|
||||
|
||||
file = getThumbnailFile(getContext(), accountUuid, attachmentId);
|
||||
if (!file.exists()) {
|
||||
String type = getType(dbName, id, FORMAT_VIEW);
|
||||
String type = getType(accountUuid, attachmentId, FORMAT_VIEW);
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(getFile(dbName, id));
|
||||
FileInputStream in = new FileInputStream(getFile(accountUuid, attachmentId));
|
||||
try {
|
||||
Bitmap thumbnail = createThumbnail(type, in);
|
||||
if (thumbnail != null) {
|
||||
@ -187,40 +180,24 @@ public class AttachmentProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try { in.close(); } catch (Throwable ignore) {}
|
||||
try { in.close(); } catch (Throwable ignore) { /* ignore */ }
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
} else {
|
||||
return ParcelFileDescriptor.open(
|
||||
getFile(dbName, id),
|
||||
ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
file = getFile(accountUuid, attachmentId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String arg1, String[] arg2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
if (projection == null) {
|
||||
projection =
|
||||
new String[] {
|
||||
AttachmentProviderColumns._ID,
|
||||
AttachmentProviderColumns.DATA,
|
||||
};
|
||||
}
|
||||
|
||||
String[] columnNames = (projection == null) ? DEFAULT_PROJECTION : projection;
|
||||
|
||||
List<String> segments = uri.getPathSegments();
|
||||
String dbName = segments.get(0);
|
||||
@ -232,7 +209,6 @@ public class AttachmentProvider extends ContentProvider {
|
||||
dbName = dbName.substring(0, dbName.length() - 3);
|
||||
}
|
||||
|
||||
//String format = segments.get(2);
|
||||
final AttachmentInfo attachmentInfo;
|
||||
try {
|
||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
@ -242,10 +218,10 @@ public class AttachmentProvider extends ContentProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
MatrixCursor ret = new MatrixCursor(projection);
|
||||
Object[] values = new Object[projection.length];
|
||||
for (int i = 0, count = projection.length; i < count; i++) {
|
||||
String column = projection[i];
|
||||
MatrixCursor ret = new MatrixCursor(columnNames);
|
||||
Object[] values = new Object[columnNames.length];
|
||||
for (int i = 0, count = columnNames.length; i < count; i++) {
|
||||
String column = columnNames[i];
|
||||
if (AttachmentProviderColumns._ID.equals(column)) {
|
||||
values[i] = id;
|
||||
} else if (AttachmentProviderColumns.DATA.equals(column)) {
|
||||
@ -265,6 +241,56 @@ public class AttachmentProvider extends ContentProvider {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String arg1, String[] arg2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getType(String dbName, String id, String format) {
|
||||
String type;
|
||||
if (FORMAT_THUMBNAIL.equals(format)) {
|
||||
type = "image/png";
|
||||
} else {
|
||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
|
||||
try {
|
||||
final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app);
|
||||
|
||||
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
|
||||
if (FORMAT_VIEW.equals(format)) {
|
||||
type = MimeUtility.getMimeTypeForViewing(attachmentInfo.type, attachmentInfo.name);
|
||||
} else {
|
||||
// When accessing the "raw" message we deliver the original MIME type.
|
||||
type = attachmentInfo.type;
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to retrieve LocalStore for " + account, e);
|
||||
type = null;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
private File getFile(String dbName, String id) throws FileNotFoundException {
|
||||
Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
|
||||
File attachmentsDir = StorageManager.getInstance(K9.app).getAttachmentDirectory(dbName,
|
||||
account.getLocalStorageProviderId());
|
||||
|
||||
File file = new File(attachmentsDir, id);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
private Bitmap createThumbnail(String type, InputStream data) {
|
||||
if (MimeUtility.mimeTypeMatches(type, "image/*")) {
|
||||
return createImageThumbnail(data);
|
||||
|
@ -245,7 +245,7 @@ public class MessageProvider extends ContentProvider {
|
||||
final BlockingQueue<List<MessageInfoHolder>> queue = new SynchronousQueue<List<MessageInfoHolder>>();
|
||||
|
||||
// new code for integrated inbox, only execute this once as it will be processed afterwards via the listener
|
||||
final SearchAccount integratedInboxAccount = new SearchAccount(getContext(), true, null, null);
|
||||
final SearchAccount integratedInboxAccount = SearchAccount.createUnifiedInboxAccount(getContext());
|
||||
final MessagingController msgController = MessagingController.getInstance(K9.app);
|
||||
|
||||
msgController.searchLocalMessages(integratedInboxAccount, null,
|
||||
|
127
src/com/fsck/k9/provider/UnreadWidgetProvider.java
Normal file
127
src/com/fsck/k9/provider/UnreadWidgetProvider.java
Normal file
@ -0,0 +1,127 @@
|
||||
package com.fsck.k9.provider;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.AccountStats;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.UnreadWidgetConfiguration;
|
||||
import com.fsck.k9.activity.FolderList;
|
||||
import com.fsck.k9.activity.MessageList;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
public class UnreadWidgetProvider extends AppWidgetProvider {
|
||||
private static final int MAX_COUNT = 9999;
|
||||
|
||||
/**
|
||||
* Trigger update for all of our unread widgets.
|
||||
*
|
||||
* @param context
|
||||
* The {@code Context} object to use for the broadcast intent.
|
||||
*/
|
||||
public static void updateUnreadCount(Context context) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(appContext);
|
||||
|
||||
ComponentName thisWidget = new ComponentName(appContext, UnreadWidgetProvider.class);
|
||||
int[] widgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
|
||||
|
||||
Intent intent = new Intent(context, UnreadWidgetProvider.class);
|
||||
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);
|
||||
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public static void updateWidget(Context context, AppWidgetManager appWidgetManager,
|
||||
int appWidgetId, String accountUuid) {
|
||||
|
||||
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
|
||||
R.layout.unread_widget_layout);
|
||||
|
||||
int unreadCount = 0;
|
||||
String accountName = context.getString(R.string.app_name);
|
||||
Intent clickIntent = null;
|
||||
try {
|
||||
Account account = Preferences.getPreferences(context).getAccount(accountUuid);
|
||||
if (account != null) {
|
||||
AccountStats stats = new AccountStats();
|
||||
account.getLocalStore().getMessageCounts(stats);
|
||||
unreadCount = stats.unreadMessageCount;
|
||||
accountName = account.getDescription();
|
||||
if (K9.FOLDER_NONE.equals(account.getAutoExpandFolderName())) {
|
||||
clickIntent = FolderList.actionHandleAccountIntent(context, account, null);
|
||||
} else {
|
||||
clickIntent = MessageList.actionHandleFolderIntent(context, account,
|
||||
account.getAutoExpandFolderName());
|
||||
}
|
||||
clickIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (K9.DEBUG) {
|
||||
Log.e(K9.LOG_TAG, "Error getting widget configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (unreadCount <= 0) {
|
||||
// Hide TextView for unread count if there are no unread messages.
|
||||
remoteViews.setViewVisibility(R.id.unread_count, View.GONE);
|
||||
} else {
|
||||
remoteViews.setViewVisibility(R.id.unread_count, View.VISIBLE);
|
||||
|
||||
String displayCount = (unreadCount <= MAX_COUNT) ?
|
||||
String.valueOf(unreadCount) : String.valueOf(MAX_COUNT) + "+";
|
||||
remoteViews.setTextViewText(R.id.unread_count, displayCount);
|
||||
}
|
||||
|
||||
remoteViews.setTextViewText(R.id.account_name, accountName);
|
||||
|
||||
if (clickIntent == null) {
|
||||
// If the widget configuration couldn't be loaded we open the configuration
|
||||
// activity when the user clicks the widget.
|
||||
clickIntent = new Intent(context, UnreadWidgetConfiguration.class);
|
||||
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||
}
|
||||
clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId,
|
||||
clickIntent, 0);
|
||||
|
||||
remoteViews.setOnClickPendingIntent(R.id.unread_widget_layout, pendingIntent);
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when one or more widgets need to be updated.
|
||||
*/
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
for (int widgetId : appWidgetIds) {
|
||||
String accountUuid = UnreadWidgetConfiguration.getAccountUuid(context, widgetId);
|
||||
|
||||
updateWidget(context, appWidgetManager, widgetId, accountUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a widget instance is deleted.
|
||||
*/
|
||||
@Override
|
||||
public void onDeleted(Context context, int[] appWidgetIds) {
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
UnreadWidgetConfiguration.deleteWidgetConfiguration(context, appWidgetId);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,8 @@ import android.os.Environment;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
@ -33,12 +35,34 @@ import com.fsck.k9.helper.MediaScannerNotifier;
|
||||
import com.fsck.k9.helper.SizeFormatter;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.internet.MimeHeader;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBodyPart;
|
||||
import com.fsck.k9.provider.AttachmentProvider;
|
||||
|
||||
public class AttachmentView extends FrameLayout {
|
||||
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
|
||||
/**
|
||||
* Regular expression that represents characters we won't allow in file names.
|
||||
*
|
||||
* <p>
|
||||
* Allowed are:
|
||||
* <ul>
|
||||
* <li>word characters (letters, digits, and underscores): {@code \w}</li>
|
||||
* <li>spaces: {@code " "}</li>
|
||||
* <li>special characters: {@code !}, {@code #}, {@code $}, {@code %}, {@code &}, {@code '},
|
||||
* {@code (}, {@code )}, {@code -}, {@code @}, {@code ^}, {@code `}, <code>{</code>,
|
||||
* <code>}</code>, {@code ~}, {@code .}, {@code ,}</li>
|
||||
* </ul></p>
|
||||
*/
|
||||
private static final String INVALID_CHARACTERS = "[^\\w !#$%&'()\\-@\\^`{}~.,]+";
|
||||
|
||||
/**
|
||||
* Invalid characters in a file name are replaced by this character.
|
||||
*/
|
||||
private static final String REPLACEMENT_CHARACTER = "_";
|
||||
|
||||
|
||||
private Context mContext;
|
||||
public Button viewButton;
|
||||
@ -81,86 +105,125 @@ public class AttachmentView extends FrameLayout {
|
||||
*/
|
||||
public void showFileBrowser(AttachmentView caller);
|
||||
}
|
||||
public boolean populateFromPart(Part inputPart, Message message, Account account, MessagingController controller, MessagingListener listener) {
|
||||
try {
|
||||
part = (LocalAttachmentBodyPart) inputPart;
|
||||
|
||||
contentType = MimeUtility.unfoldAndDecode(part.getContentType());
|
||||
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
|
||||
/**
|
||||
* Populates this view with information about the attachment.
|
||||
*
|
||||
* <p>
|
||||
* This method also decides which attachments are displayed when the "show attachments" button
|
||||
* is pressed, and which attachments are only displayed after the "show more attachments"
|
||||
* button was pressed.<br>
|
||||
* Inline attachments with content ID and unnamed attachments fall into the second category.
|
||||
* </p>
|
||||
*
|
||||
* @param inputPart
|
||||
* @param message
|
||||
* @param account
|
||||
* @param controller
|
||||
* @param listener
|
||||
*
|
||||
* @return {@code true} for a regular attachment. {@code false}, otherwise.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error
|
||||
*/
|
||||
public boolean populateFromPart(Part inputPart, Message message, Account account,
|
||||
MessagingController controller, MessagingListener listener) throws MessagingException {
|
||||
boolean firstClassAttachment = true;
|
||||
part = (LocalAttachmentBodyPart) inputPart;
|
||||
|
||||
name = MimeUtility.getHeaderParameter(contentType, "name");
|
||||
if (name == null) {
|
||||
name = MimeUtility.getHeaderParameter(contentDisposition, "filename");
|
||||
}
|
||||
if (name == null) {
|
||||
return false;
|
||||
}
|
||||
contentType = MimeUtility.unfoldAndDecode(part.getContentType());
|
||||
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
|
||||
|
||||
mAccount = account;
|
||||
mMessage = message;
|
||||
mController = controller;
|
||||
mListener = listener;
|
||||
|
||||
size = Integer.parseInt(MimeUtility.getHeaderParameter(contentDisposition, "size"));
|
||||
contentType = MimeUtility.getMimeTypeForViewing(part.getMimeType(), name);
|
||||
TextView attachmentName = (TextView) findViewById(R.id.attachment_name);
|
||||
TextView attachmentInfo = (TextView) findViewById(R.id.attachment_info);
|
||||
ImageView attachmentIcon = (ImageView) findViewById(R.id.attachment_icon);
|
||||
viewButton = (Button) findViewById(R.id.view);
|
||||
downloadButton = (Button) findViewById(R.id.download);
|
||||
if ((!MimeUtility.mimeTypeMatches(contentType, K9.ACCEPTABLE_ATTACHMENT_VIEW_TYPES))
|
||||
|| (MimeUtility.mimeTypeMatches(contentType, K9.UNACCEPTABLE_ATTACHMENT_VIEW_TYPES))) {
|
||||
viewButton.setVisibility(View.GONE);
|
||||
}
|
||||
if ((!MimeUtility.mimeTypeMatches(contentType, K9.ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES))
|
||||
|| (MimeUtility.mimeTypeMatches(contentType, K9.UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES))) {
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
}
|
||||
if (size > K9.MAX_ATTACHMENT_DOWNLOAD_SIZE) {
|
||||
viewButton.setVisibility(View.GONE);
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
viewButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onViewButtonClicked();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
downloadButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onSaveButtonClicked();
|
||||
return;
|
||||
}
|
||||
});
|
||||
downloadButton.setOnLongClickListener(new OnLongClickListener() {
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
callback.showFileBrowser(AttachmentView.this);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
attachmentName.setText(name);
|
||||
attachmentInfo.setText(SizeFormatter.formatSize(mContext, size));
|
||||
Bitmap previewIcon = getPreviewIcon();
|
||||
if (previewIcon != null) {
|
||||
attachmentIcon.setImageBitmap(previewIcon);
|
||||
} else {
|
||||
attachmentIcon.setImageResource(R.drawable.attached_image_placeholder);
|
||||
}
|
||||
name = MimeUtility.getHeaderParameter(contentType, "name");
|
||||
if (name == null) {
|
||||
name = MimeUtility.getHeaderParameter(contentDisposition, "filename");
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "error ", e);
|
||||
if (name == null) {
|
||||
firstClassAttachment = false;
|
||||
String extension = MimeUtility.getExtensionByMimeType(contentType);
|
||||
name = "noname" + ((extension != null) ? "." + extension : "");
|
||||
}
|
||||
|
||||
return true;
|
||||
// Inline parts with a content-id are almost certainly components of an HTML message
|
||||
// not attachments. Only show them if the user pressed the button to show more
|
||||
// attachments.
|
||||
if (contentDisposition != null &&
|
||||
MimeUtility.getHeaderParameter(contentDisposition, null).matches("^(?i:inline)")
|
||||
&& part.getHeader(MimeHeader.HEADER_CONTENT_ID) != null) {
|
||||
firstClassAttachment = false;
|
||||
}
|
||||
|
||||
mAccount = account;
|
||||
mMessage = message;
|
||||
mController = controller;
|
||||
mListener = listener;
|
||||
|
||||
String sizeParam = MimeUtility.getHeaderParameter(contentDisposition, "size");
|
||||
if (sizeParam != null) {
|
||||
try {
|
||||
size = Integer.parseInt(sizeParam);
|
||||
} catch (NumberFormatException e) { /* ignore */ }
|
||||
}
|
||||
|
||||
contentType = MimeUtility.getMimeTypeForViewing(part.getMimeType(), name);
|
||||
TextView attachmentName = (TextView) findViewById(R.id.attachment_name);
|
||||
TextView attachmentInfo = (TextView) findViewById(R.id.attachment_info);
|
||||
ImageView attachmentIcon = (ImageView) findViewById(R.id.attachment_icon);
|
||||
viewButton = (Button) findViewById(R.id.view);
|
||||
downloadButton = (Button) findViewById(R.id.download);
|
||||
if ((!MimeUtility.mimeTypeMatches(contentType, K9.ACCEPTABLE_ATTACHMENT_VIEW_TYPES))
|
||||
|| (MimeUtility.mimeTypeMatches(contentType, K9.UNACCEPTABLE_ATTACHMENT_VIEW_TYPES))) {
|
||||
viewButton.setVisibility(View.GONE);
|
||||
}
|
||||
if ((!MimeUtility.mimeTypeMatches(contentType, K9.ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES))
|
||||
|| (MimeUtility.mimeTypeMatches(contentType, K9.UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES))) {
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
}
|
||||
if (size > K9.MAX_ATTACHMENT_DOWNLOAD_SIZE) {
|
||||
viewButton.setVisibility(View.GONE);
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
viewButton.setOnClickListener(this);
|
||||
downloadButton.setOnClickListener(this);
|
||||
downloadButton.setOnLongClickListener(this);
|
||||
|
||||
attachmentName.setText(name);
|
||||
attachmentInfo.setText(SizeFormatter.formatSize(mContext, size));
|
||||
Bitmap previewIcon = getPreviewIcon();
|
||||
if (previewIcon != null) {
|
||||
attachmentIcon.setImageBitmap(previewIcon);
|
||||
} else {
|
||||
attachmentIcon.setImageResource(R.drawable.attached_image_placeholder);
|
||||
}
|
||||
|
||||
return firstClassAttachment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.view: {
|
||||
onViewButtonClicked();
|
||||
break;
|
||||
}
|
||||
case R.id.download: {
|
||||
onSaveButtonClicked();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
if (view.getId() == R.id.download) {
|
||||
callback.showFileBrowser(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Bitmap getPreviewIcon() {
|
||||
@ -196,7 +259,8 @@ public class AttachmentView extends FrameLayout {
|
||||
*/
|
||||
public void writeFile(File directory) {
|
||||
try {
|
||||
File file = Utility.createUniqueFile(directory, name);
|
||||
String filename = sanitizeFilename(name);
|
||||
File file = Utility.createUniqueFile(directory, filename);
|
||||
Uri uri = AttachmentProvider.getAttachmentUri(mAccount, part.getAttachmentId());
|
||||
InputStream in = mContext.getContentResolver().openInputStream(uri);
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
@ -207,9 +271,25 @@ public class AttachmentView extends FrameLayout {
|
||||
attachmentSaved(file.toString());
|
||||
new MediaScannerNotifier(mContext, file);
|
||||
} catch (IOException ioe) {
|
||||
if (K9.DEBUG) {
|
||||
Log.e(K9.LOG_TAG, "Error saving attachment", ioe);
|
||||
}
|
||||
attachmentNotSaved();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private String sanitizeFilename(String filename) {
|
||||
return filename.replaceAll(INVALID_CHARACTERS, REPLACEMENT_CHARACTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* saves the file to the defaultpath setting in the config, or if the config
|
||||
* is not set => to the Environment
|
||||
|
@ -2,6 +2,8 @@ package com.fsck.k9.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.StyleSpan;
|
||||
@ -10,7 +12,10 @@ import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@ -25,14 +30,14 @@ import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.text.DateFormat;
|
||||
|
||||
public class MessageHeader extends LinearLayout {
|
||||
public class MessageHeader extends ScrollView implements OnClickListener {
|
||||
private Context mContext;
|
||||
private TextView mFromView;
|
||||
private TextView mDateView;
|
||||
@ -44,17 +49,19 @@ public class MessageHeader extends LinearLayout {
|
||||
private DateFormat mTimeFormat;
|
||||
|
||||
private View mChip;
|
||||
private View mChip2;
|
||||
private CheckBox mFlagged;
|
||||
private int defaultSubjectColor;
|
||||
private LinearLayout mToContainerView;
|
||||
private LinearLayout mCcContainerView;
|
||||
private TextView mAdditionalHeadersView;
|
||||
private View mAttachmentIcon;
|
||||
private View mAnsweredIcon;
|
||||
private Message mMessage;
|
||||
private Account mAccount;
|
||||
private FontSizes mFontSizes = K9.getFontSizes();
|
||||
private Contacts mContacts;
|
||||
private ImageView mShowAdditionalHeadersIcon;
|
||||
private SavedState mSavedState;
|
||||
|
||||
/**
|
||||
* Pair class is only available since API Level 5, so we need
|
||||
@ -79,7 +86,6 @@ public class MessageHeader extends LinearLayout {
|
||||
}
|
||||
|
||||
private void initializeLayout() {
|
||||
mAttachmentIcon = findViewById(R.id.attachment);
|
||||
mAnsweredIcon = findViewById(R.id.answered);
|
||||
mFromView = (TextView) findViewById(R.id.from);
|
||||
mToView = (TextView) findViewById(R.id.to);
|
||||
@ -89,17 +95,20 @@ public class MessageHeader extends LinearLayout {
|
||||
mSubjectView = (TextView) findViewById(R.id.subject);
|
||||
mAdditionalHeadersView = (TextView) findViewById(R.id.additional_headers_view);
|
||||
mChip = findViewById(R.id.chip);
|
||||
mChip2 = findViewById(R.id.chip2);
|
||||
mDateView = (TextView) findViewById(R.id.date);
|
||||
mTimeView = (TextView) findViewById(R.id.time);
|
||||
mFlagged = (CheckBox) findViewById(R.id.flagged);
|
||||
mShowAdditionalHeadersIcon = (ImageView) findViewById(R.id.show_additional_headers_icon);
|
||||
|
||||
|
||||
defaultSubjectColor = mSubjectView.getCurrentTextColor();
|
||||
mSubjectView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewSubject());
|
||||
mTimeView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewTime());
|
||||
mDateView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewDate());
|
||||
mAdditionalHeadersView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewAdditionalHeaders());
|
||||
mAdditionalHeadersView.setVisibility(View.GONE);
|
||||
mAttachmentIcon.setVisibility(View.GONE);
|
||||
hideAdditionalHeaders();
|
||||
|
||||
mAnsweredIcon.setVisibility(View.GONE);
|
||||
mFromView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewSender());
|
||||
mToView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewTo());
|
||||
@ -107,39 +116,45 @@ public class MessageHeader extends LinearLayout {
|
||||
((TextView) findViewById(R.id.to_label)).setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewTo());
|
||||
((TextView) findViewById(R.id.cc_label)).setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageViewCC());
|
||||
|
||||
setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onShowAdditionalHeaders();
|
||||
}
|
||||
});
|
||||
findViewById(R.id.show_additional_headers_area).setOnClickListener(this);
|
||||
mFromView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
mFromView.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mMessage != null) {
|
||||
try {
|
||||
final Address senderEmail = mMessage.getFrom()[0];
|
||||
mContacts.createContact(senderEmail);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't create contact", e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.show_additional_headers_area: {
|
||||
onShowAdditionalHeaders();
|
||||
break;
|
||||
}
|
||||
});
|
||||
case R.id.from: {
|
||||
onAddSenderToContacts();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onAddSenderToContacts() {
|
||||
if (mMessage != null) {
|
||||
try {
|
||||
final Address senderEmail = mMessage.getFrom()[0];
|
||||
mContacts.createContact(senderEmail);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't create contact", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnFlagListener(OnClickListener listener) {
|
||||
if (mFlagged == null)
|
||||
return;
|
||||
mFlagged.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
|
||||
public boolean additionalHeadersVisible() {
|
||||
if (mAdditionalHeadersView != null && mAdditionalHeadersView.getVisibility() == View.VISIBLE) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return (mAdditionalHeadersView != null &&
|
||||
mAdditionalHeadersView.getVisibility() == View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,7 +164,7 @@ public class MessageHeader extends LinearLayout {
|
||||
private void hideAdditionalHeaders() {
|
||||
mAdditionalHeadersView.setVisibility(View.GONE);
|
||||
mAdditionalHeadersView.setText("");
|
||||
|
||||
mShowAdditionalHeadersIcon.setImageResource(R.drawable.show_more);
|
||||
}
|
||||
|
||||
|
||||
@ -168,6 +183,7 @@ public class MessageHeader extends LinearLayout {
|
||||
// Show the additional headers that we have got.
|
||||
populateAdditionalHeadersView(additionalHeaders);
|
||||
mAdditionalHeadersView.setVisibility(View.VISIBLE);
|
||||
mShowAdditionalHeadersIcon.setImageResource(R.drawable.show_less);
|
||||
}
|
||||
if (!allHeadersDownloaded) {
|
||||
/*
|
||||
@ -227,14 +243,25 @@ public class MessageHeader extends LinearLayout {
|
||||
mToView.setText(to);
|
||||
mCcContainerView.setVisibility((cc != null && cc.length() > 0) ? View.VISIBLE : View.GONE);
|
||||
mCcView.setText(cc);
|
||||
mAttachmentIcon.setVisibility(((LocalStore.LocalMessage) message).hasAttachments() ? View.VISIBLE : View.GONE);
|
||||
mAnsweredIcon.setVisibility(message.isSet(Flag.ANSWERED) ? View.VISIBLE : View.GONE);
|
||||
mFlagged.setChecked(message.isSet(Flag.FLAGGED));
|
||||
mChip.setBackgroundDrawable(mAccount.generateColorChip().drawable());
|
||||
mChip.getBackground().setAlpha(!message.isSet(Flag.SEEN) ? 255 : 127);
|
||||
|
||||
int chipColor = mAccount.getChipColor();
|
||||
int chipColorAlpha = (!message.isSet(Flag.SEEN)) ? 255 : 127;
|
||||
mChip.setBackgroundColor(chipColor);
|
||||
mChip.getBackground().setAlpha(chipColorAlpha);
|
||||
mChip2.setBackgroundColor(chipColor);
|
||||
mChip2.getBackground().setAlpha(chipColorAlpha);
|
||||
|
||||
setVisibility(View.VISIBLE);
|
||||
if (mAdditionalHeadersView.getVisibility() == View.VISIBLE) {
|
||||
showAdditionalHeaders();
|
||||
|
||||
if (mSavedState != null) {
|
||||
if (mSavedState.additionalHeadersVisible) {
|
||||
showAdditionalHeaders();
|
||||
}
|
||||
mSavedState = null;
|
||||
} else {
|
||||
hideAdditionalHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,4 +321,61 @@ public class MessageHeader extends LinearLayout {
|
||||
mAdditionalHeadersView.setText(sb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
|
||||
SavedState savedState = new SavedState(superState);
|
||||
|
||||
savedState.additionalHeadersVisible = additionalHeadersVisible();
|
||||
|
||||
return savedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
if(!(state instanceof SavedState)) {
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
SavedState savedState = (SavedState)state;
|
||||
super.onRestoreInstanceState(savedState.getSuperState());
|
||||
|
||||
mSavedState = savedState;
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
boolean additionalHeadersVisible;
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Parcelable.Creator<SavedState> CREATOR =
|
||||
new Parcelable.Creator<SavedState>() {
|
||||
@Override
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
this.additionalHeadersVisible = (in.readInt() != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeInt((this.additionalHeadersVisible) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,20 @@
|
||||
package com.fsck.k9.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Picture;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.Toast;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
public class MessageWebView extends WebView {
|
||||
// Store a reference to the listeners in MessagingController. We can't fetch it directly since
|
||||
// we don't know the application name.
|
||||
private Set<MessagingListener> mListeners = null;
|
||||
|
||||
|
||||
/**
|
||||
* We use WebSettings.getBlockNetworkLoads() to prevent the WebView that displays email
|
||||
@ -75,7 +70,7 @@ public class MessageWebView extends WebView {
|
||||
this.setVerticalScrollBarEnabled(true);
|
||||
this.setVerticalScrollbarOverlay(true);
|
||||
this.setScrollBarStyle(SCROLLBARS_INSIDE_OVERLAY);
|
||||
|
||||
this.setLongClickable(true);
|
||||
|
||||
final WebSettings webSettings = this.getSettings();
|
||||
|
||||
@ -97,22 +92,16 @@ public class MessageWebView extends WebView {
|
||||
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
|
||||
}
|
||||
|
||||
if (Integer.parseInt(Build.VERSION.SDK) >= 9 ) {
|
||||
setOverScrollMode(OVER_SCROLL_NEVER);
|
||||
}
|
||||
|
||||
|
||||
webSettings.setTextSize(K9.getFontSizes().getMessageViewContent());
|
||||
|
||||
// Disable network images by default. This is overridden by preferences.
|
||||
blockNetworkData(true);
|
||||
|
||||
// Listen for when the screen has finished drawing.
|
||||
setPictureListener(new PictureListener() {
|
||||
@Override
|
||||
public void onNewPicture(WebView webView, Picture picture) {
|
||||
if (mListeners != null) {
|
||||
for (MessagingListener l : mListeners) {
|
||||
l.messageViewFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
@ -132,7 +121,15 @@ public class MessageWebView extends WebView {
|
||||
}
|
||||
}
|
||||
|
||||
public void setListeners(final Set<MessagingListener> listeners) {
|
||||
this.mListeners = listeners;
|
||||
public void wrapSetTitleBar(final View title) {
|
||||
try {
|
||||
Class<?> webViewClass = Class.forName("android.webkit.WebView");
|
||||
Method setEmbeddedTitleBar = webViewClass.getMethod("setEmbeddedTitleBar", View.class);
|
||||
setEmbeddedTitleBar.invoke(this, title);
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
Log.v(K9.LOG_TAG, "failed to find the setEmbeddedTitleBar method",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,16 +7,20 @@ import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.K9Activity;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
import com.fsck.k9.crypto.CryptoProvider;
|
||||
@ -24,49 +28,67 @@ import com.fsck.k9.crypto.PgpData;
|
||||
import com.fsck.k9.helper.Contacts;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.internet.MimeHeader;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
public class SingleMessageView extends LinearLayout {
|
||||
public class SingleMessageView extends LinearLayout implements OnClickListener {
|
||||
private boolean mScreenReaderEnabled;
|
||||
private MessageCryptoView mCryptoView;
|
||||
private MessageWebView mMessageContentView;
|
||||
private AccessibleWebView mAccessibleMessageContentView;
|
||||
private MessageHeader mHeaderContainer;
|
||||
private LinearLayout mAttachments;
|
||||
private View mShowPicturesSection;
|
||||
private Button mShowHiddenAttachments;
|
||||
private LinearLayout mHiddenAttachments;
|
||||
private View mShowPicturesAction;
|
||||
private View mShowMessageAction;
|
||||
private View mShowAttachmentsAction;
|
||||
private boolean mShowPictures;
|
||||
private boolean mHasAttachments;
|
||||
private Button mDownloadRemainder;
|
||||
private LayoutInflater mInflater;
|
||||
private Contacts mContacts;
|
||||
private AttachmentView.AttachmentFileDownloadCallback attachmentCallback;
|
||||
private LinearLayout mHeaderPlaceHolder;
|
||||
private LinearLayout mTitleBarHeaderContainer;
|
||||
private View mAttachmentsContainer;
|
||||
private LinearLayout mInsideAttachmentsContainer;
|
||||
private SavedState mSavedState;
|
||||
|
||||
public void initialize(Activity activity) {
|
||||
mMessageContentView = (MessageWebView) findViewById(R.id.message_content);
|
||||
mAccessibleMessageContentView = (AccessibleWebView) findViewById(R.id.accessible_message_content);
|
||||
mAttachments = (LinearLayout) findViewById(R.id.attachments);
|
||||
mMessageContentView.configure();
|
||||
|
||||
mHeaderPlaceHolder = (LinearLayout) findViewById(R.id.message_view_header_container);
|
||||
|
||||
mHeaderContainer = (MessageHeader) findViewById(R.id.header_container);
|
||||
|
||||
mAttachmentsContainer = findViewById(R.id.attachments_container);
|
||||
mInsideAttachmentsContainer = (LinearLayout) findViewById(R.id.inside_attachments_container);
|
||||
mAttachments = (LinearLayout) findViewById(R.id.attachments);
|
||||
mHiddenAttachments = (LinearLayout) findViewById(R.id.hidden_attachments);
|
||||
mHiddenAttachments.setVisibility(View.GONE);
|
||||
mShowHiddenAttachments = (Button) findViewById(R.id.show_hidden_attachments);
|
||||
mShowHiddenAttachments.setVisibility(View.GONE);
|
||||
mCryptoView = (MessageCryptoView) findViewById(R.id.layout_decrypt);
|
||||
mCryptoView.setActivity(activity);
|
||||
mCryptoView.setupChildViews();
|
||||
mShowPicturesSection = findViewById(R.id.show_pictures_section);
|
||||
mShowPicturesAction = findViewById(R.id.show_pictures);
|
||||
mShowMessageAction = findViewById(R.id.show_message);
|
||||
|
||||
mShowAttachmentsAction = findViewById(R.id.show_attachments);
|
||||
|
||||
mShowPictures = false;
|
||||
|
||||
mContacts = Contacts.getInstance(activity);
|
||||
|
||||
mInflater = activity.getLayoutInflater();
|
||||
mDownloadRemainder = (Button) findViewById(R.id.download_remainder);
|
||||
mMessageContentView.configure();
|
||||
|
||||
|
||||
mAttachments.setVisibility(View.GONE);
|
||||
mDownloadRemainder.setVisibility(View.GONE);
|
||||
mAttachmentsContainer.setVisibility(View.GONE);
|
||||
if (isScreenReaderActive(activity)) {
|
||||
mAccessibleMessageContentView.setVisibility(View.VISIBLE);
|
||||
mMessageContentView.setVisibility(View.GONE);
|
||||
@ -75,8 +97,62 @@ public class SingleMessageView extends LinearLayout {
|
||||
mAccessibleMessageContentView.setVisibility(View.GONE);
|
||||
mMessageContentView.setVisibility(View.VISIBLE);
|
||||
mScreenReaderEnabled = false;
|
||||
|
||||
mHeaderPlaceHolder.removeView(mHeaderContainer);
|
||||
// the HTC version of WebView tries to force the background of the
|
||||
// titlebar, which is really unfair.
|
||||
mHeaderContainer.setBackgroundColor(((K9Activity)activity).getThemeBackgroundColor());
|
||||
|
||||
mTitleBarHeaderContainer = new LinearLayout(activity);
|
||||
mTitleBarHeaderContainer.addView(mHeaderContainer);
|
||||
mMessageContentView.wrapSetTitleBar(mTitleBarHeaderContainer);
|
||||
}
|
||||
|
||||
mShowHiddenAttachments.setOnClickListener(this);
|
||||
mShowMessageAction.setOnClickListener(this);
|
||||
mShowAttachmentsAction.setOnClickListener(this);
|
||||
mShowPicturesAction.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.show_hidden_attachments: {
|
||||
onShowHiddenAttachments();
|
||||
break;
|
||||
}
|
||||
case R.id.show_message: {
|
||||
onShowMessage();
|
||||
break;
|
||||
}
|
||||
case R.id.show_attachments: {
|
||||
onShowAttachments();
|
||||
break;
|
||||
}
|
||||
case R.id.show_pictures: {
|
||||
setLoadPictures(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onShowHiddenAttachments() {
|
||||
mShowHiddenAttachments.setVisibility(View.GONE);
|
||||
mHiddenAttachments.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void onShowMessage() {
|
||||
showShowMessageAction(false);
|
||||
showAttachments(false);
|
||||
showShowAttachmentsAction(mHasAttachments);
|
||||
showMessageWebView(true);
|
||||
}
|
||||
|
||||
public void onShowAttachments() {
|
||||
showMessageWebView(false);
|
||||
showShowAttachmentsAction(false);
|
||||
showShowMessageAction(true);
|
||||
showAttachments(true);
|
||||
}
|
||||
|
||||
public SingleMessageView(Context context, AttributeSet attrs) {
|
||||
@ -102,15 +178,19 @@ public class SingleMessageView extends LinearLayout {
|
||||
// content://<nameofpackage>.providers.StatusProvider
|
||||
cursor = cr.query(Uri.parse("content://" + screenReader.serviceInfo.packageName
|
||||
+ ".providers.StatusProvider"), null, null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToFirst();
|
||||
// These content providers use a special cursor that only has
|
||||
// one element,
|
||||
// an integer that is 1 if the screen reader is running.
|
||||
status = cursor.getInt(0);
|
||||
cursor.close();
|
||||
if (status == 1) {
|
||||
return true;
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
// These content providers use a special cursor that only has
|
||||
// one element,
|
||||
// an integer that is 1 if the screen reader is running.
|
||||
status = cursor.getInt(0);
|
||||
if (status == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -135,20 +215,27 @@ public class SingleMessageView extends LinearLayout {
|
||||
public void setLoadPictures(boolean enable) {
|
||||
mMessageContentView.blockNetworkData(!enable);
|
||||
setShowPictures(enable);
|
||||
showShowPicturesSection(false);
|
||||
showShowPicturesAction(false);
|
||||
}
|
||||
|
||||
public Button downloadRemainderButton() {
|
||||
return mDownloadRemainder;
|
||||
}
|
||||
|
||||
public void showShowPicturesSection(boolean show) {
|
||||
mShowPicturesSection.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
public void showShowPicturesAction(boolean show) {
|
||||
mShowPicturesAction.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
public void showShowMessageAction(boolean show) {
|
||||
mShowMessageAction.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
public void showShowAttachmentsAction(boolean show) {
|
||||
mShowAttachmentsAction.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void setHeaders(final Message message, Account account) {
|
||||
try {
|
||||
mHeaderContainer.populate(message, account);
|
||||
mHeaderContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
|
||||
} catch (Exception me) {
|
||||
@ -177,9 +264,9 @@ public class SingleMessageView extends LinearLayout {
|
||||
return mHeaderContainer.additionalHeadersVisible();
|
||||
}
|
||||
|
||||
public void displayMessageBody(Account account, String folder, String uid, Message message, PgpData pgpData) throws MessagingException {
|
||||
// TODO - really this code path? this is an odd place to put it
|
||||
removeAllAttachments();
|
||||
public void setMessage(Account account, LocalMessage message, PgpData pgpData,
|
||||
MessagingController controller, MessagingListener listener) throws MessagingException {
|
||||
resetView();
|
||||
|
||||
String type;
|
||||
String text = pgpData.getDecryptedData();
|
||||
@ -187,13 +274,49 @@ public class SingleMessageView extends LinearLayout {
|
||||
type = "text/plain";
|
||||
} else {
|
||||
// getTextForDisplay() always returns HTML-ified content.
|
||||
text = ((LocalStore.LocalMessage) message).getTextForDisplay();
|
||||
text = message.getTextForDisplay();
|
||||
type = "text/html";
|
||||
}
|
||||
if (text != null) {
|
||||
final String emailText = text;
|
||||
final String contentType = type;
|
||||
loadBodyFromText(account.getCryptoProvider(), pgpData, message, emailText, contentType);
|
||||
loadBodyFromText(emailText, contentType);
|
||||
updateCryptoLayout(account.getCryptoProvider(), pgpData, message);
|
||||
} else {
|
||||
loadBodyFromUrl("file:///android_asset/empty.html");
|
||||
}
|
||||
|
||||
mHasAttachments = message.hasAttachments();
|
||||
|
||||
if (mHasAttachments) {
|
||||
renderAttachments(message, 0, message, account, controller, listener);
|
||||
}
|
||||
|
||||
mHiddenAttachments.setVisibility(View.GONE);
|
||||
|
||||
boolean lookForImages = true;
|
||||
if (mSavedState != null) {
|
||||
if (mSavedState.showPictures) {
|
||||
setLoadPictures(true);
|
||||
lookForImages = false;
|
||||
}
|
||||
|
||||
if (mSavedState.attachmentViewVisible) {
|
||||
onShowAttachments();
|
||||
} else {
|
||||
onShowMessage();
|
||||
}
|
||||
|
||||
if (mSavedState.hiddenAttachmentsVisible) {
|
||||
onShowHiddenAttachments();
|
||||
}
|
||||
|
||||
mSavedState = null;
|
||||
} else {
|
||||
onShowMessage();
|
||||
}
|
||||
|
||||
if (text != null && lookForImages) {
|
||||
// If the message contains external pictures and the "Show pictures"
|
||||
// button wasn't already pressed, see if the user's preferences has us
|
||||
// showing them anyway.
|
||||
@ -206,11 +329,9 @@ public class SingleMessageView extends LinearLayout {
|
||||
mContacts.isInContacts(from[0].getAddress()))) {
|
||||
setLoadPictures(true);
|
||||
} else {
|
||||
showShowPicturesSection(true);
|
||||
showShowPicturesAction(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loadBodyFromUrl("file:///android_asset/empty.html");
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,14 +341,13 @@ public class SingleMessageView extends LinearLayout {
|
||||
|
||||
}
|
||||
|
||||
public void loadBodyFromText(CryptoProvider cryptoProvider, PgpData pgpData, Message message, String emailText, String contentType) {
|
||||
private void loadBodyFromText(String emailText, String contentType) {
|
||||
if (mScreenReaderEnabled) {
|
||||
mAccessibleMessageContentView.loadDataWithBaseURL("http://", emailText, contentType, "utf-8", null);
|
||||
} else {
|
||||
mMessageContentView.loadDataWithBaseURL("http://", emailText, contentType, "utf-8", null);
|
||||
mMessageContentView.scrollTo(0, 0);
|
||||
}
|
||||
updateCryptoLayout(cryptoProvider, pgpData, message);
|
||||
|
||||
}
|
||||
|
||||
@ -235,6 +355,23 @@ public class SingleMessageView extends LinearLayout {
|
||||
mCryptoView.updateLayout(cp, pgpData, message);
|
||||
}
|
||||
|
||||
public void showAttachments(boolean show) {
|
||||
mAttachmentsContainer.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
boolean showHidden = (show && mHiddenAttachments.getVisibility() == View.GONE &&
|
||||
mHiddenAttachments.getChildCount() > 0);
|
||||
mShowHiddenAttachments.setVisibility(showHidden ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (show) {
|
||||
moveHeaderToLayout();
|
||||
} else {
|
||||
moveHeaderToWebViewTitleBar();
|
||||
}
|
||||
}
|
||||
|
||||
public void showMessageWebView(boolean show) {
|
||||
mMessageContentView.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void setAttachmentsEnabled(boolean enabled) {
|
||||
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
|
||||
AttachmentView attachment = (AttachmentView) mAttachments.getChildAt(i);
|
||||
@ -243,7 +380,6 @@ public class SingleMessageView extends LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void removeAllAttachments() {
|
||||
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
|
||||
mAttachments.removeView(mAttachments.getChildAt(i));
|
||||
@ -259,25 +395,27 @@ public class SingleMessageView extends LinearLayout {
|
||||
renderAttachments(mp.getBodyPart(i), depth + 1, message, account, controller, listener);
|
||||
}
|
||||
} else if (part instanceof LocalStore.LocalAttachmentBodyPart) {
|
||||
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
|
||||
// Inline parts with a content-id are almost certainly components of an HTML message
|
||||
// not attachments. Don't show attachment download buttons for them.
|
||||
if (contentDisposition != null &&
|
||||
MimeUtility.getHeaderParameter(contentDisposition, null).matches("^(?i:inline)")
|
||||
&& part.getHeader(MimeHeader.HEADER_CONTENT_ID) != null) {
|
||||
return;
|
||||
}
|
||||
AttachmentView view = (AttachmentView)mInflater.inflate(R.layout.message_view_attachment, null);
|
||||
view.setCallback(attachmentCallback);
|
||||
if (view.populateFromPart(part, message, account, controller, listener)) {
|
||||
addAttachment(view);
|
||||
|
||||
try {
|
||||
if (view.populateFromPart(part, message, account, controller, listener)) {
|
||||
addAttachment(view);
|
||||
} else {
|
||||
addHiddenAttachment(view);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Error adding attachment view", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addAttachment(View attachmentView) {
|
||||
mAttachments.addView(attachmentView);
|
||||
mAttachments.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void addHiddenAttachment(View attachmentView) {
|
||||
mHiddenAttachments.addView(attachmentView);
|
||||
}
|
||||
|
||||
public void zoom(KeyEvent event) {
|
||||
@ -297,11 +435,26 @@ public class SingleMessageView extends LinearLayout {
|
||||
}
|
||||
|
||||
public void resetView() {
|
||||
mDownloadRemainder.setVisibility(View.GONE);
|
||||
setLoadPictures(false);
|
||||
mMessageContentView.scrollTo(0, 0);
|
||||
mHeaderContainer.setVisibility(View.GONE);
|
||||
mMessageContentView.clearView();
|
||||
showShowAttachmentsAction(false);
|
||||
showShowMessageAction(false);
|
||||
showShowPicturesAction(false);
|
||||
mAttachments.removeAllViews();
|
||||
mHiddenAttachments.removeAllViews();
|
||||
|
||||
/*
|
||||
* Clear the WebView content
|
||||
*
|
||||
* For some reason WebView.clearView() doesn't clear the contents when the WebView changes
|
||||
* its size because the button to download the complete message was previously shown and
|
||||
* is now hidden.
|
||||
*/
|
||||
loadBodyFromText("", "text/plain");
|
||||
}
|
||||
|
||||
public void resetHeaderView() {
|
||||
mHeaderContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public AttachmentView.AttachmentFileDownloadCallback getAttachmentCallback() {
|
||||
@ -313,20 +466,85 @@ public class SingleMessageView extends LinearLayout {
|
||||
this.attachmentCallback = attachmentCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a copy of the {@link com.fsck.k9.controller.MessagingController#getListeners()}. This method will also
|
||||
* pass along these listeners to the underlying views.
|
||||
* @param listeners Set of listeners.
|
||||
*/
|
||||
public void setListeners(final Set<MessagingListener> listeners) {
|
||||
if (!mScreenReaderEnabled) {
|
||||
if (mMessageContentView != null) {
|
||||
mMessageContentView.setListeners(listeners);
|
||||
private void moveHeaderToLayout() {
|
||||
if (mTitleBarHeaderContainer != null && mTitleBarHeaderContainer.getChildCount() != 0) {
|
||||
mTitleBarHeaderContainer.removeView(mHeaderContainer);
|
||||
mInsideAttachmentsContainer.addView(mHeaderContainer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void moveHeaderToWebViewTitleBar() {
|
||||
if (mTitleBarHeaderContainer != null && mTitleBarHeaderContainer.getChildCount() == 0) {
|
||||
mInsideAttachmentsContainer.removeView(mHeaderContainer);
|
||||
mTitleBarHeaderContainer.addView(mHeaderContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
|
||||
SavedState savedState = new SavedState(superState);
|
||||
|
||||
savedState.attachmentViewVisible = (mAttachmentsContainer != null &&
|
||||
mAttachmentsContainer.getVisibility() == View.VISIBLE);
|
||||
savedState.hiddenAttachmentsVisible = (mHiddenAttachments != null &&
|
||||
mHiddenAttachments.getVisibility() == View.VISIBLE);
|
||||
savedState.showPictures = mShowPictures;
|
||||
|
||||
return savedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
if(!(state instanceof SavedState)) {
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
SavedState savedState = (SavedState)state;
|
||||
super.onRestoreInstanceState(savedState.getSuperState());
|
||||
|
||||
mSavedState = savedState;
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
boolean attachmentViewVisible;
|
||||
boolean hiddenAttachmentsVisible;
|
||||
boolean showPictures;
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Parcelable.Creator<SavedState> CREATOR =
|
||||
new Parcelable.Creator<SavedState>() {
|
||||
@Override
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
} else {
|
||||
if (mAccessibleMessageContentView != null) {
|
||||
mAccessibleMessageContentView.setListeners(listeners);
|
||||
|
||||
@Override
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
this.attachmentViewVisible = (in.readInt() != 0);
|
||||
this.hiddenAttachmentsVisible = (in.readInt() != 0);
|
||||
this.showPictures = (in.readInt() != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeInt((this.attachmentViewVisible) ? 1 : 0);
|
||||
out.writeInt((this.hiddenAttachmentsVisible) ? 1 : 0);
|
||||
out.writeInt((this.showPictures) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
188
tests/src/com/fsck/k9/mail/internet/ViewablesTest.java
Normal file
188
tests/src/com/fsck/k9/mail/internet/ViewablesTest.java
Normal file
@ -0,0 +1,188 @@
|
||||
package com.fsck.k9.mail.internet;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import android.test.AndroidTestCase;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Message.RecipientType;
|
||||
import com.fsck.k9.mail.internet.MimeUtility.ViewableContainer;
|
||||
|
||||
public class ViewablesTest extends AndroidTestCase {
|
||||
|
||||
public void testSimplePlainTextMessage() throws MessagingException {
|
||||
String bodyText = "K-9 Mail rocks :>";
|
||||
|
||||
// Create text/plain body
|
||||
TextBody body = new TextBody(bodyText);
|
||||
|
||||
// Create message
|
||||
MimeMessage message = new MimeMessage();
|
||||
message.setBody(body);
|
||||
|
||||
// Extract text
|
||||
ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
|
||||
|
||||
String expectedText = bodyText;
|
||||
String expectedHtml =
|
||||
"<html><head/><body>" +
|
||||
"<pre style=\"white-space: pre-wrap; word-wrap:break-word; " +
|
||||
"font-family: sans-serif\">" +
|
||||
"K-9 Mail rocks :>" +
|
||||
"</pre>" +
|
||||
"</body></html>";
|
||||
|
||||
assertEquals(expectedText, container.text);
|
||||
assertEquals(expectedHtml, container.html);
|
||||
}
|
||||
|
||||
public void testSimpleHtmlMessage() throws MessagingException {
|
||||
String bodyText = "<strong>K-9 Mail</strong> rocks :>";
|
||||
|
||||
// Create text/plain body
|
||||
TextBody body = new TextBody(bodyText);
|
||||
|
||||
// Create message
|
||||
MimeMessage message = new MimeMessage();
|
||||
message.setHeader("Content-Type", "text/html");
|
||||
message.setBody(body);
|
||||
|
||||
// Extract text
|
||||
ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
|
||||
|
||||
String expectedText = "K-9 Mail rocks :>";
|
||||
String expectedHtml =
|
||||
"<html><head/><body>" +
|
||||
bodyText +
|
||||
"</body></html>";
|
||||
|
||||
assertEquals(expectedText, container.text);
|
||||
assertEquals(expectedHtml, container.html);
|
||||
}
|
||||
|
||||
public void testMultipartPlainTextMessage() throws MessagingException {
|
||||
String bodyText1 = "text body 1";
|
||||
String bodyText2 = "text body 2";
|
||||
|
||||
// Create text/plain bodies
|
||||
TextBody body1 = new TextBody(bodyText1);
|
||||
TextBody body2 = new TextBody(bodyText2);
|
||||
|
||||
// Create multipart/mixed part
|
||||
MimeMultipart multipart = new MimeMultipart();
|
||||
MimeBodyPart bodyPart1 = new MimeBodyPart(body1, "text/plain");
|
||||
MimeBodyPart bodyPart2 = new MimeBodyPart(body2, "text/plain");
|
||||
multipart.addBodyPart(bodyPart1);
|
||||
multipart.addBodyPart(bodyPart2);
|
||||
|
||||
// Create message
|
||||
MimeMessage message = new MimeMessage();
|
||||
message.setBody(multipart);
|
||||
|
||||
// Extract text
|
||||
ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
|
||||
|
||||
String expectedText =
|
||||
bodyText1 + "\n\n" +
|
||||
"------------------------------------------------------------------------\n\n" +
|
||||
bodyText2;
|
||||
String expectedHtml =
|
||||
"<html><head/><body>" +
|
||||
"<pre style=\"white-space: pre-wrap; word-wrap:break-word; " +
|
||||
"font-family: sans-serif\">" +
|
||||
bodyText1 +
|
||||
"</pre>" +
|
||||
"<p style=\"margin-top: 2.5em; margin-bottom: 1em; " +
|
||||
"border-bottom: 1px solid #000\"></p>" +
|
||||
"<pre style=\"white-space: pre-wrap; word-wrap:break-word; " +
|
||||
"font-family: sans-serif\">" +
|
||||
bodyText2 +
|
||||
"</pre>" +
|
||||
"</body></html>";
|
||||
|
||||
|
||||
assertEquals(expectedText, container.text);
|
||||
assertEquals(expectedHtml, container.html);
|
||||
}
|
||||
|
||||
public void testTextPlusRfc822Message() throws MessagingException {
|
||||
Locale.setDefault(Locale.US);
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
String bodyText = "Some text here";
|
||||
String innerBodyText = "Hey there. I'm inside a message/rfc822 (inline) attachment.";
|
||||
|
||||
// Create text/plain body
|
||||
TextBody textBody = new TextBody(bodyText);
|
||||
|
||||
// Create inner text/plain body
|
||||
TextBody innerBody = new TextBody(innerBodyText);
|
||||
|
||||
// Create message/rfc822 body
|
||||
MimeMessage innerMessage = new MimeMessage();
|
||||
innerMessage.addSentDate(new Date(112, 02, 17));
|
||||
innerMessage.setRecipients(RecipientType.TO, new Address[] { new Address("to@example.com") });
|
||||
innerMessage.setSubject("Subject");
|
||||
innerMessage.setFrom(new Address("from@example.com"));
|
||||
innerMessage.setBody(innerBody);
|
||||
|
||||
// Create multipart/mixed part
|
||||
MimeMultipart multipart = new MimeMultipart();
|
||||
MimeBodyPart bodyPart1 = new MimeBodyPart(textBody, "text/plain");
|
||||
MimeBodyPart bodyPart2 = new MimeBodyPart(innerMessage, "message/rfc822");
|
||||
bodyPart2.setHeader("Content-Disposition", "inline; filename=\"message.eml\"");
|
||||
multipart.addBodyPart(bodyPart1);
|
||||
multipart.addBodyPart(bodyPart2);
|
||||
|
||||
// Create message
|
||||
MimeMessage message = new MimeMessage();
|
||||
message.setBody(multipart);
|
||||
|
||||
// Extract text
|
||||
ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
|
||||
|
||||
String expectedText =
|
||||
bodyText +
|
||||
"\n\n" +
|
||||
"----- message.eml ------------------------------------------------------" +
|
||||
"\n\n" +
|
||||
"From: from@example.com" + "\n" +
|
||||
"To: to@example.com" + "\n" +
|
||||
"Sent: Sat Mar 17 00:00:00 GMT+00:00 2012" + "\n" +
|
||||
"Subject: Subject" + "\n" +
|
||||
"\n" +
|
||||
innerBodyText;
|
||||
String expectedHtml =
|
||||
"<html><head/><body>" +
|
||||
"<pre style=\"white-space: pre-wrap; word-wrap:break-word; " +
|
||||
"font-family: sans-serif\">" +
|
||||
bodyText +
|
||||
"</pre>" +
|
||||
"<p style=\"margin-top: 2.5em; margin-bottom: 1em; border-bottom: " +
|
||||
"1px solid #000\">message.eml</p>" +
|
||||
"<table style=\"border: 0\">" +
|
||||
"<tr>" +
|
||||
"<th style=\"text-align: left; vertical-align: top;\">From:</th>" +
|
||||
"<td>from@example.com</td>" +
|
||||
"</tr><tr>" +
|
||||
"<th style=\"text-align: left; vertical-align: top;\">To:</th>" +
|
||||
"<td>to@example.com</td>" +
|
||||
"</tr><tr>" +
|
||||
"<th style=\"text-align: left; vertical-align: top;\">Sent:</th>" +
|
||||
"<td>Sat Mar 17 00:00:00 GMT+00:00 2012</td>" +
|
||||
"</tr><tr>" +
|
||||
"<th style=\"text-align: left; vertical-align: top;\">Subject:</th>" +
|
||||
"<td>Subject</td>" +
|
||||
"</tr>" +
|
||||
"</table>" +
|
||||
"<pre style=\"white-space: pre-wrap; word-wrap:break-word; " +
|
||||
"font-family: sans-serif\">" +
|
||||
innerBodyText +
|
||||
"</pre>" +
|
||||
"</body></html>";
|
||||
|
||||
assertEquals(expectedText, container.text);
|
||||
assertEquals(expectedHtml, container.html);
|
||||
}
|
||||
}
|
125
tests/src/com/fsck/k9/mail/store/imap/ImapUtilityTest.java
Normal file
125
tests/src/com/fsck/k9/mail/store/imap/ImapUtilityTest.java
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import android.test.MoreAsserts;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class ImapUtilityTest extends TestCase {
|
||||
/**
|
||||
* Test getting elements of an IMAP sequence set.
|
||||
*/
|
||||
public void testGetImapSequenceValues() {
|
||||
String[] expected;
|
||||
List<String> actual;
|
||||
|
||||
// Test valid sets
|
||||
expected = new String[] {"1"};
|
||||
actual = ImapUtility.getImapSequenceValues("1");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[] {"1", "3", "2"};
|
||||
actual = ImapUtility.getImapSequenceValues("1,3,2");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[] {"4", "5", "6"};
|
||||
actual = ImapUtility.getImapSequenceValues("4:6");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[] {"9", "8", "7"};
|
||||
actual = ImapUtility.getImapSequenceValues("9:7");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[] {"1", "2", "3", "4", "9", "8", "7"};
|
||||
actual = ImapUtility.getImapSequenceValues("1,2:4,9:7");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
// Test partially invalid sets
|
||||
expected = new String[] { "1", "5" };
|
||||
actual = ImapUtility.getImapSequenceValues("1,x,5");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[] { "1", "2", "3" };
|
||||
actual = ImapUtility.getImapSequenceValues("a:d,1:3");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
// Test invalid sets
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapSequenceValues("");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapSequenceValues(null);
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapSequenceValues("a");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapSequenceValues("1:x");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting elements of an IMAP range.
|
||||
*/
|
||||
public void testGetImapRangeValues() {
|
||||
String[] expected;
|
||||
List<String> actual;
|
||||
|
||||
// Test valid ranges
|
||||
expected = new String[] {"1", "2", "3"};
|
||||
actual = ImapUtility.getImapRangeValues("1:3");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[] {"16", "15", "14"};
|
||||
actual = ImapUtility.getImapRangeValues("16:14");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
// Test in-valid ranges
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapRangeValues("");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapRangeValues(null);
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapRangeValues("a");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapRangeValues("6");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapRangeValues("1:3,6");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapRangeValues("1:x");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
|
||||
expected = new String[0];
|
||||
actual = ImapUtility.getImapRangeValues("1:*");
|
||||
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user