Collapse extra raw contact editors
There are now two different header styles
1) When creating a new raw contact: if there are multiple
accounts to create the raw contact under, then show a
header with an acconut selector
2) Otherwise, show a visually distinct account header without
a drop down account selector. If there are multiple raw
contacts, this header also lets you collapse raw contacts
Typical users, who only have a single writeable account, will
never see header (1). This is fortunate, since switching between
the two header styles is a bit visually inconsistent.
Bug: 18004959
Change-Id: I133a7355a220f20b55657a6c1a51ff44665ea3b9
diff --git a/res/drawable-hdpi/ic_account_circle_black_24dp.png b/res/drawable-hdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..ba5a509
--- /dev/null
+++ b/res/drawable-hdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_account_circle_black_24dp.png b/res/drawable-mdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..0c1202d
--- /dev/null
+++ b/res/drawable-mdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_account_circle_black_24dp.png b/res/drawable-xhdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..f26b201
--- /dev/null
+++ b/res/drawable-xhdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_account_circle_black_24dp.png b/res/drawable-xxhdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..3cc0a63
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_account_circle_black_24dp.png b/res/drawable-xxxhdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..c6b56c3
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/layout/editor_account_header_expandable.xml b/res/layout/editor_account_header_expandable.xml
new file mode 100644
index 0000000..5d347f0
--- /dev/null
+++ b/res/layout/editor_account_header_expandable.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- Header at the top of a raw contact editor. This is clickable to expand/collapse the editor. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/account_header_container"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="16dp"
+ android:focusable="true"
+ >
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:paddingBottom="24dp"
+ android:paddingTop="24dp"
+ android:orientation="vertical"
+ >
+
+ <TextView
+ android:id="@+id/account_type"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:singleLine="true"
+ android:textColor="@color/primary_text_color"
+ android:ellipsize="end"
+ />
+
+ <TextView
+ android:id="@+id/account_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:singleLine="true"
+ android:textColor="@color/secondary_text_color"
+ android:ellipsize="end"
+ />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/expand_account_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:layout_gravity="center_vertical|end"
+ android:clickable="false"
+ android:paddingLeft="@dimen/editor_round_button_padding_left"
+ android:paddingRight="@dimen/editor_round_button_padding_right"
+ android:paddingStart="@dimen/editor_round_button_padding_left"
+ android:paddingEnd="@dimen/editor_round_button_padding_right"
+ android:paddingTop="@dimen/editor_round_button_padding_top"
+ android:paddingBottom="@dimen/editor_round_button_padding_bottom"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/editor_account_header_with_dropdown.xml b/res/layout/editor_account_selector.xml
similarity index 61%
rename from res/layout/editor_account_header_with_dropdown.xml
rename to res/layout/editor_account_selector.xml
index 015358e..2f883f2 100644
--- a/res/layout/editor_account_header_with_dropdown.xml
+++ b/res/layout/editor_account_selector.xml
@@ -14,19 +14,24 @@
limitations under the License.
-->
+<!-- Header at the top of a raw contact editor. This allows users to change the account that
+ the raw contact is saved in. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/account_container"
+ android:id="@+id/account_selector_container"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="48dip"
- android:background="#EEEEEE"
+ android:layout_marginBottom="@dimen/editor_padding_between_editor_views"
android:orientation="horizontal"
- android:gravity="center_vertical"
- android:paddingLeft="@dimen/account_container_left_padding"
- android:paddingRight="28dip"
- android:paddingStart="@dimen/account_container_left_padding"
- android:paddingEnd="28dip">
+ android:paddingTop="16dp"
+ android:visibility="gone" >
+
+ <ImageView
+ android:id="@+id/kind_icon"
+ android:src="@drawable/ic_account_circle_black_24dp"
+ android:contentDescription="@string/header_account_entry"
+ style="@style/EditKindIconStyle" />
<LinearLayout
android:id="@+id/account"
@@ -34,42 +39,29 @@
android:layout_width="0dip"
android:layout_weight="1"
android:orientation="vertical"
- style="?android:attr/spinnerStyle">
+ android:layout_marginEnd="48dp"
+ style="@android:style/Widget.Material.Spinner.Underlined">
<TextView
- android:id="@+id/account_type"
+ android:id="@+id/account_type_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="16sp"
android:singleLine="true"
android:textColor="@color/primary_text_color"
android:ellipsize="end" />
<TextView
- android:id="@+id/account_name"
+ android:id="@+id/account_name_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="8dip"
android:paddingEnd="8dip"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textSize="14sp"
android:singleLine="true"
- android:textColor="@color/primary_text_color"
+ android:textColor="@color/secondary_text_color"
android:ellipsize="end" />
</LinearLayout>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginLeft="10dip"
- android:layout_marginStart="10dip">
-
- <ImageView
- android:id="@+id/account_icon"
- android:layout_width="32dip"
- android:layout_height="32dip"
- android:layout_gravity="center_vertical" />
-
- </FrameLayout>
-
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/expanding_entry_card_view.xml b/res/layout/expanding_entry_card_view.xml
index 6b89759..7eacd1f 100644
--- a/res/layout/expanding_entry_card_view.xml
+++ b/res/layout/expanding_entry_card_view.xml
@@ -33,8 +33,8 @@
<View
android:id="@+id/title_separator"
android:layout_width="match_parent"
- android:layout_height="@dimen/expanding_entry_card_item_separator_height"
- android:background="@color/expanding_entry_card_item_separator_color"
+ android:layout_height="@dimen/divider_line_height"
+ android:background="@color/divider_line_color_light"
android:visibility="gone" />
<LinearLayout
diff --git a/res/layout/quickcontact_expanding_entry_card_button.xml b/res/layout/quickcontact_expanding_entry_card_button.xml
index 1f0c724..b198b06 100644
--- a/res/layout/quickcontact_expanding_entry_card_button.xml
+++ b/res/layout/quickcontact_expanding_entry_card_button.xml
@@ -22,8 +22,8 @@
<View
android:layout_width="match_parent"
- android:layout_height="@dimen/expanding_entry_card_item_separator_height"
- android:background="@color/expanding_entry_card_item_separator_color" />
+ android:layout_height="@dimen/divider_line_height"
+ android:background="@color/divider_line_color_light" />
<LinearLayout
android:layout_width="match_parent"
diff --git a/res/layout/raw_contact_editor_view.xml b/res/layout/raw_contact_editor_view.xml
index ace6c8f..54aef1c 100644
--- a/res/layout/raw_contact_editor_view.xml
+++ b/res/layout/raw_contact_editor_view.xml
@@ -19,39 +19,51 @@
android:id="@+id/body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingTop="@dimen/editor_padding_top" >
+ android:orientation="vertical" >
+ <!-- There are two mutually exclusive account headers that look significantly different.
+ The editor_account_selector is used when an account needs to be chosen. -->
<include
- layout="@layout/editor_account_header_with_dropdown" />
-
- <Space
- android:layout_height="8dip"
- android:layout_width="match_parent" />
-
+ layout="@layout/editor_account_header_expandable" />
<include
- android:id="@+id/edit_name"
- layout="@layout/structured_name_editor_view" />
-
- <include
- android:id="@+id/edit_phonetic_name"
- layout="@layout/phonetic_name_editor_view" />
-
- <include
- android:id="@+id/edit_nick_name"
- layout="@layout/item_kind_section" />
-
- <include
- android:id="@+id/edit_photo"
- android:layout_marginRight="8dip"
- android:layout_marginEnd="8dip"
- layout="@layout/item_photo_editor" />
+ layout="@layout/editor_account_selector" />
<LinearLayout
- android:id="@+id/sect_fields"
+ android:id="@+id/collapsable_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_marginBottom="16dip"/>
+ android:orientation="vertical" >
+
+ <include
+ android:id="@+id/edit_name"
+ layout="@layout/structured_name_editor_view" />
+
+ <include
+ android:id="@+id/edit_phonetic_name"
+ layout="@layout/phonetic_name_editor_view" />
+
+ <include
+ android:id="@+id/edit_nick_name"
+ layout="@layout/item_kind_section" />
+
+ <include
+ android:id="@+id/edit_photo"
+ android:layout_marginRight="8dip"
+ android:layout_marginEnd="8dip"
+ layout="@layout/item_photo_editor" />
+
+ <LinearLayout
+ android:id="@+id/sect_fields"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginBottom="16dip"/>
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/divider_line_height"
+ android:background="@color/divider_line_color_light" />
</com.android.contacts.editor.RawContactEditorView>
diff --git a/res/layout/raw_contact_readonly_editor_view.xml b/res/layout/raw_contact_readonly_editor_view.xml
index a1eab90..f85971f 100644
--- a/res/layout/raw_contact_readonly_editor_view.xml
+++ b/res/layout/raw_contact_readonly_editor_view.xml
@@ -18,57 +18,69 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingTop="@dimen/editor_padding_top">
+ android:orientation="vertical" >
<include
- layout="@layout/editor_account_header" />
+ layout="@layout/editor_account_header_expandable" />
- <!-- Want 16dp of apparent top padding. Since EditText has 10dp of inset/padding built in,
- only set marginTop=6dp. -->
<LinearLayout
+ android:id="@+id/collapsable_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/editor_min_line_item_height"
- android:layout_marginBottom="@dimen/editor_padding_between_editor_views"
- android:layout_marginTop="6dp"
- android:orientation="horizontal">
+ android:orientation="vertical" >
- <ImageView
- android:id="@+id/kind_icon"
- android:src="@drawable/ic_person_black_24dp"
- android:contentDescription="@string/header_name_entry"
- style="@style/EditKindIconStyle" />
-
- <EditText
- android:id="@+id/read_only_name"
+ <!-- Want 16dp of apparent top padding. Since EditText has 10dp of inset/padding built in,
+ only set marginTop=6dp. -->
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginRight="@dimen/editor_delete_button_width"
- android:singleLine="true"
- android:textSize="@dimen/editor_form_text_size"
- android:textColor="?android:attr/textColorSecondary"
- android:enabled="false"/>
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/editor_min_line_item_height"
+ android:layout_marginBottom="@dimen/editor_padding_between_editor_views"
+ android:layout_marginTop="6dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/kind_icon"
+ android:src="@drawable/ic_person_black_24dp"
+ android:contentDescription="@string/header_name_entry"
+ style="@style/EditKindIconStyle" />
+
+ <EditText
+ android:id="@+id/read_only_name"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginRight="@dimen/editor_delete_button_width"
+ android:singleLine="true"
+ android:textSize="@dimen/editor_form_text_size"
+ android:textColor="?android:attr/textColorSecondary"
+ android:enabled="false"/>
+
+ </LinearLayout>
+
+ <include
+ android:id="@+id/edit_photo"
+ layout="@layout/item_photo_editor_readonly" />
+
+ <Button
+ android:id="@+id/button_edit_externally"
+ android:text="@string/edit_contact"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="@dimen/editor_padding_below_photo"
+ android:layout_marginEnd="13dip"
+ android:layout_marginStart="13dip"/>
+
+ <LinearLayout android:id="@+id/sect_general"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"/>
</LinearLayout>
- <include
- android:id="@+id/edit_photo"
- layout="@layout/item_photo_editor_readonly" />
-
- <Button
- android:id="@+id/button_edit_externally"
- android:text="@string/edit_contact"
+ <View
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="@dimen/editor_padding_below_photo"
- android:layout_marginEnd="13dip"
- android:layout_marginStart="13dip"/>
-
- <LinearLayout android:id="@+id/sect_general"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"/>
+ android:layout_height="@dimen/divider_line_height"
+ android:background="@color/divider_line_color_light" />
</com.android.contacts.editor.RawContactReadOnlyEditorView>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index daea14b..d9ac84a 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -37,8 +37,8 @@
<color name="floating_action_button_icon_color">@color/contacts_accent_color</color>
- <!-- Color of the separator between entries in an ExpandingEntryCardView -->
- <color name="expanding_entry_card_item_separator_color">#e4e4e4</color>
+ <!-- Horizontal separator line should be 12% dark in the light theme. -->
+ <color name="divider_line_color_light">#e0e0e0</color>
<!-- Color of the text on an ExpandingEntryCard button -->
<color name="expanding_entry_card_button_text_color">@android:color/black</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1a77057..b31b5b9 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -25,9 +25,6 @@
<!-- The ratio of width:height for the contact's photo in landscape -->
<item name="quickcontact_landscape_photo_ratio" type="dimen" format="float">0.7</item>
- <!-- Top padding of the entire contact editor -->
- <dimen name="editor_padding_top">0dip</dimen>
-
<!-- Padding of the rounded plus/minus/expand/collapse buttons in the editor -->
<dimen name="editor_round_button_padding_left">16dip</dimen>
<dimen name="editor_round_button_padding_right">16dip</dimen>
@@ -174,8 +171,9 @@
<!-- Extra top padding if the title is set to null -->
<dimen name="expanding_entry_card_null_title_top_extra_padding">2dp</dimen>
- <!-- Height of the separator between entries in an ExpandingEntryCardView -->
- <dimen name="expanding_entry_card_item_separator_height">1dp</dimen>
+ <!-- Height of the separator between entries in an ExpandingEntryCardView and contact editor. -->
+ <dimen name="divider_line_height">1dp</dimen>
+
<!-- Dimensions for an entry in ExpandingEntryCardView -->
<dimen name="expanding_entry_card_item_padding_start">20dp</dimen>
<dimen name="expanding_entry_card_item_padding_end">20dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5d59f32..e11a519 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -705,6 +705,8 @@
<string name="header_event_entry">Event</string>
<!-- Header for the Relation entry [CHAR LIMIT=40] -->
<string name="header_relation_entry">Relation</string>
+ <!-- Content description for the account field header image. Example accounts listed in this field: Google, Hotmail and Exchange. [CHAR LIMIT=NONE] -->
+ <string name="header_account_entry">Account</string>
<!-- Content description for the name fields header entry [CHAR LIMIT=NONE] -->
<string name="header_name_entry">Name</string>
<!-- Content description for the email fields header entry [CHAR LIMIT=NONE] -->
@@ -714,6 +716,11 @@
<!-- Content description for the camera icon beside the photo section in the Raw Contact Editor [CHAR LIMIT=NONE] -->
<string name="header_photo_entry">Photo</string>
+ <!-- Content description for the expand button inside the raw contact editor's header. [CHAR LIMIT=NONE] -->
+ <string name="content_description_expand_editor">Click to expand contact editor.</string>
+ <!-- Content description for the collapse button inside the raw contact editor's header. [CHAR LIMIT=NONE] -->
+ <string name="content_description_collapse_editor">Click to collapse contact editor.</string>
+
<!-- Content description for directions secondary button [CHAR LIMIT=NONE] -->
<string name="content_description_directions">directions to location</string>
diff --git a/src/com/android/contacts/editor/BaseRawContactEditorView.java b/src/com/android/contacts/editor/BaseRawContactEditorView.java
index 28261cf..473f2ab 100644
--- a/src/com/android/contacts/editor/BaseRawContactEditorView.java
+++ b/src/com/android/contacts/editor/BaseRawContactEditorView.java
@@ -22,10 +22,13 @@
import android.net.Uri;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.Data;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.contacts.R;
import com.android.contacts.common.model.RawContactDelta;
@@ -33,6 +36,7 @@
import com.android.contacts.common.model.RawContactModifier;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountType.EditType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
/**
* Base view that provides common code for the editor interaction for a specific
@@ -47,10 +51,18 @@
private PhotoEditorView mPhoto;
- private View mBody;
- private View mDivider;
+ private View mAccountHeaderContainer;
+ private ImageView mExpandAccountButton;
+ private LinearLayout mCollapsibleSection;
+ private TextView mAccountName;
+ private TextView mAccountType;
- private boolean mExpanded = true;
+ protected Listener mListener;
+
+ public interface Listener {
+ void onExternalEditorRequest(AccountWithDataSet account, Uri uri);
+ void onEditorExpansionChanged();
+ }
public BaseRawContactEditorView(Context context) {
super(context);
@@ -64,16 +76,27 @@
protected void onFinishInflate() {
super.onFinishInflate();
- mBody = findViewById(R.id.body);
- mDivider = findViewById(R.id.divider);
-
mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
mPhoto.setEnabled(isEnabled());
+
+ mAccountHeaderContainer = findViewById(R.id.account_header_container);
+ mExpandAccountButton = (ImageView) findViewById(R.id.expand_account_button);
+ mCollapsibleSection = (LinearLayout) findViewById(R.id.collapsable_section);
+ mAccountName = (TextView) findViewById(R.id.account_name);
+ mAccountType = (TextView) findViewById(R.id.account_type);
+
+ setCollapsed(false);
+ setCollapsible(true);
}
public void setGroupMetaData(Cursor groupMetaData) {
}
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
/**
* Assign the given {@link Bitmap} to the internal {@link PhotoEditorView}
* in order to update the {@link RawContactDelta} currently being edited.
@@ -111,25 +134,80 @@
public abstract long getRawContactId();
/**
+ * If {@param isCollapsible} is TRUE, then this editor can be collapsed by clicking on its
+ * account header.
+ */
+ public void setCollapsible(boolean isCollapsible) {
+ if (isCollapsible) {
+ mAccountHeaderContainer.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final int startingHeight = mCollapsibleSection.getMeasuredHeight();
+ final boolean isCollapsed = isCollapsed();
+ setCollapsed(!isCollapsed);
+ // The slideAndFadeIn animation only looks good when collapsing. For expanding,
+ // it looks like the editor is loading sluggishly. I tried animating the
+ // clipping bounds instead of the alpha value. But because the editors are very
+ // tall, this animation looked very similar to doing no animation at all. It
+ // wasn't worth the significant additional complexity.
+ if (!isCollapsed) {
+ EditorAnimator.getInstance().slideAndFadeIn(mCollapsibleSection,
+ startingHeight);
+ }
+ if (mListener != null) {
+ mListener.onEditorExpansionChanged();
+ }
+ updateAccountHeaderContentDescription();
+ }
+ });
+ mExpandAccountButton.setVisibility(View.VISIBLE);
+ mAccountHeaderContainer.setClickable(true);
+ } else {
+ mAccountHeaderContainer.setOnClickListener(null);
+ mExpandAccountButton.setVisibility(View.GONE);
+ mAccountHeaderContainer.setClickable(false);
+ }
+ }
+
+ public boolean isCollapsed() {
+ return mCollapsibleSection.getLayoutParams().height == 0;
+ }
+
+ public void setCollapsed(boolean isCollapsed) {
+ final LinearLayout.LayoutParams params
+ = (LayoutParams) mCollapsibleSection.getLayoutParams();
+ if (isCollapsed) {
+ params.height = 0;
+ mCollapsibleSection.setLayoutParams(params);
+ mExpandAccountButton.setImageResource(R.drawable.ic_menu_expander_minimized_holo_light);
+ } else {
+ params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ mCollapsibleSection.setLayoutParams(params);
+ mExpandAccountButton.setImageResource(R.drawable.ic_menu_expander_maximized_holo_light);
+ }
+ }
+
+ protected void updateAccountHeaderContentDescription() {
+ final StringBuilder builder = new StringBuilder();
+ if (!TextUtils.isEmpty(mAccountType.getText())) {
+ builder.append(mAccountType.getText()).append('\n');
+ }
+ if (!TextUtils.isEmpty(mAccountName.getText())) {
+ builder.append(mAccountName.getText()).append('\n');
+ }
+ if (mExpandAccountButton.getVisibility() == View.VISIBLE) {
+ builder.append(getResources().getString(isCollapsed()
+ ? R.string.content_description_expand_editor
+ : R.string.content_description_collapse_editor));
+ }
+ mAccountHeaderContainer.setContentDescription(builder);
+ }
+
+ /**
* Set the internal state for this view, given a current
* {@link RawContactDelta} state and the {@link AccountType} that
* apply to that state.
*/
public abstract void setState(RawContactDelta state, AccountType source, ViewIdGenerator vig,
boolean isProfile);
-
- /* package */ void setExpanded(boolean value) {
- // only allow collapsing if we are one of several children
- final boolean newValue;
- if (getParent() instanceof ViewGroup && ((ViewGroup) getParent()).getChildCount() == 1) {
- newValue = true;
- } else {
- newValue = value;
- }
-
- if (newValue == mExpanded) return;
- mExpanded = newValue;
- mBody.setVisibility(newValue ? View.VISIBLE : View.GONE);
- mDivider.setVisibility(newValue ? View.GONE : View.VISIBLE);
- }
}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index ac8a641..55f2ad0 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -99,6 +99,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
public class ContactEditorFragment extends Fragment implements
@@ -134,6 +135,7 @@
private static final String KEY_SEND_TO_VOICE_MAIL_STATE = "sendToVoicemailState";
private static final String KEY_CUSTOM_RINGTONE = "customRingtone";
private static final String KEY_ARE_PHONE_OPTIONS_CHANGEABLE = "arePhoneOptionsChangable";
+ private static final String KEY_EXPANDED_EDITORS = "expandedEditors";
public static final String SAVE_MODE_EXTRA_KEY = "saveMode";
@@ -276,6 +278,9 @@
// Used to temporarily store existing contact data during a rebind call (i.e. account switch)
private ImmutableList<RawContact> mRawContacts;
+ // Used to store which raw contact editors have been expanded. Keyed on raw contact ids.
+ private HashMap<Long, Boolean> mExpandedEditors = new HashMap<Long, Boolean>();
+
private AggregationSuggestionEngine mAggregationSuggestionEngine;
private long mAggregationSuggestionsRawContactId;
private View mAggregationSuggestionView;
@@ -522,6 +527,8 @@
mSendToVoicemailState = savedState.getBoolean(KEY_SEND_TO_VOICE_MAIL_STATE);
mCustomRingtone = savedState.getString(KEY_CUSTOM_RINGTONE);
mArePhoneOptionsChangable = savedState.getBoolean(KEY_ARE_PHONE_OPTIONS_CHANGEABLE);
+ mExpandedEditors = (HashMap<Long, Boolean>)
+ savedState.getSerializable(KEY_EXPANDED_EDITORS);
}
// mState can still be null because it may not have have finished loading before
@@ -584,6 +591,11 @@
mListener.onCustomEditContactActivityRequested(account, uri, null, false);
}
+ @Override
+ public void onEditorExpansionChanged() {
+ updatedExpandedEditorsMap();
+ }
+
private void bindEditorsForExistingContact(String displayName, boolean isUserProfile,
ImmutableList<RawContact> rawContacts) {
setEnabled(true);
@@ -835,28 +847,30 @@
if (!type.areContactsWritable()) {
editor = (BaseRawContactEditorView) inflater.inflate(
R.layout.raw_contact_readonly_editor_view, mContent, false);
- ((RawContactReadOnlyEditorView) editor).setListener(this);
} else {
editor = (RawContactEditorView) inflater.inflate(R.layout.raw_contact_editor_view,
mContent, false);
}
- if (mHasNewContact && !mNewLocalProfile) {
- final List<AccountWithDataSet> accounts =
- AccountTypeManager.getInstance(mContext).getAccounts(true);
- if (accounts.size() > 1) {
- addAccountSwitcher(mState.get(0), editor);
- } else {
- disableAccountSwitcher(editor);
- }
- } else {
- disableAccountSwitcher(editor);
+ editor.setListener(this);
+ final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(mContext)
+ .getAccounts(true);
+ if (mHasNewContact && !mNewLocalProfile && accounts.size() > 1) {
+ addAccountSwitcher(mState.get(0), editor);
}
editor.setEnabled(mEnabled);
+ if (mExpandedEditors.containsKey(rawContactId)) {
+ editor.setCollapsed(mExpandedEditors.get(rawContactId));
+ } else {
+ // By default, only the first editor will be expanded.
+ editor.setCollapsed(i != 0);
+ }
+
mContent.addView(editor);
editor.setState(rawContactDelta, type, mViewIdGenerator, isEditingUserProfile());
+ editor.setCollapsible(numRawContacts > 1);
// Set up the photo handler.
bindPhotoHandler(editor, type, mState);
@@ -918,6 +932,21 @@
// Activity can be null if we have been detached from the Activity
final Activity activity = getActivity();
if (activity != null) activity.invalidateOptionsMenu();
+
+ updatedExpandedEditorsMap();
+ }
+
+ /**
+ * Update the values in {@link #mExpandedEditors}.
+ */
+ private void updatedExpandedEditorsMap() {
+ for (int i = 0; i < mContent.getChildCount(); i++) {
+ final View childView = mContent.getChildAt(i);
+ if (childView instanceof BaseRawContactEditorView) {
+ BaseRawContactEditorView childEditor = (BaseRawContactEditorView) childView;
+ mExpandedEditors.put(childEditor.getRawContactId(), childEditor.isCollapsed());
+ }
+ }
}
/**
@@ -1002,7 +1031,11 @@
currentState.getAccountType(),
currentState.getDataSet());
final View accountView = editor.findViewById(R.id.account);
- final View anchorView = editor.findViewById(R.id.account_container);
+ final View anchorView = editor.findViewById(R.id.account_selector_container);
+ if (accountView == null) {
+ return;
+ }
+ anchorView.setVisibility(View.VISIBLE);
accountView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -1031,14 +1064,6 @@
});
}
- private void disableAccountSwitcher(BaseRawContactEditorView editor) {
- // Remove the pressed state from the account header because the user cannot switch accounts
- // on an existing contact
- final View accountView = editor.findViewById(R.id.account);
- accountView.setBackground(null);
- accountView.setEnabled(false);
- }
-
@Override
public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.edit_contact, menu);
@@ -1789,6 +1814,7 @@
outState.putBoolean(KEY_SEND_TO_VOICE_MAIL_STATE, mSendToVoicemailState);
outState.putString(KEY_CUSTOM_RINGTONE, mCustomRingtone);
outState.putBoolean(KEY_ARE_PHONE_OPTIONS_CHANGEABLE, mArePhoneOptionsChangable);
+ outState.putSerializable(KEY_EXPANDED_EDITORS, mExpandedEditors);
super.onSaveInstanceState(outState);
}
diff --git a/src/com/android/contacts/editor/EditorAnimator.java b/src/com/android/contacts/editor/EditorAnimator.java
index 507e094..35ed1de 100644
--- a/src/com/android/contacts/editor/EditorAnimator.java
+++ b/src/com/android/contacts/editor/EditorAnimator.java
@@ -91,7 +91,6 @@
mRunner.endOldAnimation();
target.setVisibility(View.VISIBLE);
target.setAlpha(0.0f);
- target.requestFocus();
SchedulingUtils.doAfterLayout(target, new Runnable() {
@Override
public void run() {
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 4a05d7a..a3d2f09 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -31,7 +31,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageView;
import android.widget.TextView;
import com.android.contacts.GroupMetaDataLoader;
@@ -42,6 +41,7 @@
import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.ValuesDelta;
import com.android.contacts.common.model.RawContactModifier;
+
import com.google.common.base.Objects;
import java.util.ArrayList;
@@ -69,9 +69,13 @@
private ViewGroup mFields;
- private ImageView mAccountIcon;
- private TextView mAccountTypeTextView;
- private TextView mAccountNameTextView;
+ private View mAccountSelector;
+ private TextView mAccountSelectorTypeTextView;
+ private TextView mAccountSelectorNameTextView;
+
+ private View mAccountHeader;
+ private TextView mAccountHeaderTypeTextView;
+ private TextView mAccountHeaderNameTextView;
private long mRawContactId = -1;
private boolean mAutoAddToDefaultGroup = true;
@@ -132,9 +136,13 @@
mFields = (ViewGroup)findViewById(R.id.sect_fields);
- mAccountIcon = (ImageView) findViewById(R.id.account_icon);
- mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
- mAccountNameTextView = (TextView) findViewById(R.id.account_name);
+ mAccountHeader = findViewById(R.id.account_header_container);
+ mAccountHeaderTypeTextView = (TextView) findViewById(R.id.account_type);
+ mAccountHeaderNameTextView = (TextView) findViewById(R.id.account_name);
+
+ mAccountSelector = findViewById(R.id.account_selector_container);
+ mAccountSelectorTypeTextView = (TextView) findViewById(R.id.account_type_selector);
+ mAccountSelectorNameTextView = (TextView) findViewById(R.id.account_name_selector);
}
@Override
@@ -183,13 +191,13 @@
if (isProfile) {
String accountName = state.getAccountName();
if (TextUtils.isEmpty(accountName)) {
- mAccountNameTextView.setVisibility(View.GONE);
- mAccountTypeTextView.setText(R.string.local_profile_title);
+ mAccountHeaderNameTextView.setVisibility(View.GONE);
+ mAccountHeaderTypeTextView.setText(R.string.local_profile_title);
} else {
CharSequence accountType = type.getDisplayLabel(mContext);
- mAccountTypeTextView.setText(mContext.getString(R.string.external_profile_title,
+ mAccountHeaderTypeTextView.setText(mContext.getString(R.string.external_profile_title,
accountType));
- mAccountNameTextView.setText(accountName);
+ mAccountHeaderNameTextView.setText(accountName);
}
} else {
String accountName = state.getAccountName();
@@ -198,17 +206,27 @@
accountType = mContext.getString(R.string.account_phone);
}
if (!TextUtils.isEmpty(accountName)) {
- mAccountNameTextView.setVisibility(View.VISIBLE);
- mAccountNameTextView.setText(
+ mAccountHeaderNameTextView.setVisibility(View.VISIBLE);
+ mAccountHeaderNameTextView.setText(
mContext.getString(R.string.from_account_format, accountName));
} else {
// Hide this view so the other text view will be centered vertically
- mAccountNameTextView.setVisibility(View.GONE);
+ mAccountHeaderNameTextView.setVisibility(View.GONE);
}
- mAccountTypeTextView.setText(
+ mAccountHeaderTypeTextView.setText(
mContext.getString(R.string.account_type_format, accountType));
}
- mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
+ updateAccountHeaderContentDescription();
+
+ // The account selector and header are both used to display the same information.
+ mAccountSelectorTypeTextView.setText(mAccountHeaderTypeTextView.getText());
+ mAccountSelectorTypeTextView.setVisibility(mAccountHeaderTypeTextView.getVisibility());
+ mAccountSelectorNameTextView.setText(mAccountHeaderNameTextView.getText());
+ mAccountSelectorNameTextView.setVisibility(mAccountHeaderNameTextView.getVisibility());
+ // Showing the account header at the same time as the account selector drop down is
+ // confusing. They should be mutually exclusive.
+ mAccountHeader.setVisibility(mAccountSelector.getVisibility() == View.GONE
+ ? View.VISIBLE : View.GONE);
// Show photo editor when supported
RawContactModifier.ensureKindExists(state, type, Photo.CONTENT_ITEM_TYPE);
diff --git a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
index 7901093..477cbd3 100644
--- a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
+++ b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.net.Uri;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
@@ -36,7 +35,6 @@
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
-import android.widget.Toast;
import com.android.contacts.R;
import com.android.contacts.common.GeoUtil;
@@ -60,22 +58,14 @@
private Button mEditExternallyButton;
private ViewGroup mGeneral;
- private View mAccountContainer;
- private ImageView mAccountIcon;
- private TextView mAccountTypeTextView;
- private TextView mAccountNameTextView;
+ private TextView mAccountHeaderTypeTextView;
+ private TextView mAccountHeaderNameTextView;
private String mAccountName;
private String mAccountType;
private String mDataSet;
private long mRawContactId = -1;
- private Listener mListener;
-
- public interface Listener {
- void onExternalEditorRequest(AccountWithDataSet account, Uri uri);
- }
-
public RawContactReadOnlyEditorView(Context context) {
super(context);
}
@@ -84,9 +74,6 @@
super(context, attrs);
}
- public void setListener(Listener listener) {
- mListener = listener;
- }
/** {@inheritDoc} */
@Override
@@ -101,10 +88,8 @@
mEditExternallyButton.setOnClickListener(this);
mGeneral = (ViewGroup)findViewById(R.id.sect_general);
- mAccountContainer = findViewById(R.id.account_container);
- mAccountIcon = (ImageView) findViewById(R.id.account_icon);
- mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
- mAccountNameTextView = (TextView) findViewById(R.id.account_name);
+ mAccountHeaderTypeTextView = (TextView) findViewById(R.id.account_type);
+ mAccountHeaderNameTextView = (TextView) findViewById(R.id.account_name);
}
/**
@@ -131,13 +116,13 @@
if (isProfile) {
if (TextUtils.isEmpty(mAccountName)) {
- mAccountNameTextView.setVisibility(View.GONE);
- mAccountTypeTextView.setText(R.string.local_profile_title);
+ mAccountHeaderNameTextView.setVisibility(View.GONE);
+ mAccountHeaderTypeTextView.setText(R.string.local_profile_title);
} else {
CharSequence accountType = type.getDisplayLabel(mContext);
- mAccountTypeTextView.setText(mContext.getString(R.string.external_profile_title,
+ mAccountHeaderTypeTextView.setText(mContext.getString(R.string.external_profile_title,
accountType));
- mAccountNameTextView.setText(mAccountName);
+ mAccountHeaderNameTextView.setText(mAccountName);
}
} else {
CharSequence accountType = type.getDisplayLabel(mContext);
@@ -145,23 +130,20 @@
accountType = mContext.getString(R.string.account_phone);
}
if (!TextUtils.isEmpty(mAccountName)) {
- mAccountNameTextView.setVisibility(View.VISIBLE);
- mAccountNameTextView.setText(
+ mAccountHeaderNameTextView.setVisibility(View.VISIBLE);
+ mAccountHeaderNameTextView.setText(
mContext.getString(R.string.from_account_format, mAccountName));
} else {
// Hide this view so the other text view will be centered vertically
- mAccountNameTextView.setVisibility(View.GONE);
+ mAccountHeaderNameTextView.setVisibility(View.GONE);
}
- mAccountTypeTextView.setText(mContext.getString(R.string.account_type_format,
+ mAccountHeaderTypeTextView.setText(mContext.getString(R.string.account_type_format,
accountType));
}
- mAccountTypeTextView.setTextColor(mContext.getResources().getColor(
- R.color.secondary_text_color));
+ updateAccountHeaderContentDescription();
// TODO: Expose data set in the UI somehow?
- mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
-
mRawContactId = state.getRawContactId();
ValuesDelta primary;
@@ -182,16 +164,8 @@
mContext.getString(R.string.missing_name));
if (type.getEditContactActivityClassName() != null) {
- mAccountContainer.setEnabled(false);
mEditExternallyButton.setVisibility(View.VISIBLE);
} else {
- mAccountContainer.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Toast.makeText(mContext, mContext.getString(R.string.contact_read_only),
- Toast.LENGTH_SHORT).show();
- }
- });
mEditExternallyButton.setVisibility(View.GONE);
}
diff --git a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
index af50d6b..b912a3d 100644
--- a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
+++ b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
@@ -421,10 +421,10 @@
Resources res = getResources();
separator.setBackgroundColor(res.getColor(
- R.color.expanding_entry_card_item_separator_color));
+ R.color.divider_line_color_light));
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- res.getDimensionPixelSize(R.dimen.expanding_entry_card_item_separator_height));
+ res.getDimensionPixelSize(R.dimen.divider_line_height));
// The separator is aligned with the text in the entry. This is offset by a default
// margin. If there is an icon present, the icon's width and margin are added
int marginStart = res.getDimensionPixelSize(