Changing layout and behavior of aggregation suggestions.
Bug: 3098963
Change-Id: I50c8d56bf97b1af15200ce416afc6810899f16d7
diff --git a/res/layout-xlarge/aggregation_suggestions.xml b/res/layout-xlarge/aggregation_suggestions.xml
index 81b4edd..3a314b6 100644
--- a/res/layout-xlarge/aggregation_suggestions.xml
+++ b/res/layout-xlarge/aggregation_suggestions.xml
@@ -17,39 +17,16 @@
*/
-->
-<com.android.contacts.widget.InterpolatingLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="20dip">
-
+ android:orientation="vertical"
+ android:background="@drawable/aggregation_suggestions_bg_light_holo"
+ android:paddingBottom="10dip">
<LinearLayout
- android:layout_width="wrap_content"
+ android:id="@+id/aggregation_suggestions"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/aggregation_suggestions_bg_light_holo"
- android:minWidth="100dip"
- android:paddingBottom="10dip"
- ex:layout_narrowParentWidth="600dip"
- ex:layout_narrowWidth="400dip"
- ex:layout_wideParentWidth="300dip"
- ex:layout_wideWidth="240dip">
- <TextView
- android:id="@+id/aggregation_suggestion_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_marginLeft="5dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorTertiary"
- android:textStyle="bold" />
-
- <LinearLayout
- android:id="@+id/aggregation_suggestions"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
- </LinearLayout>
-</com.android.contacts.widget.InterpolatingLayout>
\ No newline at end of file
+ android:orientation="vertical" />
+</LinearLayout>
diff --git a/res/layout-xlarge/aggregation_suggestions_item.xml b/res/layout-xlarge/aggregation_suggestions_item.xml
index 865c492..642b865 100644
--- a/res/layout-xlarge/aggregation_suggestions_item.xml
+++ b/res/layout-xlarge/aggregation_suggestions_item.xml
@@ -25,24 +25,6 @@
android:paddingLeft="5dip"
android:paddingRight="15dip"
>
- <Button
- android:id="@+id/aggregation_suggestion_join_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/aggregation_suggestion_join_button"
- android:layout_centerInParent="true"
- android:layout_alignParentRight="true"
- />
-
- <Button
- android:id="@+id/aggregation_suggestion_edit_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/aggregation_suggestion_edit_button"
- android:layout_centerInParent="true"
- android:layout_alignParentRight="true"
- />
-
<ImageView
android:id="@+id/aggregation_suggestion_photo"
android:layout_width="@dimen/aggregation_suggestion_icon_size"
@@ -58,10 +40,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/aggregation_suggestion_photo"
- android:layout_toLeftOf="@id/aggregation_suggestion_join_button"
android:layout_marginLeft="10dip"
android:layout_marginTop="4dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorSecondary"
/>
@@ -70,7 +51,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/aggregation_suggestion_photo"
- android:layout_toLeftOf="@id/aggregation_suggestion_join_button"
android:layout_below="@id/aggregation_suggestion_name"
android:layout_marginLeft="10dip"
android:textAppearance="?android:attr/textAppearanceMedium"
diff --git a/res/layout/aggregation_suggestions.xml b/res/layout/aggregation_suggestions.xml
index 0542daa..3b9d278 100644
--- a/res/layout/aggregation_suggestions.xml
+++ b/res/layout/aggregation_suggestions.xml
@@ -22,18 +22,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/aggregation_suggestions_bg">
- <TextView
- android:id="@+id/aggregation_suggestion_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_marginLeft="5dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorPrimary"
- android:textStyle="bold"
- />
-
<LinearLayout
android:id="@+id/aggregation_suggestions"
android:layout_width="match_parent"
diff --git a/res/layout/aggregation_suggestions_item.xml b/res/layout/aggregation_suggestions_item.xml
index 988508b..cd27e2b 100644
--- a/res/layout/aggregation_suggestions_item.xml
+++ b/res/layout/aggregation_suggestions_item.xml
@@ -25,23 +25,6 @@
android:paddingLeft="5dip"
android:paddingRight="15dip"
>
- <Button
- android:id="@+id/aggregation_suggestion_join_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/aggregation_suggestion_join_button"
- android:layout_centerInParent="true"
- android:layout_alignParentRight="true"
- />
-
- <Button
- android:id="@+id/aggregation_suggestion_edit_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/aggregation_suggestion_edit_button"
- android:layout_centerInParent="true"
- android:layout_alignParentRight="true"
- />
<ImageView
android:id="@+id/aggregation_suggestion_photo"
@@ -58,7 +41,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/aggregation_suggestion_photo"
- android:layout_toLeftOf="@id/aggregation_suggestion_join_button"
android:layout_marginLeft="10dip"
android:layout_marginTop="4dip"
android:textAppearance="?android:attr/textAppearanceSmall"
@@ -70,7 +52,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/aggregation_suggestion_photo"
- android:layout_toLeftOf="@id/aggregation_suggestion_join_button"
android:layout_below="@id/aggregation_suggestion_name"
android:layout_marginLeft="10dip"
android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 49a4186..84f1db1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1303,19 +1303,23 @@
<!-- The name of the invisible local contact directory -->
<string name="local_invisible_directory">Other</string>
- <!-- The heading of the contact aggregation suggestions section in Contact editor. [CHAR LIMIT=128]-->
- <plurals name="aggregation_suggestion_title">
- <item quantity="one">Similar contact</item>
- <item quantity="other">Similar contacts</item>
- </plurals>
+ <!-- The title of a confirmation dialog shown when the user selects a
+ contact aggregation suggestion in Contact editor. [CHAR LIMIT=128]-->
+ <string name="aggregation_suggestion_join_dialog_title">Join contacts</string>
- <!-- The heading of the aggregation suggestion section of the Contact editor
- indicating the contact that will be joined with the current contact. [CHAR LIMIT=128]-->
- <string name="aggregation_suggestion_joined_title">Joined contact:</string>
+ <!-- The message in a confirmation dialog shown when the user selects a
+ contact aggregation suggestion in Contact editor. [CHAR LIMIT=512]-->
+ <string name="aggregation_suggestion_join_dialog_message">Join
+ the current contact with the selected contact?</string>
- <!-- The button next to a contact aggregation suggestion in Contact editor. Causes a logical
- join with the suggested contact. [CHAR LIMIT=12]-->
- <string name="aggregation_suggestion_join_button">Join</string>
+ <!-- The title of a confirmation dialog shown when the user selects a
+ contact aggregation suggestion in Contact editor. [CHAR LIMIT=128]-->
+ <string name="aggregation_suggestion_edit_dialog_title">Edit selected contacts</string>
+
+ <!-- The message in a confirmation dialog shown when the user selects a
+ contact aggregation suggestion in Contact editor. [CHAR LIMIT=512]-->
+ <string name="aggregation_suggestion_edit_dialog_message">Switch to editing
+ the selected contact? Information you entered so far will be copied.</string>
<!-- The button next to a contact aggregation suggestion in Contact editor. Causes the UI
to switch to the suggested contact in the editor. [CHAR LIMIT=12]-->
diff --git a/src/com/android/contacts/model/EntityDeltaList.java b/src/com/android/contacts/model/EntityDeltaList.java
index 455cced..a998a37 100644
--- a/src/com/android/contacts/model/EntityDeltaList.java
+++ b/src/com/android/contacts/model/EntityDeltaList.java
@@ -43,7 +43,7 @@
*/
public class EntityDeltaList extends ArrayList<EntityDelta> implements Parcelable {
private boolean mSplitRawContacts;
- private List<Long> mJoinWithRawContactIds;
+ private long[] mJoinWithRawContactIds;
private EntityDeltaList() {
}
@@ -340,7 +340,7 @@
mSplitRawContacts = true;
}
- public void setJoinWithRawContacts(List<Long> rawContactIds) {
+ public void setJoinWithRawContacts(long[] rawContactIds) {
mJoinWithRawContactIds = rawContactIds;
}
@@ -357,7 +357,7 @@
for (EntityDelta delta : this) {
dest.writeParcelable(delta, flags);
}
- dest.writeList(mJoinWithRawContactIds);
+ dest.writeLongArray(mJoinWithRawContactIds);
}
@SuppressWarnings("unchecked")
@@ -367,7 +367,7 @@
for (int i = 0; i < size; i++) {
this.add(source.<EntityDelta> readParcelable(loader));
}
- mJoinWithRawContactIds = source.readArrayList(loader);
+ mJoinWithRawContactIds = source.createLongArray();
}
public static final Parcelable.Creator<EntityDeltaList> CREATOR =
diff --git a/src/com/android/contacts/views/editor/AggregationSuggestionView.java b/src/com/android/contacts/views/editor/AggregationSuggestionView.java
index 8c8adf7..0c35a39 100644
--- a/src/com/android/contacts/views/editor/AggregationSuggestionView.java
+++ b/src/com/android/contacts/views/editor/AggregationSuggestionView.java
@@ -17,21 +17,17 @@
package com.android.contacts.views.editor;
import com.android.contacts.R;
-import com.android.contacts.model.BaseAccountType;
import com.android.contacts.model.AccountTypes;
+import com.android.contacts.model.BaseAccountType;
import com.android.contacts.views.editor.AggregationSuggestionEngine.RawContact;
import com.android.contacts.views.editor.AggregationSuggestionEngine.Suggestion;
import com.google.android.collect.Lists;
-import android.content.ContentUris;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.ContactsContract.Contacts;
import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -42,7 +38,7 @@
/**
* A view that contains a name, picture and other data for a contact aggregation suggestion.
*/
-public class AggregationSuggestionView extends RelativeLayout implements OnClickListener {
+public class AggregationSuggestionView extends RelativeLayout {
public interface Listener {
@@ -67,14 +63,17 @@
public AggregationSuggestionView(Context context) {
super(context);
+ setClickable(true);
}
public AggregationSuggestionView(Context context, AttributeSet attrs) {
super(context, attrs);
+ setClickable(true);
}
public AggregationSuggestionView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ setClickable(true);
}
public void setNewContact(boolean flag) {
@@ -106,15 +105,6 @@
dataText = suggestion.phoneNumber;
}
data.setText(dataText);
-
- boolean canEdit = canEditSuggestedContact();
- Button join = (Button) findViewById(R.id.aggregation_suggestion_join_button);
- join.setOnClickListener(this);
- join.setVisibility(canEdit ? View.GONE : View.VISIBLE);
-
- Button edit = (Button) findViewById(R.id.aggregation_suggestion_edit_button);
- edit.setOnClickListener(this);
- edit.setVisibility(canEdit ? View.VISIBLE : View.GONE);
}
/**
@@ -146,17 +136,19 @@
}
@Override
- public void onClick(View v) {
+ public boolean performClick() {
if (mListener != null) {
- if (v.getId() == R.id.aggregation_suggestion_join_button) {
+ if (canEditSuggestedContact()) {
+ mListener.onEditAction(Contacts.getLookupUri(mContactId, mLookupKey));
+ } else {
ArrayList<Long> rawContactIds = Lists.newArrayList();
for (RawContact rawContact : mRawContacts) {
rawContactIds.add(rawContact.rawContactId);
}
mListener.onJoinAction(mContactId, rawContactIds);
- } else {
- mListener.onEditAction(Contacts.getLookupUri(mContactId, mLookupKey));
}
+ return true;
}
+ return false;
}
}
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index 6b24f3a..b7c35c0 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -34,6 +34,9 @@
import android.accounts.Account;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.app.Fragment;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
@@ -47,6 +50,7 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.CursorLoader;
+import android.content.DialogInterface;
import android.content.Entity;
import android.content.Intent;
import android.content.Loader;
@@ -78,9 +82,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewStub;
import android.widget.LinearLayout;
-import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
@@ -94,7 +98,8 @@
public class ContactEditorFragment extends Fragment implements
SplitContactConfirmationDialogFragment.Listener, PhotoDialogFragment.Listener,
- SelectAccountDialogFragment.Listener, AggregationSuggestionEngine.Listener {
+ SelectAccountDialogFragment.Listener, AggregationSuggestionEngine.Listener,
+ AggregationSuggestionView.Listener {
private static final String TAG = "ContactEditorFragment";
@@ -1177,7 +1182,8 @@
@Override
public void onAggregationSuggestionChange() {
- View rawContactView = getContactEditorView(mAggregationSuggestionsRawContactId);
+ RawContactEditorView rawContactView =
+ (RawContactEditorView)getRawContactEditorView(mAggregationSuggestionsRawContactId);
if (rawContactView == null) {
return;
}
@@ -1199,11 +1205,6 @@
List<Suggestion> suggestions = mAggregationSuggestionEngine.getSuggestions();
- TextView title = (TextView) mAggregationSuggestionView.findViewById(
- R.id.aggregation_suggestion_title);
- title.setText(getActivity().getResources().getQuantityString(
- R.plurals.aggregation_suggestion_title, count));
-
LinearLayout itemList = (LinearLayout) mAggregationSuggestionView.findViewById(
R.id.aggregation_suggestions);
itemList.removeAllViews();
@@ -1218,30 +1219,12 @@
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
suggestionView.setNewContact(mState.size() == 1 && mState.get(0).isContactInsert());
- suggestionView.setListener(new AggregationSuggestionView.Listener() {
-
- @Override
- public void onJoinAction(long contactId, List<Long> rawContactIds) {
- mState.setJoinWithRawContacts(rawContactIds);
- // If we are in the edit mode (indicated by a non-zero contact ID),
- // join the suggested contact, save all changes, and stay in the editor.
- doSaveAction(SaveMode.RELOAD);
- }
-
- @Override
- public void onEditAction(Uri contactLookupUri) {
- // Abandon the currently edited contact and switch to editing
- // the suggested one, transferring all the data there
- if (mListener != null) {
- mListener.onEditOtherContactRequested(
- contactLookupUri, mState.get(0).getContentValues());
- }
- }
- });
+ suggestionView.setListener(this);
suggestionView.bindSuggestion(suggestion);
itemList.addView(suggestionView);
}
+ adjustAggregationSuggestionViewLayout(rawContactView);
mAggregationSuggestionView.setVisibility(View.VISIBLE);
if (requestOnScreen) {
@@ -1256,6 +1239,115 @@
}
/**
+ * Adjusts the layout of the aggregation suggestion view so that it is placed directly
+ * underneath and have the same width as the last text editor of the contact name editor.
+ */
+ private void adjustAggregationSuggestionViewLayout(RawContactEditorView rawContactView) {
+ FieldEditorView nameEditor = rawContactView.getNameEditor();
+ Rect rect = new Rect();
+ nameEditor.acquireEditorBounds(rect);
+ MarginLayoutParams layoutParams =
+ (MarginLayoutParams) mAggregationSuggestionView.getLayoutParams();
+ layoutParams.leftMargin = rect.left;
+ layoutParams.width = rect.width();
+ mAggregationSuggestionView.setLayoutParams(layoutParams);
+ }
+
+ @Override
+ public void onJoinAction(long contactId, List<Long> rawContactIdList) {
+ long rawContactIds[] = new long[rawContactIdList.size()];
+ for (int i = 0; i < rawContactIds.length; i++) {
+ rawContactIds[i] = rawContactIdList.get(i);
+ }
+ JoinSuggestedContactDialogFragment dialog =
+ new JoinSuggestedContactDialogFragment();
+ Bundle args = new Bundle();
+ args.putLongArray("rawContactIds", rawContactIds);
+ dialog.setArguments(args);
+ dialog.setTargetFragment(this, 0);
+ dialog.show(getFragmentManager(), "join");
+ }
+
+ public static class JoinSuggestedContactDialogFragment extends DialogFragment {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.aggregation_suggestion_join_dialog_title)
+ .setMessage(R.string.aggregation_suggestion_join_dialog_message)
+ .setPositiveButton(android.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ ContactEditorFragment targetFragment =
+ (ContactEditorFragment) getTargetFragment();
+ long rawContactIds[] =
+ getArguments().getLongArray("rawContactIds");
+ targetFragment.doJoinSuggestedContact(rawContactIds);
+ }
+ }
+ )
+ .setNegativeButton(android.R.string.no, null)
+ .create();
+ }
+ }
+
+ /**
+ * Joins the suggested contact (specified by the id's of constituent raw
+ * contacts), save all changes, and stay in the editor.
+ */
+ protected void doJoinSuggestedContact(long[] rawContactIds) {
+ mState.setJoinWithRawContacts(rawContactIds);
+ doSaveAction(SaveMode.RELOAD);
+ }
+
+ @Override
+ public void onEditAction(Uri contactLookupUri) {
+ SuggestionEditConfirmationDialogFragment dialog =
+ new SuggestionEditConfirmationDialogFragment();
+ Bundle args = new Bundle();
+ args.putParcelable("contactUri", contactLookupUri);
+ dialog.setArguments(args);
+ dialog.setTargetFragment(this, 0);
+ dialog.show(getFragmentManager(), "edit");
+ }
+
+ public static class SuggestionEditConfirmationDialogFragment extends DialogFragment {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.aggregation_suggestion_edit_dialog_title)
+ .setMessage(R.string.aggregation_suggestion_edit_dialog_message)
+ .setPositiveButton(android.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ ContactEditorFragment targetFragment =
+ (ContactEditorFragment) getTargetFragment();
+ Uri contactUri =
+ getArguments().getParcelable("contactUri");
+ targetFragment.doEditSuggestedContact(contactUri);
+ }
+ }
+ )
+ .setNegativeButton(android.R.string.no, null)
+ .create();
+ }
+ }
+
+ /**
+ * Abandons the currently edited contact and switches to editing the suggested
+ * one, transferring all the data there
+ */
+ protected void doEditSuggestedContact(Uri contactUri) {
+ if (mListener != null) {
+ mListener.onEditOtherContactRequested(
+ contactUri, mState.get(0).getContentValues());
+ }
+ }
+
+ /**
* Scrolls the editor if necessary to reveal the aggregation suggestion that is
* shown below the name editor. Makes sure that the currently focused field
* remains visible.
@@ -1480,7 +1572,7 @@
* Sets the photo stored in mPhoto and writes it to the RawContact with the given id
*/
private void setPhoto(long rawContact, Bitmap photo) {
- BaseRawContactEditorView requestingEditor = getContactEditorView(rawContact);
+ BaseRawContactEditorView requestingEditor = getRawContactEditorView(rawContact);
if (requestingEditor != null) {
requestingEditor.setPhotoBitmap(photo);
} else {
@@ -1491,7 +1583,7 @@
/**
* Finds raw contact editor view for the given rawContactId.
*/
- public BaseRawContactEditorView getContactEditorView(long rawContactId) {
+ public BaseRawContactEditorView getRawContactEditorView(long rawContactId) {
for (int i = 0; i < mContent.getChildCount(); i++) {
final View childView = mContent.getChildAt(i);
if (childView instanceof BaseRawContactEditorView) {
diff --git a/src/com/android/contacts/views/editor/FieldEditorView.java b/src/com/android/contacts/views/editor/FieldEditorView.java
index 926782d..c16c7ed 100644
--- a/src/com/android/contacts/views/editor/FieldEditorView.java
+++ b/src/com/android/contacts/views/editor/FieldEditorView.java
@@ -33,6 +33,7 @@
import android.content.DialogInterface;
import android.content.Entity;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -685,4 +686,20 @@
mFieldEditTexts[0].requestFocus();
}
}
+
+ /**
+ * Populates the bound rectangle with the bounds of the last editor field inside this view.
+ */
+ public void acquireEditorBounds(Rect bounds) {
+ if (mFieldEditTexts != null) {
+ for (int i = mFieldEditTexts.length; --i >= 0;) {
+ EditText editText = mFieldEditTexts[i];
+ if (editText.getVisibility() == View.VISIBLE) {
+ bounds.set(editText.getLeft(), editText.getTop(), editText.getRight(),
+ editText.getBottom());
+ return;
+ }
+ }
+ }
+ }
}