Pixel perfect: Message view

Still waiting for a few missing assets, but it's done other than that.

The divider change is already merged; new layouts all use the framework style.

It's basically all layout changes, except for:
- Now the recepient address line has "Show details", which shows a dialog with
  all to/cc/bcc addresses with the timestamp.
- Now invite response buttons are checkboxes.
  (But the basic behavior doesn't change -- once you check a response,
  the message will be gone, so you can't change the response later.)

Copied message_header_bg from gmail manuall.  It'll look silly without it.

Bug 3138021
Bug 3307021

Change-Id: I6f7eb91d6104c3143a5c58b0c4c6c19929cea477
This commit is contained in:
Makoto Onuki 2011-01-10 13:35:47 -08:00
parent 07f5f60b8f
commit a826d3fb03
21 changed files with 996 additions and 452 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1007 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<!-- xlarge -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="64dip"
android:orientation="horizontal"
>
<!-- STOPSHIP default icon is not final -->
<ImageView
android:id="@+id/attachment_icon"
android:layout_width="64dip"
android:layout_height="match_parent"
android:layout_weight="0"
android:src="@drawable/attached_image_placeholder"
android:scaleType="fitCenter"
/>
<LinearLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:divider="?android:attr/dividerVertical"
android:showDividers="middle"
android:dividerPadding="16dip"
>
<RelativeLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginLeft="32dip"
android:layout_marginRight="16dip"
>
<ProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
android:visibility="invisible"
/>
<TextView
android:id="@+id/attachment_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_above="@id/progress"
android:layout_marginLeft="16dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
android:gravity="right|bottom"
/>
<TextView
android:id="@+id/attachment_name"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/attachment_info"
android:layout_above="@id/progress"
android:textSize="18dip"
android:textColor="@color/text_primary_color"
android:singleLine="true"
android:ellipsize="middle"
android:gravity="left|bottom"
/>
</RelativeLayout>
<!-- Buttons -->
<LinearLayout
android:layout_width="224dip"
android:layout_height="match_parent"
android:layout_weight="0"
android:orientation="horizontal"
android:divider="?android:attr/dividerVertical"
android:showDividers="middle"
android:dividerPadding="16dip"
>
<Button
android:id="@+id/load"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
style="@android:style/Widget.Holo.Button.Borderless"
android:padding="0dip"
android:text="@string/message_view_attachment_load_action"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center"
android:visibility="gone"
/>
<Button
android:id="@+id/cancel"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
style="@android:style/Widget.Holo.Button.Borderless"
android:padding="0dip"
android:text="@string/message_view_attachment_cancel_action"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center"
android:visibility="gone"
/>
<Button
android:id="@+id/view"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
style="@android:style/Widget.Holo.Button.Borderless"
android:padding="0dip"
android:text="@string/message_view_attachment_view_action"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center"
android:visibility="gone"
/>
<Button
android:id="@+id/save"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
style="@android:style/Widget.Holo.Button.Borderless"
android:padding="0dip"
android:text="@string/message_view_attachment_save_action"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center"
android:visibility="gone"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -43,72 +43,71 @@
<TextView
android:id="@+id/subject"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_height="40dip"
android:layout_alignParentTop="true"
android:layout_marginTop="30dip"
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
android:singleLine="false"
android:ellipsize="none"
android:layout_marginTop="0dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:singleLine="true"
android:ellipsize="end"
android:textSize="18dip"
android:textColor="@color/text_primary_color"
android:textStyle="bold"
/>
<!-- Horizontal line below subject -->
<View
android:id="@+id/top_divider"
android:layout_width="0dip"
android:layout_below="@id/subject"
android:layout_alignLeft="@id/subject"
android:layout_alignRight="@id/subject"
android:layout_marginTop="8dip"
android:layout_marginBottom="16dip"
style="@style/message_view_horizontal_divider"
android:gravity="left|center_vertical"
/>
<!-- Badge -->
<ImageButton
android:id="@+id/badge"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_gravity="center_vertical"
android:layout_alignTop="@id/top_divider"
android:layout_width="64dip"
android:layout_height="64dip"
android:layout_below="@id/subject"
android:layout_alignLeft="@id/subject"
android:padding="0dip"
android:scaleType="fitCenter"
/>
<!-- Presence icon -->
<ImageView
android:id="@+id/presence"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/top_divider"
<!-- Background for sender name and buttons -->
<View
android:id="@+id/header_background"
android:layout_width="0dip"
android:layout_height="64dip"
android:layout_below="@id/subject"
android:layout_toRightOf="@id/badge"
android:layout_marginLeft="16dip"
android:layout_marginRight="12dip"
android:src="@drawable/presence_inactive"
android:background="@drawable/quickcontact_presence_bg"
android:layout_alignRight="@id/subject"
android:background="@drawable/message_header_bg"
/>
<!-- Fold -->
<ImageView
android:id="@+id/header_background_fold"
android:layout_width="32dip"
android:layout_height="20dip"
android:layout_below="@id/header_background"
android:layout_alignRight="@id/header_background"
android:src="@drawable/message_header_fold"
android:padding="0dip"
/>
<!--
reply, reply-all, forward, Star
They can all be gone; don't refer to them from other views.
Instead refer to this outer layout.
These buttons can all be gone at runtime, so don't refer to individual buttons from
other views. Instead refer to this outer layout.
-->
<LinearLayout
android:id="@+id/forward_reply_buttons"
android:layout_width="wrap_content"
android:layout_height="24dip"
android:layout_alignRight="@id/subject"
android:layout_below="@id/top_divider"
android:layout_height="wrap_content"
android:layout_marginTop="16dip"
android:layout_marginLeft="8dip"
android:layout_marginRight="32dip"
android:layout_alignTop="@id/header_background"
android:layout_alignRight="@id/header_background"
android:orientation="horizontal"
>
<ImageButton
android:id="@+id/reply"
android:layout_width="32dip"
android:layout_height="match_parent"
android:layout_marginRight="16dip"
android:layout_marginLeft="16dip"
android:layout_height="32dip"
android:layout_marginRight="32dip"
android:src="@drawable/ic_reply"
android:visibility="gone"
style="@android:style/Widget.Holo.Button.Borderless"
@ -116,9 +115,8 @@
<ImageButton
android:id="@+id/reply_all"
android:layout_width="32dip"
android:layout_height="match_parent"
android:layout_marginRight="16dip"
android:layout_marginLeft="16dip"
android:layout_height="32dip"
android:layout_marginRight="32dip"
android:src="@drawable/ic_reply_all"
android:visibility="gone"
style="@android:style/Widget.Holo.Button.Borderless"
@ -126,9 +124,8 @@
<ImageButton
android:id="@+id/forward"
android:layout_width="32dip"
android:layout_height="match_parent"
android:layout_marginRight="16dip"
android:layout_marginLeft="16dip"
android:layout_height="32dip"
android:layout_marginRight="32dip"
android:src="@drawable/ic_forward"
android:visibility="gone"
style="@android:style/Widget.Holo.Button.Borderless"
@ -136,151 +133,127 @@
<ImageButton
android:id="@+id/favorite"
android:layout_width="32dip"
android:layout_height="match_parent"
android:layout_marginRight="16dip"
android:layout_marginLeft="16dip"
android:layout_height="32dip"
android:src="@drawable/ic_star_none_holo_light"
android:visibility="gone"
style="@android:style/Widget.Holo.Button.Borderless"
/>
</LinearLayout>
</LinearLayout>
<!-- Sender display name -->
<TextView
android:id="@+id/from_name"
<!-- presence, sender address, sender name: make them center_vertical -->
<LinearLayout
android:id="@+id/presence_from_name"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_below="@id/top_divider"
android:layout_toRightOf="@id/presence"
android:layout_height="0dip"
android:layout_marginLeft="32dip"
android:layout_marginRight="16dip"
android:layout_alignTop="@id/header_background"
android:layout_alignBottom="@id/header_background"
android:layout_alignLeft="@id/header_background"
android:layout_toLeftOf="@id/forward_reply_buttons"
android:textSize="18dip"
android:textColor="@color/text_primary_color"
android:gravity="center_vertical"
android:orientation="vertical"
>
<LinearLayout
android:id="@+id/presence_from_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="left|center_vertical"
>
<!-- Presence icon -->
<ImageView
android:id="@+id/presence"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:src="@android:drawable/presence_offline"
/>
<!-- Sender display name -->
<TextView
android:id="@+id/from_name"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="4dip"
android:textSize="18dip"
android:textColor="@color/text_primary_color"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
android:gravity="left|center_vertical"
/>
</LinearLayout>
<!-- From address -->
<TextView
android:id="@+id/from_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
android:ellipsize="end"
android:paddingRight="6dip"
/>
</LinearLayout>
<!-- Addresses, timestamp -->
<Button
android:id="@+id/show_details"
android:layout_width="wrap_content"
android:layout_height="40dip"
android:layout_below="@id/header_background"
android:layout_toLeftOf="@id/header_background_fold"
android:layout_marginRight="16dip"
style="@android:style/Widget.Holo.Button.Borderless"
android:padding="0dip"
android:gravity="center_vertical"
android:text="@string/message_view_show_details"
android:textSize="14dip"
android:textColor="@color/text_ternary_color"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
/>
<!-- From address -->
<TextView
android:id="@+id/from_address"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/presence"
android:layout_below="@id/presence"
android:layout_toLeftOf="@id/forward_reply_buttons"
android:layout_marginTop="2dip"
android:layout_marginBottom="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
android:ellipsize="end"
android:paddingRight="6dip"
/>
<!-- To: and timestamp -->
<TextView
android:text="@string/message_view_show_details"
android:id="@+id/datetime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/from_address"
android:layout_alignRight="@id/subject"
android:layout_height="40dip"
android:layout_below="@id/header_background"
android:layout_toLeftOf="@id/show_details"
android:layout_marginRight="48dip"
android:gravity="center_vertical"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:textColor="@color/text_ternary_color"
android:singleLine="true"
/>
<TextView
android:id="@+id/to_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/datetime"
android:layout_alignLeft="@id/from_address"
android:layout_marginRight="4dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
android:text="@string/message_compose_to_label"
/>
<TextView
android:id="@+id/to"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/datetime"
android:layout_toRightOf="@id/to_label"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
/>
<!-- Cc -->
<LinearLayout
android:id="@+id/cc_container"
android:id="@+id/addresses"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_below="@id/to_label"
android:layout_alignLeft="@id/to_label"
android:layout_alignRight="@id/subject"
android:visibility="gone"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
android:text="@string/message_compose_cc_label"
/>
<TextView
android:id="@+id/cc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
android:ellipsize="end"
/>
</LinearLayout>
<!-- Bcc -->
<LinearLayout
android:id="@+id/bcc_container"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_below="@id/cc_container"
android:layout_alignLeft="@id/to_label"
android:layout_alignRight="@id/subject"
android:visibility="gone"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
android:text="@string/message_compose_bcc_label"
/>
<TextView
android:id="@+id/bcc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="true"
android:ellipsize="end"
/>
</LinearLayout>
android:layout_height="40dip"
android:layout_below="@id/header_background"
android:layout_alignLeft="@id/badge"
android:layout_toLeftOf="@id/datetime"
android:layout_marginRight="16dip"
android:gravity="center_vertical"
android:textSize="14dip"
android:textColor="@color/text_ternary_color"
android:singleLine="true"
android:ellipsize="end"
/>
<View
android:id="@+id/below_header_divider"
android:id="@+id/below_address_divider"
android:layout_width="0dip"
android:layout_below="@id/bcc_container"
android:layout_alignLeft="@id/bcc_container"
android:layout_alignRight="@id/subject"
android:layout_marginTop="8dip"
android:layout_marginBottom="2dip"
android:layout_below="@id/show_details"
android:layout_alignLeft="@id/addresses"
android:layout_alignRight="@id/show_details"
style="@style/message_view_horizontal_divider"
/>
@ -291,60 +264,87 @@
<LinearLayout
android:layout_width="0dip"
android:layout_height="0dip"
android:layout_below="@id/below_header_divider"
android:layout_alignLeft="@id/below_header_divider"
android:layout_alignRight="@id/below_header_divider"
android:layout_below="@id/below_address_divider"
android:layout_alignLeft="@id/badge"
android:layout_alignRight="@id/header_background"
android:layout_alignParentBottom="true"
android:orientation="vertical"
>
<!-- Tabs -->
<!-- Tabs + divider -->
<!-- Can't use a RelativeLayout, because tabs can be GONE -->
<LinearLayout
android:id="@+id/message_tabs_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="6dip"
android:paddingRight="6dip"
android:divider="?android:attr/dividerHorizontal"
android:showDividers="beginning|middle|end"
android:dividerPadding="8dip"
android:orientation="vertical"
>
<Button
android:id="@+id/show_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/message_view_show_message_action"
style="@android:style/Widget.Holo.Light.Tab"
/>
<Button
android:id="@+id/show_invite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/message_view_show_invite_action"
style="@android:style/Widget.Holo.Light.Tab"
/>
<Button
android:id="@+id/show_attachments"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
style="@android:style/Widget.Holo.Light.Tab"
/>
<!-- filler -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dip"
android:orientation="horizontal"
>
<Button
android:id="@+id/show_message"
android:layout_width="128dip"
android:layout_height="match_parent"
android:layout_gravity="center"
android:text="@string/message_view_show_message_action"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:singleLine="true"
android:ellipsize="end"
style="@android:style/Widget.Holo.Light.Tab"
/>
<Button
android:id="@+id/show_invite"
android:layout_width="128dip"
android:layout_height="match_parent"
android:layout_gravity="center"
android:text="@string/message_view_show_invite_action"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:singleLine="true"
android:ellipsize="end"
style="@android:style/Widget.Holo.Light.Tab"
/>
<Button
android:id="@+id/show_attachments"
android:layout_width="128dip"
android:layout_height="match_parent"
android:layout_gravity="center"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:singleLine="true"
android:ellipsize="end"
style="@android:style/Widget.Holo.Light.Tab"
/>
<!-- filler -->
<View
android:layout_width="0dip"
android:layout_height="0dip"
android:layout_weight="1"
/>
<Button
android:id="@+id/show_pictures"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
style="@android:style/Widget.Holo.Button.Borderless"
android:padding="0dip"
android:text="@string/message_view_show_pictures_action"
android:textSize="14dip"
android:textColor="@color/text_ternary_color"
android:textStyle="bold"
/>
<View
android:layout_width="48dip"
android:layout_height="0dip"
/>
</LinearLayout>
<View
android:layout_width="0dip"
android:layout_height="0dip"
android:layout_weight="1"
/>
<Button
android:id="@+id/show_pictures"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/message_view_show_pictures_action"
style="@android:style/Widget.Holo.Light.Tab"
android:layout_width="match_parent"
android:layout_marginBottom="16dip"
style="@style/message_view_horizontal_divider"
/>
</LinearLayout>
@ -385,8 +385,9 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dip"
android:background="#ffffff"
android:background="@color/message_view_info_back_color"
android:divider="?android:attr/dividerHorizontal"
android:showDividers="beginning|middle|end"
/>
</ScrollView>

View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<!-- xlarge -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/invite_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/message_view_info_back_color"
android:orientation="vertical"
>
<View
android:layout_width="match_parent"
style="@style/message_view_horizontal_divider"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="64dip"
android:orientation="horizontal"
>
<!-- STOPSHIP Put icon here -->
<ImageView
android:layout_width="64dip"
android:layout_height="64dip"
android:layout_weight="0"
android:src="@null"
android:visibility="gone"
/>
<LinearLayout
android:layout_width="200dip"
android:layout_weight="1"
android:layout_height="64dip"
android:orientation="horizontal"
android:divider="?android:attr/dividerVertical"
android:showDividers="middle"
android:dividerPadding="16dip"
>
<RelativeLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
>
<CheckBox
android:id="@+id/accept"
android:layout_width="104dip"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginLeft="32dip"
android:text="@string/message_view_invite_accept"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:singleLine="true"
android:ellipsize="end"
/>
<CheckBox
android:id="@+id/maybe"
android:layout_width="104dip"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@id/accept"
android:layout_marginLeft="24dip"
android:text="@string/message_view_invite_maybe"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:singleLine="true"
android:ellipsize="end"
/>
<CheckBox
android:id="@+id/decline"
android:layout_width="104dip"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@id/maybe"
android:layout_marginLeft="24dip"
android:text="@string/message_view_invite_decline"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:singleLine="true"
android:ellipsize="end"
/>
<!-- "Going?" -->
<TextView
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_above="@id/accept"
android:layout_marginLeft="32dip"
android:layout_marginRight="32dip"
android:singleLine="true"
android:ellipsize="end"
android:text="@string/message_view_invite_text"
android:textSize="18dip"
android:textColor="@color/text_primary_color"
android:gravity="left|bottom"
/>
</RelativeLayout>
<Button
android:id="@+id/invite_link"
android:layout_width="192dip"
android:layout_height="match_parent"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
style="@android:style/Widget.Holo.Button.Borderless"
android:padding="0dip"
android:text="@string/message_view_invite_view"
android:textSize="14dip"
android:textColor="@color/text_primary_color"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center"
/>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
style="@style/message_view_horizontal_divider"
/>
</LinearLayout>

View File

@ -4,9 +4,9 @@
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.
@ -14,6 +14,8 @@
limitations under the License.
-->
<!-- Non-xlarge -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:stretchColumns="2"
android:shrinkColumns="2"
>
<TableRow
android:id="@+id/date_row"
>
<TextView
android:layout_column="1"
android:text="@string/message_view_date_label"
android:padding="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:textStyle="bold"
/>
<TextView
android:id="@+id/date"
android:padding="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:singleLine="false"
/>
</TableRow>
<TableRow
android:id="@+id/to_row"
>
<TextView
android:layout_column="1"
android:text="@string/message_view_to_label"
android:padding="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:textStyle="bold"
/>
<TextView
android:id="@+id/to"
android:padding="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
/>
</TableRow>
<TableRow
android:id="@+id/cc_row"
>
<TextView
android:layout_column="1"
android:text="@string/message_view_cc_label"
android:padding="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:textStyle="bold"
/>
<TextView
android:id="@+id/cc"
android:padding="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
/>
</TableRow>
<TableRow
android:id="@+id/bcc_row"
>
<TextView
android:layout_column="1"
android:text="@string/message_view_bcc_label"
android:padding="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
android:textStyle="bold"
/>
<TextView
android:id="@+id/bcc"
android:padding="8dip"
android:textSize="14dip"
android:textColor="@color/text_secondary_color"
/>
</TableRow>
</TableLayout>

View File

@ -46,7 +46,7 @@
/>
<ImageView
android:id="@+id/presence"
android:src="@drawable/presence_inactive"
android:src="@android:drawable/presence_offline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dip"
@ -116,71 +116,17 @@
android:layout_marginLeft="4dip"
android:singleLine="true" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/message_view_to_label" />
<TextView
android:id="@+id/to"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="0dip"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:singleLine="false"
android:ellipsize="none" />
</LinearLayout>
<LinearLayout
android:id="@+id/cc_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/message_view_cc_label" />
<TextView
android:id="@+id/cc"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="0dip"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:singleLine="false"
android:ellipsize="none" />
</LinearLayout>
<LinearLayout
android:id="@+id/bcc_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/message_view_bcc_label" />
<TextView
android:id="@+id/bcc"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="0dip"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:singleLine="false"
android:ellipsize="none" />
</LinearLayout>
<TextView
android:id="@+id/addresses"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="0dip"
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:singleLine="false"
android:ellipsize="none" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

View File

@ -14,6 +14,8 @@
limitations under the License.
-->
<!-- Non-xlarge -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/invite_section"
@ -84,7 +86,7 @@
android:textColor="?android:attr/textColorPrimaryInverse"
/>
<TextView
<CheckBox
android:id="@+id/accept"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -98,7 +100,7 @@
android:focusable="true"
/>
<TextView
<CheckBox
android:id="@+id/maybe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -112,7 +114,7 @@
android:focusable="true"
/>
<TextView
<CheckBox
android:id="@+id/decline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@ -32,6 +32,7 @@
<!-- Standard text colors -->
<color name="text_primary_color">#ffffff</color>
<color name="text_secondary_color">#666666</color>
<color name="text_ternary_color">#888888</color>
<!-- STOPSHIP Use same color as EditText tray? -->
<color name="divider_color">#ff808080</color>
@ -57,4 +58,6 @@
<color name="widget_unread_count_color">#4d4d4d</color>
<color name="widget_default_text_color">#000000</color>
<color name="widget_light_text_color">#666666</color>
<color name="message_view_info_back_color">#deecfa</color>
</resources>

View File

@ -111,6 +111,8 @@
<string name="add_cc_bcc_action">+ Cc/Bcc</string>
<!-- Menu item -->
<string name="add_attachment_action">Add attachment</string>
<!-- Generic button [CHAR LIMIT=16] -->
<string name="close_action">Close</string>
<!-- Menu item (debug screen) -->
<string name="dump_settings_action">Dump settings</string>
<!-- Appears in choose attachment dialog title -->
@ -300,6 +302,8 @@
<!-- Label for CC field in read message view -->
<string name="message_view_cc_label">Cc:</string>
<string name="message_view_bcc_label">Bcc:</string>
<!-- Label for the date field. [CHAR LIMIT=20]-->
<string name="message_view_date_label">Date:</string>
<!-- Button name to view an attachment (in another activity) [CHAR LIMIT=10]-->
<string name="message_view_attachment_view_action">View</string>
<!-- Button name, to load an attachment from the mail server [CHAR LIMIT=10]-->
@ -351,6 +355,10 @@ save attachment.</string>
<!-- Toast shown following a meeting invite reply, declined -->
<string name="message_view_invite_toast_no">You have declined this invitation</string>
<!--Confirmation dialog title shown when user tries to delete messages. [CHAR LIMIT=16] -->
<!-- Link label to show to/cc/bcc of the curent message [CHAR LIMIT=32] -->
<string name="message_view_show_details">Show details</string>
<!-- Title of the dialog box to show to/cc/bcc of the message. [CHAR LIMIT=32] -->
<string name="message_view_message_details_dialog_title">Message Details</string>
<!-- Title of the EML viewer activity. [CHAR LIMIT=32] -->
<string name="eml_view_title">Viewing

View File

@ -33,11 +33,11 @@ import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
@ -47,7 +47,11 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.provider.OpenableColumns;
import android.telephony.TelephonyManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Base64;
import android.util.Log;
import android.widget.AbsListView;
@ -1172,6 +1176,20 @@ public class Utility {
return name;
}
/**
* Append a bold span to a {@link SpannableStringBuilder}.
*/
public static SpannableStringBuilder appendBold(SpannableStringBuilder ssb, String text) {
if (!TextUtils.isEmpty(text)) {
SpannableString ss = new SpannableString(text);
ss.setSpan(new StyleSpan(Typeface.BOLD), 0, ss.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(ss);
}
return ssb;
}
/**
* Stringify a cursor for logging purpose.
*/

View File

@ -16,7 +16,6 @@
package com.android.email.activity;
import com.android.email.R;
import com.android.email.Utility;
import android.content.AsyncTaskLoader;
@ -37,7 +36,7 @@ import android.util.Log;
* Loader to load presence statuses and the contact photoes.
*/
public class ContactStatusLoader extends AsyncTaskLoader<ContactStatusLoader.Result> {
public static final int PRESENCE_UNKNOWN_RESOURCE_ID = R.drawable.presence_inactive;
public static final int PRESENCE_UNKNOWN_RESOURCE_ID = android.R.drawable.presence_offline;
/** email address -> photo id, presence */
/* package */ static final String[] PROJECTION_PHOTO_ID_PRESENCE = new String[] {

View File

@ -36,8 +36,9 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
import java.security.InvalidParameterException;
@ -47,7 +48,8 @@ import java.security.InvalidParameterException;
*
* See {@link MessageViewBase} for the class relation diagram.
*/
public class MessageViewFragment extends MessageViewFragmentBase {
public class MessageViewFragment extends MessageViewFragmentBase
implements CheckBox.OnCheckedChangeListener {
private ImageView mFavoriteIcon;
private View mInviteSection;
@ -56,9 +58,9 @@ public class MessageViewFragment extends MessageViewFragmentBase {
private View mForwardButton;
// calendar meeting invite answers
private TextView mMeetingYes;
private TextView mMeetingMaybe;
private TextView mMeetingNo;
private CheckBox mMeetingYes;
private CheckBox mMeetingMaybe;
private CheckBox mMeetingNo;
private MessageCommandButtonView mCommandButtons;
private int mPreviousMeetingResponse = -1;
@ -163,9 +165,9 @@ public class MessageViewFragment extends MessageViewFragmentBase {
mReplyButton = view.findViewById(R.id.reply);
mReplyAllButton = view.findViewById(R.id.reply_all);
mForwardButton = view.findViewById(R.id.forward);
mMeetingYes = (TextView) view.findViewById(R.id.accept);
mMeetingMaybe = (TextView) view.findViewById(R.id.maybe);
mMeetingNo = (TextView) view.findViewById(R.id.decline);
mMeetingYes = (CheckBox) view.findViewById(R.id.accept);
mMeetingMaybe = (CheckBox) view.findViewById(R.id.maybe);
mMeetingNo = (CheckBox) view.findViewById(R.id.decline);
// Star is only visible on this fragment (as opposed to MessageFileViewFragment.)
view.findViewById(R.id.favorite).setVisibility(View.VISIBLE);
@ -174,9 +176,9 @@ public class MessageViewFragment extends MessageViewFragmentBase {
mReplyButton.setOnClickListener(this);
mReplyAllButton.setOnClickListener(this);
mForwardButton.setOnClickListener(this);
mMeetingYes.setOnClickListener(this);
mMeetingMaybe.setOnClickListener(this);
mMeetingNo.setOnClickListener(this);
mMeetingYes.setOnCheckedChangeListener(this);
mMeetingMaybe.setOnCheckedChangeListener(this);
mMeetingNo.setOnCheckedChangeListener(this);
view.findViewById(R.id.invite_link).setOnClickListener(this);
// Show the command buttons at the bottom.
@ -244,7 +246,9 @@ public class MessageViewFragment extends MessageViewFragmentBase {
@Override
protected void resetView() {
super.resetView();
// TODO Hide command buttons. (Careful when to re-show it)
mMeetingYes.setChecked(false);
mMeetingNo.setChecked(false);
mMeetingMaybe.setChecked(false);
}
@Override
@ -362,18 +366,6 @@ public class MessageViewFragment extends MessageViewFragmentBase {
onClickFavorite();
return;
case R.id.accept:
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_ACCEPTED,
R.string.message_view_invite_toast_yes);
return;
case R.id.maybe:
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_TENTATIVE,
R.string.message_view_invite_toast_maybe);
return;
case R.id.decline:
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_DECLINED,
R.string.message_view_invite_toast_no);
return;
case R.id.invite_link:
onInviteLinkClicked();
return;
@ -381,6 +373,25 @@ public class MessageViewFragment extends MessageViewFragmentBase {
super.onClick(view);
}
@Override
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
if (!isChecked) return;
switch (view.getId()) {
case R.id.accept:
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_ACCEPTED,
R.string.message_view_invite_toast_yes);
return;
case R.id.maybe:
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_TENTATIVE,
R.string.message_view_invite_toast_maybe);
return;
case R.id.decline:
onRespondToInvite(EmailServiceConstants.MEETING_REQUEST_DECLINED,
R.string.message_view_invite_toast_no);
return;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {

View File

@ -45,6 +45,7 @@ import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@ -55,6 +56,7 @@ import android.os.Environment;
import android.os.Handler;
import android.provider.ContactsContract;
import android.provider.ContactsContract.QuickContact;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
@ -105,11 +107,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
private TextView mFromNameView;
private TextView mFromAddressView;
private TextView mDateTimeView;
private TextView mToView;
private TextView mCcView;
private View mCcContainerView;
private TextView mBccView;
private View mBccContainerView;
private TextView mAddressesView;
private WebView mMessageContentView;
private LinearLayout mAttachments;
private View mTabSection;
@ -117,6 +115,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
private ImageView mSenderPresenceView;
private View mMainView;
private View mLoadingProgress;
private Button mShowDetailsButton;
private TextView mMessageTab;
private TextView mAttachmentTab;
@ -289,11 +288,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
mSubjectView = (TextView) view.findViewById(R.id.subject);
mFromNameView = (TextView) view.findViewById(R.id.from_name);
mFromAddressView = (TextView) view.findViewById(R.id.from_address);
mToView = (TextView) view.findViewById(R.id.to);
mCcView = (TextView) view.findViewById(R.id.cc);
mCcContainerView = view.findViewById(R.id.cc_container);
mBccView = (TextView) view.findViewById(R.id.bcc);
mBccContainerView = view.findViewById(R.id.bcc_container);
mAddressesView = (TextView) view.findViewById(R.id.addresses);
mDateTimeView = (TextView) view.findViewById(R.id.datetime);
mMessageContentView = (WebView) view.findViewById(R.id.message_content);
mAttachments = (LinearLayout) view.findViewById(R.id.attachments);
@ -302,6 +297,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
mSenderPresenceView = (ImageView) view.findViewById(R.id.presence);
mMainView = view.findViewById(R.id.main_panel);
mLoadingProgress = view.findViewById(R.id.loading_progress);
mShowDetailsButton = (Button) view.findViewById(R.id.show_details);
mFromNameView.setOnClickListener(this);
mFromAddressView.setOnClickListener(this);
@ -318,6 +314,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
mAttachmentTab.setOnClickListener(this);
mShowPicturesTab.setOnClickListener(this);
mInviteTab.setOnClickListener(this);
mShowDetailsButton.setOnClickListener(this);
mAttachmentsScroll = view.findViewById(R.id.attachments_scroll);
mInviteScroll = view.findViewById(R.id.invite_scroll);
@ -560,8 +557,10 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
makeVisible(mMessageTab, messageTabVisible);
makeVisible(mInviteTab, (tabFlags & TAB_FLAGS_HAS_INVITE) != 0);
makeVisible(mAttachmentTab, (tabFlags & TAB_FLAGS_HAS_ATTACHMENT) != 0);
makeVisible(mShowPicturesTab, (tabFlags & TAB_FLAGS_HAS_PICTURES) != 0);
mShowPicturesTab.setEnabled((tabFlags & TAB_FLAGS_PICTURE_LOADED) == 0);
final boolean hasPictures = (tabFlags & TAB_FLAGS_HAS_PICTURES) != 0;
final boolean pictureLoaded = (tabFlags & TAB_FLAGS_PICTURE_LOADED) != 0;
makeVisible(mShowPicturesTab, hasPictures && !pictureLoaded);
mAttachmentTab.setText(mContext.getResources().getQuantityString(
R.plurals.message_view_show_attachments_action,
@ -725,7 +724,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
if (AttachmentDownloadService.getQueueSize() == 0) {
// Set to invisible; if the button is still in this state one second from now, we'll
// assume the download won't start right away, and we make the cancel button visible
attachment.cancelButton.setVisibility(View.INVISIBLE);
attachment.cancelButton.setVisibility(View.GONE);
// Create the timed task that will change the button state
new AsyncTask<Void, Void, Void>() {
@Override
@ -737,7 +736,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
}
@Override
protected void onPostExecute(Void result) {
if (attachment.cancelButton.getVisibility() == View.INVISIBLE) {
if (attachment.cancelButton.getVisibility() != View.VISIBLE) {
attachment.cancelButton.setVisibility(View.VISIBLE);
}
}
@ -757,23 +756,21 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
attachment.loadButton.setVisibility(View.VISIBLE);
attachment.cancelButton.setVisibility(View.GONE);
ProgressBar bar = attachment.progressView;
bar.setVisibility(View.GONE);
bar.setVisibility(View.INVISIBLE);
}
}
/**
* Called by ControllerResults. Show the "View" and "Save" buttons; hide "Load"
* Called by ControllerResults. Show the "View" and "Save" buttons; hide "Load" and "Stop"
*
* @param attachmentId the attachment that was just downloaded
*/
private void doFinishLoadAttachment(long attachmentId) {
AttachmentInfo info = findAttachmentInfo(attachmentId);
if (info != null) {
info.loadButton.setVisibility(View.INVISIBLE);
info.loadButton.setVisibility(View.GONE);
if (!TextUtils.isEmpty(info.name)) {
info.saveButton.setVisibility(View.VISIBLE);
}
info.cancelButton.setVisibility(View.GONE);
info.saveButton.setVisibility(TextUtils.isEmpty(info.name) ? View.GONE : View.VISIBLE);
info.viewButton.setVisibility(View.VISIBLE);
}
}
@ -789,6 +786,19 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
}
}
private void onShowDetails() {
if (mMessage == null) {
return; // shouldn't happen
}
String date = formatDate(mMessage.mTimeStamp, true);
String to = Address.toString(Address.unpack(mMessage.mTo));
String cc = Address.toString(Address.unpack(mMessage.mCc));
String bcc = Address.toString(Address.unpack(mMessage.mBcc));
MessageViewMessageDetailsDialog dialog = MessageViewMessageDetailsDialog.newInstance(
getActivity(), date, to, cc, bcc);
dialog.show(getActivity().getFragmentManager(), null);
}
@Override
public void onClick(View view) {
if (!isMessageOpen()) {
@ -825,6 +835,9 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
case R.id.show_pictures:
onShowPicturesInHtml();
break;
case R.id.show_details:
onShowDetails();
break;
}
}
@ -1132,7 +1145,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
attachmentProgress.setProgress(100);
attachmentSave.setVisibility(View.VISIBLE);
attachmentView.setVisibility(View.VISIBLE);
attachmentLoad.setVisibility(View.INVISIBLE);
attachmentLoad.setVisibility(View.GONE);
attachmentCancel.setVisibility(View.GONE);
Bitmap previewIcon = getPreviewIcon(attachmentInfo);
@ -1141,8 +1154,9 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
}
} else {
// Show "Load"; hide "View" and "Save"
attachmentSave.setVisibility(View.INVISIBLE);
attachmentView.setVisibility(View.INVISIBLE);
attachmentSave.setVisibility(View.GONE);
attachmentView.setVisibility(View.GONE);
// If the attachment is queued, show the indeterminate progress bar. From this point,.
// any progress changes will cause this to be replaced by the normal progress bar
if (AttachmentDownloadService.isAttachmentQueued(attachment.mId)){
@ -1227,24 +1241,43 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
mFromNameView.setText(" ");
mFromAddressView.setText(" ");
}
mDateTimeView.setText(formatDate(message.mTimeStamp));
mToView.setText(Address.toFriendly(Address.unpack(message.mTo)));
String friendlyCc = Address.toFriendly(Address.unpack(message.mCc));
mCcView.setText(friendlyCc);
mCcContainerView.setVisibility((friendlyCc != null) ? View.VISIBLE : View.GONE);
String friendlyBcc = Address.toFriendly(Address.unpack(message.mBcc));
mBccView.setText(friendlyBcc);
mBccContainerView.setVisibility((friendlyBcc != null) ? View.VISIBLE : View.GONE);
mDateTimeView.setText(formatDate(message.mTimeStamp, false));
// To/Cc/Bcc
final Resources res = mContext.getResources();
final SpannableStringBuilder ssb = new SpannableStringBuilder();
final String friendlyTo = Address.toFriendly(Address.unpack(message.mTo));
final String friendlyCc = Address.toFriendly(Address.unpack(message.mCc));
final String friendlyBcc = Address.toFriendly(Address.unpack(message.mBcc));
if (!TextUtils.isEmpty(friendlyTo)) {
Utility.appendBold(ssb, res.getString(R.string.message_view_to_label));
ssb.append(" ");
ssb.append(friendlyTo);
}
if (!TextUtils.isEmpty(friendlyCc)) {
ssb.append(" ");
Utility.appendBold(ssb, res.getString(R.string.message_view_cc_label));
ssb.append(" ");
ssb.append(friendlyCc);
}
if (!TextUtils.isEmpty(friendlyBcc)) {
ssb.append(" ");
Utility.appendBold(ssb, res.getString(R.string.message_view_bcc_label));
ssb.append(" ");
ssb.append(friendlyBcc);
}
mAddressesView.setText(ssb);
}
private String formatDate(long millis) {
private String formatDate(long millis, boolean withYear) {
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb);
DateUtils.formatDateRange(mContext, formatter, millis, millis,
DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_ABBREV_ALL
| DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_NO_YEAR);
| (withYear ? DateUtils.FORMAT_SHOW_YEAR : DateUtils.FORMAT_NO_YEAR));
return sb.toString();
}

View File

@ -0,0 +1,91 @@
/*
* 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.android.email.activity;
import com.android.email.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
/**
* "Message Details" dialog box.
*/
public class MessageViewMessageDetailsDialog extends DialogFragment {
private static final String BUNDLE_DATE = "date";
private static final String BUNDLE_TO = "to";
private static final String BUNDLE_CC = "cc";
private static final String BUNDLE_BCC = "bcc";
private static final String TextView = null;
public static MessageViewMessageDetailsDialog newInstance(Activity parent, String date,
String to, String cc, String bcc) {
MessageViewMessageDetailsDialog dialog = new MessageViewMessageDetailsDialog();
Bundle args = new Bundle();
args.putString(BUNDLE_DATE, date);
args.putString(BUNDLE_TO, to);
args.putString(BUNDLE_CC, cc);
args.putString(BUNDLE_BCC, bcc);
dialog.setArguments(args);
return dialog;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
AlertDialog.Builder builder = new AlertDialog.Builder(activity).setTitle(
activity.getResources().getString(
R.string.message_view_message_details_dialog_title));
builder.setNegativeButton(R.string.close_action, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
});
builder.setView(initView());
return builder.show();
}
private View initView() {
View root = getActivity().getLayoutInflater().inflate(R.layout.message_view_details, null);
Bundle args = getArguments();
setText(root, args.getString(BUNDLE_DATE), R.id.date, R.id.date_row);
setText(root, args.getString(BUNDLE_TO), R.id.to, R.id.to_row);
setText(root, args.getString(BUNDLE_CC), R.id.cc, R.id.cc_row);
setText(root, args.getString(BUNDLE_BCC), R.id.bcc, R.id.bcc_row);
return root;
}
private static void setText(View root, String text, int textViewId, int rowViewId) {
if (TextUtils.isEmpty(text)) {
root.findViewById(rowViewId).setVisibility(View.GONE);
return;
}
((TextView) root.findViewById(textViewId)).setText(text);
}
}

View File

@ -32,7 +32,7 @@ import java.util.regex.Pattern;
/**
* This class represent email address.
*
*
* RFC822 email address may have following format.
* "name" <address> (comment)
* "name" <address>
@ -85,7 +85,7 @@ public class Address {
/**
* Get name part as UTF-16 string. No surrounding double quote, and no MIME/base64 encoding.
*
*
* @return Name part of email address. Returns null if it is omitted.
*/
public String getPersonal() {
@ -95,7 +95,7 @@ public class Address {
/**
* Set name part from UTF-16 string. Optional surrounding double quote will be removed.
* It will be also unquoted and MIME/base64 decoded.
*
*
* @param Personal name part of email address as UTF-16 string. Null is acceptable.
*/
public void setPersonal(String personal) {
@ -133,7 +133,7 @@ public class Address {
/**
* Parse a comma-delimited list of addresses in RFC822 format and return an
* array of Address objects.
*
*
* @param addressList Address list in comma-delimited string.
* @return An array of 0 or more Addresses.
*/
@ -158,8 +158,8 @@ public class Address {
}
return addresses.toArray(new Address[] {});
}
/**
/**
* Checks whether a string email address is valid.
* E.g. name@domain.com is valid.
*/
@ -195,12 +195,12 @@ public class Address {
/**
* Get human readable address string.
* Do not use this for email header.
*
*
* @return Human readable address string. Not quoted and not encoded.
*/
@Override
public String toString() {
if (mPersonal != null) {
if (mPersonal != null && !mPersonal.equals(mAddress)) {
if (mPersonal.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) {
return Utility.quoteString(mPersonal) + " <" + mAddress + ">";
} else {
@ -213,7 +213,7 @@ public class Address {
/**
* Get human readable comma-delimited address string.
*
*
* @param addresses Address array
* @return Human readable comma-delimited address string.
*/
@ -231,10 +231,10 @@ public class Address {
}
return sb.toString();
}
/**
* Get RFC822/MIME compatible address string.
*
*
* @return RFC822/MIME compatible address string.
* It may be surrounded by double quote or quoted and MIME/base64 encoded if necessary.
*/
@ -248,7 +248,7 @@ public class Address {
/**
* Get RFC822/MIME compatible comma-delimited address string.
*
*
* @param addresses Address array
* @return RFC822/MIME compatible comma-delimited address string.
* it may be surrounded by double quoted or quoted and MIME/base64 encoded if necessary.
@ -268,11 +268,11 @@ public class Address {
}
return sb.toString();
}
/**
* Get Human friendly address string.
*
* @return the personal part of this Address, or the address part if the
*
* @return the personal part of this Address, or the address part if the
* personal part is not available
*/
public String toFriendly() {
@ -282,11 +282,11 @@ public class Address {
return mAddress;
}
}
/**
* Creates a comma-delimited list of addresses in the "friendly" format (see toFriendly() for
* Creates a comma-delimited list of addresses in the "friendly" format (see toFriendly() for
* details on the per-address conversion).
*
*
* @param addresses Array of Address[] values
* @return A comma-delimited string listing all of the addresses supplied. Null if source
* was null or empty.
@ -466,7 +466,7 @@ public class Address {
else {
address =
Utility.fastUrlDecode(addressList.substring(pairStartIndex, addressEndIndex));
personal =
personal =
Utility.fastUrlDecode(addressList.substring(addressEndIndex + 1, pairEndIndex));
}
addresses.add(new Address(address, personal));

View File

@ -27,6 +27,7 @@ import android.content.Context;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.MatrixCursor;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@ -37,6 +38,7 @@ import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.Log;
import android.widget.ListView;
@ -479,6 +481,16 @@ public class UtilityUnitTests extends AndroidTestCase {
Utility.CloseTraceCursorWrapper.log(null);
}
public void testAppendBold() {
SpannableStringBuilder ssb = new SpannableStringBuilder();
ssb.append("no");
assertEquals(ssb, Utility.appendBold(ssb, "BO"));
assertEquals("noBO", ssb.toString());
// TODO check style -- but how?
}
/**
* A {@link ListView} used by {@link #testListStateSaver}.
*/

View File

@ -29,8 +29,8 @@ import java.io.UnsupportedEncodingException;
*/
@SmallTest
public class AddressUnitTests extends AndroidTestCase {
private static final String MULTI_ADDRESSES_LIST =
private static final String MULTI_ADDRESSES_LIST =
"noname1@dom1.com, "
+ "<noname2@dom2.com>, "
+ "simple name <address3@dom3.org>, "
@ -39,19 +39,21 @@ public class AddressUnitTests extends AndroidTestCase {
+ "\u65E5\u672C\u8A9E <address6@co.jp>,"
+ "\"\u65E5\u672C\u8A9E\" <address7@co.jp>,"
+ "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>,"
+ "\"\uD834\uDF01\uD834\uDF46\" <address9@ne.jp>";
private static final int MULTI_ADDRESSES_COUNT = 9;
+ "\"\uD834\uDF01\uD834\uDF46\" <address9@ne.jp>,"
+ "noname@dom.com <noname@dom.com>" // personal == address
;
private static final int MULTI_ADDRESSES_COUNT = 10;
private static final Address PACK_ADDR_1 = new Address("john@gmail.com", "John Doe");
private static final Address PACK_ADDR_2 = new Address("foo@bar.com", null);
private static final Address PACK_ADDR_3 = new Address("mar.y+test@gmail.com", "Mar-y, B; B*arr");
private static final Address[][] PACK_CASES = {
{PACK_ADDR_2}, {PACK_ADDR_1},
{PACK_ADDR_1, PACK_ADDR_2}, {PACK_ADDR_2, PACK_ADDR_1},
{PACK_ADDR_1, PACK_ADDR_3}, {PACK_ADDR_2, PACK_ADDR_2},
{PACK_ADDR_2}, {PACK_ADDR_1},
{PACK_ADDR_1, PACK_ADDR_2}, {PACK_ADDR_2, PACK_ADDR_1},
{PACK_ADDR_1, PACK_ADDR_3}, {PACK_ADDR_2, PACK_ADDR_2},
{PACK_ADDR_1, PACK_ADDR_2, PACK_ADDR_3}, {PACK_ADDR_3, PACK_ADDR_1, PACK_ADDR_2}
};
Address mAddress1;
Address mAddress2;
Address mAddress3;
@ -95,61 +97,61 @@ public class AddressUnitTests extends AndroidTestCase {
public void testSetAddress() {
String bareAddress = "user1@dom1.com";
String bracketAddress = "<user2@dom2.com>";
Address address = new Address(bareAddress);
assertEquals("bare address", "user1@dom1.com", address.getAddress());
address.setAddress(bracketAddress);
assertEquals("bracket address", "user2@dom2.com", address.getAddress());
}
/**
* Test for empty setPersonal().
*/
public void testNullPersonal() {
Address address = new Address("user1@dom1.org");
assertNull("no name", address.getPersonal());
address.setPersonal(null);
assertNull("null name", address.getPersonal());
address.setPersonal("");
assertNull("empty name", address.getPersonal());
address.setPersonal("\"\"");
assertNull("quoted empty address", address.getPersonal());
}
/**
* Test for setPersonal().
*/
public void testSetPersonal() {
Address address = new Address("user1@dom1.net", "simple name");
assertEquals("simple name", "simple name", address.getPersonal());
address.setPersonal("big \\\"G\\\"");
assertEquals("quoted name", "big \"G\"", address.getPersonal());
address.setPersonal("=?UTF-8?Q?big \"G\"?=");
assertEquals("quoted printable name", "big \"G\"", address.getPersonal());
address.setPersonal("=?UTF-8?B?YmlnICJHIg==?=");
assertEquals("base64 encoded name", "big \"G\"", address.getPersonal());
}
/**
* Test for setPersonal() with utf-16 and utf-32.
*/
public void testSetPersonalMultipleEncodings() {
Address address = new Address("user1@dom1.co.jp", "=?UTF-8?B?5bK45pys?=");
assertEquals("base64 utf-16 name", "\u5CB8\u672C", address.getPersonal());
address.setPersonal("\"=?UTF-8?Q?=E5=B2=B8=E6=9C=AC?=\"");
assertEquals("quoted printable utf-16 name", "\u5CB8\u672C", address.getPersonal());
address.setPersonal("=?ISO-2022-JP?B?GyRCNF9LXBsoQg==?=");
assertEquals("base64 jis encoded name", "\u5CB8\u672C", address.getPersonal());
address.setPersonal("\"=?UTF-8?B?8J2MgfCdjYY=?=\"");
assertEquals("base64 utf-32 name", "\uD834\uDF01\uD834\uDF46", address.getPersonal());
@ -157,28 +159,28 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("quoted printable utf-32 name",
"\uD834\uDF01\uD834\uDF46", address.getPersonal());
}
/**
* TODO: more in-depth tests for parse()
*/
/**
* Simple quick checks of empty-input edge conditions for parse()
*
*
* NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
* behavior while I am changing some of the code in the function under test.
*/
public void testEmptyParse() {
Address[] result;
// null input => empty array
result = Address.parse(null);
assertTrue("parsing null address", result != null && result.length == 0);
// empty string input => empty array
result = Address.parse("");
assertTrue("parsing zero-length", result != null && result.length == 0);
// spaces
result = Address.parse(" ");
assertTrue("parsing spaces", result != null && result.length == 0);
@ -187,7 +189,7 @@ public class AddressUnitTests extends AndroidTestCase {
result = Address.parse(" , ");
assertTrue("parsing spaces with comma", result != null && result.length == 0);
}
/**
* Test parsing for single address.
*/
@ -212,7 +214,7 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("address with quoted name", "address4@dom4.org", address4[0].getAddress());
assertEquals("name of address with quoted name", "first,last", address4[0].getPersonal());
}
/**
* Test parsing for illegal address.
*/
@ -222,7 +224,7 @@ public class AddressUnitTests extends AndroidTestCase {
Address[] address2 = Address.parse("address2@");
assertEquals("no domain", 0, address2.length);
Address[] address3 = Address.parse("@dom3.com");
assertEquals("no local part", 0, address3.length);
@ -238,7 +240,7 @@ public class AddressUnitTests extends AndroidTestCase {
Address[] address7 = Address.parse("address7@.dom7.org");
assertEquals("domain starts with dot", 0, address7.length);
}
/**
* Test parsing for address part.
*/
@ -252,7 +254,7 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("bracket address", "address2@dom2.com", addresses[1].getAddress());
assertNull("bracket address name", addresses[1].getPersonal());
}
/**
* Test parsing for simple name part.
*/
@ -261,14 +263,14 @@ public class AddressUnitTests extends AndroidTestCase {
"name 1 <address1@dom1.net>, " +
"\"name,2\" <address2@dom2.org>");
assertEquals("address count", 2, addresses.length);
assertEquals("bare name address", "address1@dom1.net", addresses[0].getAddress());
assertEquals("bare name", "name 1", addresses[0].getPersonal());
assertEquals("double quoted name address", "address2@dom2.org", addresses[1].getAddress());
assertEquals("double quoted name", "name,2", addresses[1].getPersonal());
}
/**
* Test parsing for utf-16 name part.
*/
@ -277,7 +279,7 @@ public class AddressUnitTests extends AndroidTestCase {
"\u3042\u3044\u3046 \u3048\u304A <address1@dom1.jp>, " +
"\"\u3042\u3044\u3046,\u3048\u304A\" <address2@dom2.jp>");
assertEquals("address count", 2, addresses.length);
assertEquals("bare utf-16 name address", "address1@dom1.jp", addresses[0].getAddress());
assertEquals("bare utf-16 name",
"\u3042\u3044\u3046 \u3048\u304A", addresses[0].getPersonal());
@ -287,7 +289,7 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("double quoted utf-16 name",
"\u3042\u3044\u3046,\u3048\u304A", addresses[1].getPersonal());
}
/**
* Test parsing for utf-32 name part.
*/
@ -296,7 +298,7 @@ public class AddressUnitTests extends AndroidTestCase {
"\uD834\uDF01\uD834\uDF46 \uD834\uDF22 <address1@dom1.net>, " +
"\"\uD834\uDF01\uD834\uDF46,\uD834\uDF22\" <address2@dom2.com>");
assertEquals("address count", 2, addresses.length);
assertEquals("bare utf-32 name address", "address1@dom1.net", addresses[0].getAddress());
assertEquals("bare utf-32 name",
"\uD834\uDF01\uD834\uDF46 \uD834\uDF22", addresses[0].getPersonal());
@ -306,15 +308,15 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("double quoted utf-32 name",
"\uD834\uDF01\uD834\uDF46,\uD834\uDF22", addresses[1].getPersonal());
}
/**
* Test parsing for multi addresses.
*/
public void testParseMulti() {
Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
assertEquals("no name 1 address", "noname1@dom1.com", addresses[0].getAddress());
assertNull("no name 1 name", addresses[0].getPersonal());
assertEquals("no name 2 address", "noname2@dom2.com", addresses[1].getAddress());
@ -325,24 +327,24 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("double quoted name name", "name,4", addresses[3].getPersonal());
assertEquals("quoted name address", "bigG@dom5.net", addresses[4].getAddress());
assertEquals("quoted name name", "big \"G\"", addresses[4].getPersonal());
assertEquals("utf-16 name address", "address6@co.jp", addresses[5].getAddress());
assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", addresses[5].getPersonal());
assertEquals("utf-16 quoted name address", "address7@co.jp", addresses[6].getAddress());
assertEquals("utf-16 name address", "address6@co.jp", addresses[5].getAddress());
assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", addresses[5].getPersonal());
assertEquals("utf-16 quoted name address", "address7@co.jp", addresses[6].getAddress());
assertEquals("utf-16 quoted name name", "\u65E5\u672C\u8A9E",
addresses[6].getPersonal());
assertEquals("utf-32 name address", "address8@ne.jp", addresses[7].getAddress());
assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", addresses[7].getPersonal());
assertEquals("utf-32 quoted name address", "address9@ne.jp", addresses[8].getAddress());
addresses[6].getPersonal());
assertEquals("utf-32 name address", "address8@ne.jp", addresses[7].getAddress());
assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", addresses[7].getPersonal());
assertEquals("utf-32 quoted name address", "address9@ne.jp", addresses[8].getAddress());
assertEquals("utf-32 quoted name name", "\uD834\uDF01\uD834\uDF46",
addresses[8].getPersonal());
addresses[8].getPersonal());
}
/**
* Test various combinations of the toString (single) method
*/
public void testToStringSingle() {
Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
// test for toString() results.
@ -352,25 +354,26 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("double quoted name", "\"name,4\" <address4@dom4.org>", addresses[3].toString());
assertEquals("quoted name", "\"big \"G\"\" <bigG@dom5.net>", addresses[4].toString());
assertEquals("utf-16 name", "\u65E5\u672C\u8A9E <address6@co.jp>",
addresses[5].toString());
addresses[5].toString());
assertEquals("utf-16 quoted name", "\u65E5\u672C\u8A9E <address7@co.jp>",
addresses[6].toString());
addresses[6].toString());
assertEquals("utf-32 name", "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>",
addresses[7].toString());
addresses[7].toString());
assertEquals("utf-32 quoted name", "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>",
addresses[8].toString());
addresses[8].toString());
assertEquals("name==address", "noname@dom.com", addresses[9].toString());
}
/**
* Test various combinations of the toString (multi) method
*/
public void testToStringMulti() {
Address[] addresses = Address.parse(MULTI_ADDRESSES_LIST);
assertEquals("multi addrsses count", MULTI_ADDRESSES_COUNT, addresses.length);
String line = Address.toString(addresses);
assertEquals("toString multi",
assertEquals("toString multi",
"noname1@dom1.com,"
+ "noname2@dom2.com,"
+ "simple name <address3@dom3.org>,"
@ -379,7 +382,8 @@ public class AddressUnitTests extends AndroidTestCase {
+ "\u65E5\u672C\u8A9E <address6@co.jp>,"
+ "\u65E5\u672C\u8A9E <address7@co.jp>,"
+ "\uD834\uDF01\uD834\uDF46 <address8@ne.jp>,"
+ "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>",
+ "\uD834\uDF01\uD834\uDF46 <address9@ne.jp>,"
+ "noname@dom.com",
line);
}
@ -410,7 +414,8 @@ public class AddressUnitTests extends AndroidTestCase {
Address quotedName = new Address("bigG@dom5.net", "big \"G\"");
Address utf16Name = new Address("<address6@co.jp>", "\"\u65E5\u672C\u8A9E\"");
Address utf32Name = new Address("<address8@ne.jp>", "\uD834\uDF01\uD834\uDF46");
Address sameName = new Address("address@dom.org", "address@dom.org");
// test for internal states.
assertEquals("no name 1 address", "noname1@dom1.com", noName1.getAddress());
assertNull("no name 1 name", noName1.getPersonal());
@ -422,10 +427,12 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
assertEquals("quoted name address", "bigG@dom5.net", quotedName.getAddress());
assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
assertEquals("utf-16 name address", "address6@co.jp", utf16Name.getAddress());
assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
assertEquals("utf-32 name address", "address8@ne.jp", utf32Name.getAddress());
assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
assertEquals("utf-16 name address", "address6@co.jp", utf16Name.getAddress());
assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
assertEquals("utf-32 name address", "address8@ne.jp", utf32Name.getAddress());
assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
assertEquals("name == address address", "address@dom.org", sameName.getAddress());
assertEquals("name == address name", "address@dom.org", sameName.getPersonal());
// Test for toHeader() results.
assertEquals("no name 1", "noname1@dom1.com", noName1.toHeader());
@ -434,11 +441,13 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("double quoted name", "\"name,4\" <address4@dom4.org>", dquoteName.toHeader());
assertEquals("quoted name", "\"big \\\"G\\\"\" <bigG@dom5.net>", quotedName.toHeader());
assertEquals("utf-16 name", "=?UTF-8?B?5pel5pys6Kqe?= <address6@co.jp>",
utf16Name.toHeader());
utf16Name.toHeader());
assertEquals("utf-32 name", "=?UTF-8?B?8J2MgfCdjYY=?= <address8@ne.jp>",
utf32Name.toHeader());
utf32Name.toHeader());
assertEquals("name == address", "\"address@dom.org\" <address@dom.org>",
sameName.toHeader());
}
/**
* Test various combinations of the toHeader (multi) method
*/
@ -450,7 +459,7 @@ public class AddressUnitTests extends AndroidTestCase {
Address quotedName = new Address("bigG@dom5.net", "big \"G\"");
Address utf16Name = new Address("<address6@co.jp>", "\"\u65E5\u672C\u8A9E\"");
Address utf32Name = new Address("<address8@ne.jp>", "\uD834\uDF01\uD834\uDF46");
// test for internal states.
assertEquals("no name 1 address", "noname1@dom1.com", noName1.getAddress());
assertNull("no name 1 name", noName1.getPersonal());
@ -462,10 +471,10 @@ public class AddressUnitTests extends AndroidTestCase {
assertEquals("double quoted name name", "name,4", dquoteName.getPersonal());
assertEquals("quoted name address", "bigG@dom5.net", quotedName.getAddress());
assertEquals("quoted name name", "big \"G\"", quotedName.getPersonal());
assertEquals("utf-16 name address", "address6@co.jp", utf16Name.getAddress());
assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
assertEquals("utf-32 name address", "address8@ne.jp", utf32Name.getAddress());
assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
assertEquals("utf-16 name address", "address6@co.jp", utf16Name.getAddress());
assertEquals("utf-16 name name", "\u65E5\u672C\u8A9E", utf16Name.getPersonal());
assertEquals("utf-32 name address", "address8@ne.jp", utf32Name.getAddress());
assertEquals("utf-32 name name", "\uD834\uDF01\uD834\uDF46", utf32Name.getPersonal());
Address[] addresses = new Address[] {
noName1, noName2, simpleName, dquoteName, quotedName, utf16Name, utf32Name,
@ -482,62 +491,62 @@ public class AddressUnitTests extends AndroidTestCase {
+ "=?UTF-8?B?8J2MgfCdjYY=?= <address8@ne.jp>",
line);
}
/**
* Test various combinations of the toFriendly (single) method
*/
public void testToFriendlySingle() {
public void testToFriendlySingle() {
assertEquals("personal1", mAddress1.toFriendly());
assertEquals("address2", mAddress2.toFriendly());
assertEquals("address3", mAddress3.toFriendly());
}
/**
* Test various combinations of the toFriendly (array) method
*/
public void testToFriendlyArray() {
public void testToFriendlyArray() {
Address[] list1 = null;
Address[] list2 = new Address[0];
Address[] list3 = new Address[] { mAddress1 };
Address[] list4 = new Address[] { mAddress1, mAddress2, mAddress3 };
assertEquals(null, Address.toFriendly(list1));
assertEquals(null, Address.toFriendly(list2));
assertEquals("personal1", Address.toFriendly(list3));
assertEquals("personal1,address2,address3", Address.toFriendly(list4));
}
/**
* Simple quick checks of empty-input edge conditions for pack()
*
*
* NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
* behavior while I am changing some of the code in the function under test.
*/
public void testEmptyPack() {
String result;
// null input => null string
result = Address.pack(null);
assertNull("packing null", result);
// zero-length input => empty string
result = Address.pack(new Address[] { });
assertEquals("packing empty array", "", result);
}
/**
* Simple quick checks of empty-input edge conditions for unpack()
*
*
* NOTE: This is not a claim that these edge cases are "correct", only to maintain consistent
* behavior while I am changing some of the code in the function under test.
*/
public void testEmptyUnpack() {
Address[] result;
// null input => empty array
result = Address.unpack(null);
assertTrue("unpacking null address", result != null && result.length == 0);
// empty string input => empty array
result = Address.unpack("");
assertTrue("unpacking zero-length", result != null && result.length == 0);
@ -642,7 +651,7 @@ public class AddressUnitTests extends AndroidTestCase {
for (String address : valid) {
assertTrue(address, Address.isValidAddress(address));
}
// isAllValid() must accept empty address list as valid
assertTrue("Empty address list is valid", Address.isAllValid(""));
}