Merge "Insert name into toast during contact deletion"
diff --git a/res/layout/item_kind_section.xml b/res/layout/item_kind_section.xml
deleted file mode 100644
index d62523b..0000000
--- a/res/layout/item_kind_section.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<!-- the body surrounding all editors for a specific kind -->
-
-<com.android.contacts.editor.KindSectionView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/kind_icon"
- style="@style/EditKindIconStyle" />
-
- <LinearLayout
- android:id="@+id/kind_editors"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
-
-</com.android.contacts.editor.KindSectionView>
\ No newline at end of file
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
deleted file mode 100644
index e3eff4e..0000000
--- a/res/layout/item_photo_editor.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<view class="com.android.contacts.editor.PhotoEditorView"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <ImageView
- android:id="@+id/kind_icon"
- android:src="@drawable/ic_camera_alt_black_24dp"
- android:layout_marginTop="13dp"
- android:contentDescription="@string/header_photo_entry"
- style="@style/EditKindIconStyle" />
-
- <!-- Needs 10dp of top padding, in order get a total of 32dp of padding between this view
- and the previous DataKindSection. Note that EditTexts in other editor.xml files have this
- padding built in. Similarly, we need to add 4dp of start margin to make up for the padding
- that an EditText would have in this image's place. -->
- <ImageView
- android:id="@+id/photo"
- android:layout_width="72dip"
- android:layout_height="72dip"
- android:cropToPadding="true"
- android:scaleType="centerCrop"
- android:layout_marginTop="15dp"
- android:layout_marginStart="4dp"
- android:contentDescription="@string/description_contact_photo"
- android:layout_marginBottom="@dimen/editor_padding_below_photo"
- android:gravity="start" />
-
- <!-- We want 16dp for the effective marginStart. So we set 12dp, since the private
- @android:dimen/control_inset_material already includes 4dp of padding. -->
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="12dp" >
-
- <!-- The values applied to this button are complicated:
- 1) We want 16dp internal padding in the button. The background drawable is inset
- by private @android:dimen/button_inset_horizontal_material=4dp. Therefore,
- we need paddingStart/End of 20dp.
- 2) In order to leave enough room for the 32dp RadioButton, this can only be 46dp.
- This is 2dp less than the default touch target size.
- 3) This button will appear to be offset by the private
- @android:dimen/button_inset_vertical_material amount. Therefore, in order to achieve
- 15dp of apparent top margin, we only need to apply 9dp. -->
- <Button
- android:id="@+id/change_button"
- android:layout_width="wrap_content"
- android:layout_height="46dp"
- android:textSize="@dimen/editor_form_text_size"
- android:textColor="@color/primary_text_color"
- android:layout_marginTop="9dp"
- android:paddingStart="20dp"
- android:paddingEnd="20dp"
- android:text="@string/change_photo" />
-
- <!-- Don't explicitly set the layout_height in case we need to rely on text wrapping.
- For one line, we can expect the height to be 32dp with 16dp text size. -->
- <RadioButton
- android:id="@+id/primary_checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="48dp"
- android:textSize="@dimen/editor_form_text_size"
- android:text="@string/primary_photo" />
- </LinearLayout>
-</view>
diff --git a/res/layout/item_photo_editor_readonly.xml b/res/layout/item_photo_editor_readonly.xml
deleted file mode 100644
index edb6f61..0000000
--- a/res/layout/item_photo_editor_readonly.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?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.
--->
-
-<!-- A readonly version of item_photo_editor.xml shown in the readonly raw contact editor. -->
-<view class="com.android.contacts.editor.PhotoEditorView"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <ImageView
- android:id="@+id/kind_icon"
- android:src="@drawable/ic_camera_alt_black_24dp"
- android:layout_marginTop="0dp"
- android:contentDescription="@string/header_photo_entry"
- style="@style/EditKindIconStyle" />
-
- <!-- We need to add 2dp of start margin to make up for the padding that a TextView would
- have in this image's place. We add 2dp of top margin, so that icon drawable is a little
- below the top of this ImageView. -->
- <ImageView
- android:id="@+id/photo"
- android:layout_width="72dip"
- android:layout_height="72dip"
- android:cropToPadding="true"
- android:scaleType="centerCrop"
- android:layout_marginTop="2dp"
- android:layout_marginStart="2dp"
- android:contentDescription="@string/description_contact_photo"
- android:layout_marginBottom="@dimen/editor_padding_around_read_only_photo_editor"
- android:gravity="start" />
-
-
- <!-- Don't explicitly set the layout_height in case we need to rely on text wrapping.
- For one line, we can expect the height to be 32dp with 16dp text size. -->
- <RadioButton
- android:id="@+id/primary_checkbox"
- android:layout_marginStart="12dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/editor_delete_button_width"
- android:layout_marginTop="2dp"
- android:textSize="@dimen/editor_form_text_size"
- android:text="@string/primary_photo" />
-</view>
diff --git a/res/layout/raw_contact_readonly_editor_view.xml b/res/layout/raw_contact_readonly_editor_view.xml
deleted file mode 100644
index e50aa98..0000000
--- a/res/layout/raw_contact_readonly_editor_view.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<com.android.contacts.editor.RawContactReadOnlyEditorView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <include
- layout="@layout/editor_account_header" />
-
- <LinearLayout
- android:id="@+id/collapsable_section"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <!-- Want 16dp of apparent top padding. Since TextView has 4dp of inset/padding built in,
- only set marginTop=12dp. -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/editor_padding_around_read_only_photo_editor"
- android:layout_marginTop="12dp"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/kind_icon"
- android:layout_marginTop="2dp"
- android:src="@drawable/ic_person_black_24dp"
- android:contentDescription="@string/header_name_entry"
- style="@style/EditKindIconStyle" />
-
- <TextView
- android:id="@+id/read_only_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginRight="@dimen/editor_delete_button_width"
- android:layout_marginEnd="@dimen/editor_delete_button_width"
- android:singleLine="true"
- android:textSize="@dimen/editor_form_text_size"
- android:textColor="?android:attr/textColorSecondary"
- android:textAlignment="viewStart"
- 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="4dp"
- 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>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="@dimen/divider_line_height"
- android:background="@color/divider_line_color_light" />
-
-</com.android.contacts.editor.RawContactReadOnlyEditorView>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5a8fb88..e1c70ff 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -90,15 +90,6 @@
padding between TextView's in the readonly Raw Contact Editor. -->
<dimen name="editor_padding_between_read_only_editor_views">9dp</dimen>
- <!-- Padding above and below the photo editor. This value is chosen to give 19dp of apparent
- padding between TextView's and the photo's ImageView. -->
- <dimen name="editor_padding_around_read_only_photo_editor">15dp</dimen>
-
- <!-- Padding below the photo editor. This value is larger than
- editor_padding_between_editor_views, since ImageView's don't have space between the bottom
- of their visual bottom, like an EditText does. -->
- <dimen name="editor_padding_below_photo">9dp</dimen>
-
<!-- Width of the Type-Label in the Editor -->
<dimen name="editor_type_label_width">150dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 09df33b..8ee4c8d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -387,12 +387,6 @@
the email address, e.g. "Add xyz@foo.com to contacts?" -->
<string name="add_contact_dlg_message_fmt">Add \"<xliff:g id="email">%s</xliff:g>\" to contacts?</string>
- <!-- String describing the Contact Photo Image
-
- Used by AccessibilityService to announce the purpose of the view.
- -->
- <string name="description_contact_photo">contact photo</string>
-
<!-- String describing the Contact Editor Plus button
Used by AccessibilityService to announce the purpose of the button.
@@ -520,9 +514,6 @@
<!-- Button used for changing a photo in the Raw Contact Editor [CHAR LIMIT=15] -->
<string name="change_photo">Change</string>
- <!-- RadioButton that determines whether a raw contact's photo should be used for the entire contact [CHAR LIMIT=25] -->
- <string name="primary_photo">Primary photo</string>
-
<!-- String describing the Star/Favorite checkbox
Used by AccessibilityService to announce the purpose of the view.
@@ -768,8 +759,6 @@
<string name="header_email_entry">Email</string>
<!-- Content description for the phone fields header entry [CHAR LIMIT=NONE] -->
<string name="header_phone_entry">Phone</string>
- <!-- 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>
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 9a0aba5..0d29e06 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -54,7 +54,6 @@
import com.android.contacts.R;
import com.android.contacts.common.Experiments;
import com.android.contacts.common.activity.RequestPermissionsActivity;
-import com.android.contacts.common.interactions.ImportExportDialogFragment;
import com.android.contacts.common.list.ContactListFilter;
import com.android.contacts.common.list.ProviderStatusWatcher;
import com.android.contacts.common.list.ProviderStatusWatcher.ProviderStatusListener;
@@ -66,7 +65,6 @@
import com.android.contacts.common.util.Constants;
import com.android.contacts.common.util.ImplicitIntentsUtil;
import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.contacts.editor.EditorIntents;
import com.android.contacts.group.GroupMembersFragment;
import com.android.contacts.group.GroupMetaData;
import com.android.contacts.group.GroupUtil;
@@ -74,9 +72,9 @@
import com.android.contacts.list.ContactsRequest;
import com.android.contacts.list.ContactsUnavailableFragment;
import com.android.contacts.list.DefaultContactBrowseListFragment;
-import com.android.contacts.list.OnContactsUnavailableActionListener;
import com.android.contacts.quickcontact.QuickContactActivity;
import com.android.contacts.util.SyncUtil;
+import com.android.contactsbind.FeatureHighlightHelper;
import com.android.contactsbind.ObjectFactory;
import com.android.contactsbind.experiments.Flags;
import com.android.contacts.widget.FloatingActionButtonBehavior;
@@ -199,26 +197,6 @@
return (mProviderStatus != null) && mProviderStatus.equals(ProviderStatus.STATUS_NORMAL);
}
- /**
- * Initialize fragments that are (or may not be) in the layout.
- *
- * For the fragments that are in the layout, we initialize them in
- * {@link #createViewsAndFragments()} after inflating the layout.
- *
- * However, the {@link ContactsUnavailableFragment} is a special fragment which may not
- * be in the layout, so we have to do the initialization here.
- *
- * The ContactsUnavailableFragment is always created at runtime.
- */
- @Override
- public void onAttachFragment(Fragment fragment) {
- if (fragment instanceof ContactsUnavailableFragment) {
- mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
- mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
- new ContactsUnavailableFragmentListener());
- }
- }
-
@Override
protected void onCreate(Bundle savedState) {
if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
@@ -553,8 +531,6 @@
}
if (mContactsUnavailableFragment == null) {
mContactsUnavailableFragment = new ContactsUnavailableFragment();
- mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
- new ContactsUnavailableFragmentListener());
transaction.add(R.id.contacts_list_container, mContactsUnavailableFragment,
TAG_UNAVAILABLE);
}
@@ -588,28 +564,6 @@
return !allAccounts.get(0).isLocalAccount();
}
- private class ContactsUnavailableFragmentListener
- implements OnContactsUnavailableActionListener {
- ContactsUnavailableFragmentListener() {}
-
- @Override
- public void onCreateNewContactAction() {
- ImplicitIntentsUtil.startActivityInApp(PeopleActivity.this,
- EditorIntents.createCompactInsertContactIntent(PeopleActivity.this));
- }
-
- @Override
- public void onAddAccountAction() {
- final Intent intent = ImplicitIntentsUtil.getIntentForAddingGoogleAccount();
- ImplicitIntentsUtil.startActivityOutsideApp(PeopleActivity.this, intent);
- }
-
- @Override
- public void onImportContactsFromFileAction() {
- showImportExportDialogFragment();
- }
- }
-
private void invalidateOptionsMenuIfNeeded() {
if (mAllFragment != null
&& mAllFragment.getOptionsMenuContactsAvailable() != areContactsAvailable()) {
@@ -617,11 +571,6 @@
}
}
- private void showImportExportDialogFragment(){
- ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
- PeopleActivity.class, ImportExportDialogFragment.EXPORT_MODE_ALL_CONTACTS);
- }
-
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Bring up the search UI if the user starts typing
@@ -644,26 +593,57 @@
return;
}
+ // Handle the back event in drawer first.
if (mDrawer.isDrawerOpen(GravityCompat.START)) {
mDrawer.closeDrawer(GravityCompat.START);
- } else if (isGroupView()) {
- if (mMembersFragment.isEditMode()) {
- mMembersFragment.exitEditMode();
- } else if (mMembersFragment.getActionBarAdapter().isSelectionMode()) {
- mMembersFragment.getActionBarAdapter().setSelectionMode(false);
- mMembersFragment.displayCheckBoxes(false);
- } else if (mMembersFragment.getActionBarAdapter().isSearchMode()) {
- mMembersFragment.getActionBarAdapter().setSearchMode(false);
- } else {
- switchToAllContacts();
- }
- } else if (isDuplicatesView()) {
- switchToAllContacts();
- } else if (mAllFragment.tryRemoveHighlight()) {
return;
- } else if (isAllFragmentInSelectionMode()) {
+ }
+
+ // Handle the back event in "second level".
+ if (isGroupView()) {
+ onBackPressedGroupView();
+ return;
+ }
+
+ if (isDuplicatesView()) {
+ switchToAllContacts();
+ return;
+ }
+
+ // If feature highlight is present, let it handle the back event before mAllFragment.
+ if (FeatureHighlightHelper.tryRemoveHighlight(this)) {
+ return;
+ }
+
+ // Handle the back event in "first level" - mAllFragment.
+ if (maybeHandleInAllFragment()) {
+ return;
+ }
+
+ super.onBackPressed();
+ }
+
+ private void onBackPressedGroupView() {
+ if (mMembersFragment.isEditMode()) {
+ mMembersFragment.exitEditMode();
+ } else if (mMembersFragment.getActionBarAdapter().isSelectionMode()) {
+ mMembersFragment.getActionBarAdapter().setSelectionMode(false);
+ mMembersFragment.displayCheckBoxes(false);
+ } else if (mMembersFragment.getActionBarAdapter().isSearchMode()) {
+ mMembersFragment.getActionBarAdapter().setSearchMode(false);
+ } else {
+ switchToAllContacts();
+ }
+ }
+
+ // Returns true if back event is handled in this method.
+ private boolean maybeHandleInAllFragment() {
+ if (isAllFragmentInSelectionMode()) {
mAllFragment.getActionBarAdapter().setSelectionMode(false);
- } else if (isAllFragmentInSearchMode()) {
+ return true;
+ }
+
+ if (isAllFragmentInSearchMode()) {
mAllFragment.getActionBarAdapter().setSearchMode(false);
if (mAllFragment.wasSearchResultClicked()) {
mAllFragment.resetSearchResultClicked();
@@ -671,14 +651,18 @@
Logger.logScreenView(this, ScreenType.SEARCH_EXIT);
Logger.logSearchEvent(mAllFragment.createSearchState());
}
- } else if (!AccountFilterUtil.isAllContactsFilter(mAllFragment.getFilter())
+ return true;
+ }
+
+ if (!AccountFilterUtil.isAllContactsFilter(mAllFragment.getFilter())
&& !mAllFragment.isHidden()) {
// If mAllFragment is hidden, then mContactsUnavailableFragment is visible so we
// don't need to switch to all contacts.
switchToAllContacts();
- } else {
- super.onBackPressed();
+ return true;
}
+
+ return false;
}
private boolean isAllFragmentInSelectionMode() {
diff --git a/src/com/android/contacts/common/list/ContactListFilter.java b/src/com/android/contacts/common/list/ContactListFilter.java
index 63eae17..c6baf41 100644
--- a/src/com/android/contacts/common/list/ContactListFilter.java
+++ b/src/com/android/contacts/common/list/ContactListFilter.java
@@ -335,9 +335,12 @@
throw new IllegalStateException(
"filterType must be FILTER_TYPE_ACCOUNT or FILER_TYPE_GROUP_MEMBERS");
}
- uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
- uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
- if (!TextUtils.isEmpty(dataSet)) {
+ // null account names are not valid, see ContactsProvider2#appendAccountFromParameter
+ if (accountName != null) {
+ uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
+ uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
+ }
+ if (dataSet != null) {
uriBuilder.appendQueryParameter(RawContacts.DATA_SET, dataSet);
}
return uriBuilder;
diff --git a/src/com/android/contacts/common/logging/Logger.java b/src/com/android/contacts/common/logging/Logger.java
index 3ce208e..68cbbbb 100644
--- a/src/com/android/contacts/common/logging/Logger.java
+++ b/src/com/android/contacts/common/logging/Logger.java
@@ -87,11 +87,11 @@
final Logger logger = getInstance();
if (logger != null) {
final QuickContactEvent event = new QuickContactEvent();
- event.referrer = referrer;
+ event.referrer = referrer == null ? "Unknown" : referrer;
event.contactType = contactType;
event.cardType = cardType;
event.actionType = actionType;
- event.thirdPartyAction = thirdPartyAction;
+ event.thirdPartyAction = thirdPartyAction == null ? "" : thirdPartyAction;
logger.logQuickContactEventImpl(event);
}
}
diff --git a/src/com/android/contacts/common/model/DeviceLocalAccountLocator.java b/src/com/android/contacts/common/model/DeviceLocalAccountLocator.java
index 0efadc4..8c45c40 100644
--- a/src/com/android/contacts/common/model/DeviceLocalAccountLocator.java
+++ b/src/com/android/contacts/common/model/DeviceLocalAccountLocator.java
@@ -78,16 +78,8 @@
// Many device accounts have default groups associated with them.
addAccountsFromQuery(ContactsContract.Groups.CONTENT_URI, localAccounts);
-
addAccountsFromQuery(ContactsContract.Settings.CONTENT_URI, localAccounts);
-
- if (localAccounts.isEmpty()) {
- // It's probably safe to assume that if one of the earlier queries found a "device"
- // account then this query isn't going to find any different device accounts.
- // We skip this query because it probably is kind of expensive (relative to the other
- // queries).
- addAccountsFromQuery(ContactsContract.RawContacts.CONTENT_URI, localAccounts);
- }
+ addAccountsFromQuery(ContactsContract.RawContacts.CONTENT_URI, localAccounts);
return new ArrayList<>(localAccounts);
}
diff --git a/src/com/android/contacts/editor/BaseRawContactEditorView.java b/src/com/android/contacts/editor/BaseRawContactEditorView.java
deleted file mode 100644
index 1cdfafb..0000000
--- a/src/com/android/contacts/editor/BaseRawContactEditorView.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2009 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.contacts.editor;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-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;
-import com.android.contacts.common.model.ValuesDelta;
-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
- * RawContact represented through an {@link RawContactDelta}.
- * <p>
- * Internal updates are performed against {@link ValuesDelta} so that the
- * source {@link RawContact} can be swapped out. Any state-based changes, such as
- * adding {@link Data} rows or changing {@link EditType}, are performed through
- * {@link RawContactModifier} to ensure that {@link AccountType} are enforced.
- */
-public abstract class BaseRawContactEditorView extends LinearLayout {
-
- private PhotoEditorView mPhoto;
-
- private View mAccountHeaderContainer;
- private ImageView mExpandAccountButton;
- private LinearLayout mCollapsibleSection;
- private TextView mAccountName;
- private TextView mAccountType;
-
- protected Listener mListener;
-
- public interface Listener {
- void onExternalEditorRequest(AccountWithDataSet account, Uri uri);
- void onEditorExpansionChanged();
- }
-
- public BaseRawContactEditorView(Context context) {
- super(context);
- }
-
- public BaseRawContactEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
- mPhoto.setEnabled(isEnabled());
-
- mAccountHeaderContainer = findViewById(R.id.account_header_container);
- mExpandAccountButton = (ImageView) findViewById(R.id.account_expander_icon);
- mExpandAccountButton.setColorFilter(R.color.quantum_black_secondary_text);
- 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.
- */
- public void setPhotoEntry(Bitmap bitmap) {
- mPhoto.setPhotoEntry(bitmap);
- }
-
- /**
- * Assign the given photo {@link Uri} to UI of the {@link PhotoEditorView}, so that it can
- * display a full sized photo.
- */
- public void setFullSizedPhoto(Uri uri) {
- mPhoto.setFullSizedPhoto(uri);
- }
-
- protected void setHasPhotoEditor(boolean hasPhotoEditor) {
- mPhoto.setVisibility(hasPhotoEditor ? View.VISIBLE : View.GONE);
- }
-
- /**
- * Return true if internal {@link PhotoEditorView} has a {@link Photo} set.
- */
- public boolean hasSetPhoto() {
- return mPhoto.hasSetPhoto();
- }
-
- public PhotoEditorView getPhotoEditor() {
- return mPhoto;
- }
-
- /**
- * @return the RawContact ID that this editor is editing.
- */
- 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);
- // We want to place the focus near the top of the screen now that a
- // potentially focused editor is being collapsed.
- EditorAnimator.placeFocusAtTopOfScreenAfterReLayout(mCollapsibleSection);
- } else {
- // When expanding we should scroll the expanded view onto the screen.
- // Otherwise, user's may not notice that any expansion happened.
- EditorAnimator.getInstance().scrollViewToTop(mAccountHeaderContainer);
- mCollapsibleSection.requestFocus();
- }
- 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.setImageDrawable(getContext().getDrawable(
- R.drawable.ic_menu_expand_minimized_24dp));
- } else {
- params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- mCollapsibleSection.setLayoutParams(params);
- mExpandAccountButton.setImageDrawable(getContext().getDrawable(
- R.drawable.ic_menu_expand_maximized_24dp));
- }
- }
-
- protected void updateAccountHeaderContentDescription() {
- final StringBuilder builder = new StringBuilder();
- builder.append(EditorUiUtils.getAccountInfoContentDescription(
- mAccountName.getText(), mAccountType.getText()));
- 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);
-}
diff --git a/src/com/android/contacts/editor/CompactKindSectionView.java b/src/com/android/contacts/editor/CompactKindSectionView.java
index f550c6d..cbc4772 100644
--- a/src/com/android/contacts/editor/CompactKindSectionView.java
+++ b/src/com/android/contacts/editor/CompactKindSectionView.java
@@ -16,13 +16,6 @@
package com.android.contacts.editor;
-import com.android.contacts.R;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.model.RawContactModifier;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.account.AccountType;
-import com.android.contacts.common.model.dataitem.DataKind;
-
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.Event;
@@ -37,11 +30,20 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.contacts.R;
+import com.android.contacts.common.model.RawContactDelta;
+import com.android.contacts.common.model.RawContactModifier;
+import com.android.contacts.common.model.ValuesDelta;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.dataitem.DataKind;
+
import java.util.ArrayList;
import java.util.List;
/**
- * Version of {@link KindSectionView} that supports multiple RawContactDeltas.
+ * Custom view for an entire section of data as segmented by
+ * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a
+ * section header and a trigger for adding new {@link Data} rows.
*/
public class CompactKindSectionView extends LinearLayout {
@@ -147,7 +149,7 @@
}
}
- private KindSectionDataList mKindSectionDataList;
+ private KindSectionData mKindSectionData;
private ViewIdGenerator mViewIdGenerator;
private CompactRawContactsEditorView.Listener mListener;
@@ -180,6 +182,7 @@
@Override
protected void onFinishInflate() {
+ super.onFinishInflate();
setDrawingCacheEnabled(true);
setAlwaysDrawnWithCacheEnabled(true);
@@ -227,7 +230,7 @@
* and nicknames) are empty.
*/
public boolean isEmptyName() {
- if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionDataList.getMimeType())) {
+ if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionData.getMimeType())) {
return false;
}
for (int i = 0; i < mEditors.getChildCount(); i++) {
@@ -247,7 +250,7 @@
* without informing editor listeners.
*/
public void setName(String displayName) {
- if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionDataList.getMimeType())) {
+ if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionData.getMimeType())) {
return;
}
for (int i = 0; i < mEditors.getChildCount(); i++) {
@@ -270,7 +273,7 @@
}
public StructuredNameEditorView getPrimaryNameEditorView() {
- if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionDataList.getMimeType())
+ if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionData.getMimeType())
|| mEditors.getChildCount() == 0) {
return null;
}
@@ -278,7 +281,7 @@
}
/**
- * Binds views for the given {@link KindSectionData} list.
+ * Binds views for the given {@link KindSectionData}.
*
* We create a structured name and phonetic name editor for each {@link DataKind} with a
* {@link StructuredName#CONTENT_ITEM_TYPE} mime type. The number and order of editors are
@@ -287,15 +290,15 @@
* Empty name editors are never added and at least one structured name editor is always
* displayed, even if it is empty.
*/
- public void setState(KindSectionDataList kindSectionDataList,
+ public void setState(KindSectionData kindSectionData,
ViewIdGenerator viewIdGenerator, CompactRawContactsEditorView.Listener listener,
ValuesDelta primaryValuesDelta) {
- mKindSectionDataList = kindSectionDataList;
+ mKindSectionData = kindSectionData;
mViewIdGenerator = viewIdGenerator;
mListener = listener;
- // Set the icon using the first DataKind
- final DataKind dataKind = mKindSectionDataList.getDataKind();
+ // Set the icon using the DataKind
+ final DataKind dataKind = mKindSectionData.getDataKind();
if (dataKind != null) {
mIcon.setImageDrawable(EditorUiUtils.getMimeTypeDrawable(getContext(),
dataKind.mimeType));
@@ -313,27 +316,26 @@
private void rebuildFromState(ValuesDelta primaryValuesDelta) {
mEditors.removeAllViews();
- final String mimeType = mKindSectionDataList.getMimeType();
- for (KindSectionData kindSectionData : mKindSectionDataList) {
- if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
- addNameEditorViews(kindSectionData.getAccountType(),
- primaryValuesDelta, kindSectionData.getRawContactDelta());
- } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
- addGroupEditorView(kindSectionData.getRawContactDelta(),
- kindSectionData.getDataKind());
+ final String mimeType = mKindSectionData.getMimeType();
+ if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ addNameEditorViews(mKindSectionData.getAccountType(),
+ primaryValuesDelta, mKindSectionData.getRawContactDelta());
+ } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ addGroupEditorView(mKindSectionData.getRawContactDelta(),
+ mKindSectionData.getDataKind());
+ } else {
+ final Editor.EditorListener editorListener;
+ if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ editorListener = new OtherNameKindEditorListener();
+ } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ editorListener = new EventEditorListener();
} else {
- final Editor.EditorListener editorListener;
- if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
- editorListener = new OtherNameKindEditorListener();
- } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
- editorListener = new EventEditorListener();
- } else {
- editorListener = new NonNameEditorListener();
- }
- for (ValuesDelta valuesDelta : kindSectionData.getVisibleValuesDeltas()) {
- addNonNameEditorView(kindSectionData.getRawContactDelta(),
- kindSectionData.getDataKind(), valuesDelta, editorListener);
- }
+ editorListener = new NonNameEditorListener();
+ }
+ final List<ValuesDelta> valuesDeltas = mKindSectionData.getVisibleValuesDeltas();
+ for (int i = 0; i < valuesDeltas.size(); i++ ) {
+ addNonNameEditorView(mKindSectionData.getRawContactDelta(),
+ mKindSectionData.getDataKind(), valuesDeltas.get(i), editorListener);
}
}
}
@@ -442,9 +444,9 @@
*/
public void updateEmptyEditors(boolean shouldAnimate) {
final boolean isNameKindSection = StructuredName.CONTENT_ITEM_TYPE.equals(
- mKindSectionDataList.getMimeType());
+ mKindSectionData.getMimeType());
final boolean isGroupKindSection = GroupMembership.CONTENT_ITEM_TYPE.equals(
- mKindSectionDataList.getMimeType());
+ mKindSectionData.getMimeType());
if (isNameKindSection) {
// The name kind section is always visible
@@ -531,7 +533,8 @@
if (emptyEditors.size() > 1) {
// If there is more than 1 empty editor, then remove it from the list of editors.
int deleted = 0;
- for (final View view : emptyEditors) {
+ for (int i = 0; i < emptyEditors.size(); i++) {
+ final View view = emptyEditors.get(i);
// If no child {@link View}s are being focused on within this {@link View}, then
// remove this empty editor. We can assume that at least one empty editor has
// focus. One way to get two empty editors is by deleting characters from a
@@ -546,9 +549,8 @@
return;
}
// Determine if we should add a new empty editor
- final DataKind dataKind = mKindSectionDataList.get(0).getDataKind();
- final RawContactDelta rawContactDelta =
- mKindSectionDataList.get(0).getRawContactDelta();
+ final DataKind dataKind = mKindSectionData.getDataKind();
+ final RawContactDelta rawContactDelta = mKindSectionData.getRawContactDelta();
if (dataKind == null // There is nothing we can do.
// We have already reached the maximum number of editors, don't add any more.
|| !RawContactModifier.canInsert(rawContactDelta, dataKind)
@@ -558,7 +560,7 @@
}
// Add a new empty editor
if (mShowOneEmptyEditor) {
- final String mimeType = mKindSectionDataList.getMimeType();
+ final String mimeType = mKindSectionData.getMimeType();
if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && mEditors.getChildCount() > 0) {
return;
}
diff --git a/src/com/android/contacts/editor/CompactPhotoEditorView.java b/src/com/android/contacts/editor/CompactPhotoEditorView.java
index 899e22a..99abb73 100644
--- a/src/com/android/contacts/editor/CompactPhotoEditorView.java
+++ b/src/com/android/contacts/editor/CompactPhotoEditorView.java
@@ -16,13 +16,6 @@
package com.android.contacts.editor;
-import com.android.contacts.R;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-import com.android.contacts.util.SchedulingUtils;
-import com.android.contacts.widget.QuickContactImageView;
-
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
@@ -36,6 +29,13 @@
import android.view.ViewGroup;
import android.widget.RelativeLayout;
+import com.android.contacts.R;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.model.ValuesDelta;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.contacts.util.SchedulingUtils;
+import com.android.contacts.widget.QuickContactImageView;
+
/**
* Displays a photo and calls the host back when the user clicks it.
*/
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index 922987b..c853981 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -18,8 +18,10 @@
import android.content.ContentUris;
import android.content.Context;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -54,6 +56,8 @@
import android.widget.TextView;
import com.android.contacts.R;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.RawContactDeltaList;
@@ -63,6 +67,7 @@
import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.dataitem.CustomDataItem;
import com.android.contacts.common.model.dataitem.DataKind;
import com.android.contacts.common.util.AccountsListAdapter;
import com.android.contacts.common.util.MaterialColorMapUtils;
@@ -76,6 +81,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeSet;
/**
@@ -85,9 +91,6 @@
static final String TAG = "CompactEditorView";
- private static final KindSectionDataMapEntryComparator
- KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR = new KindSectionDataMapEntryComparator();
-
/**
* Callbacks for hosts of {@link CompactRawContactsEditorView}s.
*/
@@ -195,26 +198,6 @@
}
}
- /** Used to sort entire kind sections. */
- private static final class KindSectionDataMapEntryComparator implements
- Comparator<Map.Entry<String,KindSectionDataList>> {
-
- final MimeTypeComparator mMimeTypeComparator = new MimeTypeComparator();
-
- @Override
- public int compare(Map.Entry<String, KindSectionDataList> entry1,
- Map.Entry<String, KindSectionDataList> entry2) {
- if (entry1 == entry2) return 0;
- if (entry1 == null) return -1;
- if (entry2 == null) return 1;
-
- final String mimeType1 = entry1.getKey();
- final String mimeType2 = entry2.getKey();
-
- return mMimeTypeComparator.compare(mimeType1, mimeType2);
- }
- }
-
/**
* Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
* <ol>
@@ -260,34 +243,6 @@
}
}
- /**
- * Sorts primary accounts and google account types before others.
- */
- private static final class EditorComparator implements Comparator<KindSectionData> {
-
- private RawContactDeltaComparator mRawContactDeltaComparator;
-
- private EditorComparator(Context context) {
- mRawContactDeltaComparator = new RawContactDeltaComparator(context);
- }
-
- @Override
- public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
- if (kindSectionData1 == kindSectionData2) return 0;
- if (kindSectionData1 == null) return -1;
- if (kindSectionData2 == null) return 1;
-
- final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
- final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
-
- if (rawContactDelta1 == rawContactDelta2) return 0;
- if (rawContactDelta1 == null) return -1;
- if (rawContactDelta2 == null) return 1;
-
- return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
- }
- }
-
public static class SavedState extends BaseSavedState {
public static final Parcelable.Creator<SavedState> CREATOR =
@@ -326,7 +281,7 @@
private ViewIdGenerator mViewIdGenerator;
private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
- private long mPhotoId = -1;
+ private long mAggregatePhotoId = -1;
private boolean mHasNewContact;
private boolean mIsUserProfile;
private AccountWithDataSet mPrimaryAccount;
@@ -334,7 +289,9 @@
private long mRawContactIdToDisplayAlone = -1;
private boolean mRawContactDisplayAloneIsReadOnly;
private boolean mIsEditingReadOnlyRawContactWithNewContact;
- private Map<String,KindSectionDataList> mKindSectionDataMap = new HashMap<>();
+ private KindSectionDataList mPhotoKindSectionDataList = new KindSectionDataList();
+ private Map<String, KindSectionData> mKindSectionDataMap = new HashMap<>();
+ private Set<String> mSortedMimetypes = new TreeSet<>(new MimeTypeComparator());
// Account header
private View mAccountHeaderContainer;
@@ -349,7 +306,7 @@
private CompactPhotoEditorView mPhotoView;
private ViewGroup mKindSectionViews;
- private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
+ private Map<String, CompactKindSectionView> mKindSectionViewMap = new HashMap<>();
private View mMoreFields;
private boolean mIsExpanded;
@@ -357,8 +314,6 @@
private long mPhotoRawContactId;
private ValuesDelta mPhotoValuesDelta;
- private Pair<KindSectionData, ValuesDelta> mPrimaryNameKindSectionData;
-
public CompactRawContactsEditorView(Context context) {
super(context);
}
@@ -486,11 +441,11 @@
}
private void unsetSuperPrimaryFromAllPhotos() {
- final List<KindSectionData> kindSectionDataList =
- mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
- for (KindSectionData kindSectionData : kindSectionDataList) {
- for (ValuesDelta valuesDelta : kindSectionData.getNonEmptyValuesDeltas()) {
- valuesDelta.setSuperPrimary(false);
+ for (int i = 0; i < mPhotoKindSectionDataList.size(); i++) {
+ final KindSectionData kindSectionData = mPhotoKindSectionDataList.get(0);
+ final List<ValuesDelta> valuesDeltas = kindSectionData.getNonEmptyValuesDeltas();
+ for (int j = 0; j < valuesDeltas.size(); j++) {
+ valuesDeltas.get(j).setSuperPrimary(false);
}
}
}
@@ -524,10 +479,8 @@
final Bundle updatedPhotos = mListener == null ? null : mListener.getUpdatedPhotos();
- final List<KindSectionData> kindSectionDataList =
- mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
- for (int i = 0; i < kindSectionDataList.size(); i++) {
- final KindSectionData kindSectionData = kindSectionDataList.get(i);
+ for (int i = 0; i < mPhotoKindSectionDataList.size(); i++) {
+ final KindSectionData kindSectionData = mPhotoKindSectionDataList.get(i);
final AccountType accountType = kindSectionData.getAccountType();
final List<ValuesDelta> valuesDeltas = kindSectionData.getNonEmptyValuesDeltas();
if (valuesDeltas.isEmpty()) continue;
@@ -570,15 +523,13 @@
*/
public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
// Find the values delta to mark as primary
- final KindSectionDataList kindSectionDataList =
- mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
if (photo.kindSectionDataListIndex < 0
- || photo.kindSectionDataListIndex >= kindSectionDataList.size()) {
+ || photo.kindSectionDataListIndex >= mPhotoKindSectionDataList.size()) {
wlog("Invalid kind section data list index");
return;
}
final KindSectionData kindSectionData =
- kindSectionDataList.get(photo.kindSectionDataListIndex);
+ mPhotoKindSectionDataList.get(photo.kindSectionDataListIndex);
final List<ValuesDelta> valuesDeltaList = kindSectionData.getNonEmptyValuesDeltas();
if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
wlog("Invalid values delta list index");
@@ -596,23 +547,20 @@
}
public View getAggregationAnchorView() {
- final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
- StructuredName.CONTENT_ITEM_TYPE);
- if (!kindSectionViews.isEmpty()) {
- return mKindSectionViews.getChildAt(0).findViewById(R.id.anchor_view);
- }
- return null;
+ CompactKindSectionView nameView = mKindSectionViewMap.get(StructuredName.CONTENT_ITEM_TYPE);
+ return nameView != null ? nameView.getChildAt(0).findViewById(R.id.anchor_view) : null;
}
public void setGroupMetaData(Cursor groupMetaData) {
- final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
- GroupMembership.CONTENT_ITEM_TYPE);
- for (CompactKindSectionView kindSectionView : kindSectionViews) {
- kindSectionView.setGroupMetaData(groupMetaData);
- if (mIsExpanded) {
- kindSectionView.setHideWhenEmpty(false);
- kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
- }
+ final CompactKindSectionView groupKindSectionView =
+ mKindSectionViewMap.get(GroupMembership.CONTENT_ITEM_TYPE);
+ if (groupKindSectionView == null) {
+ return;
+ }
+ groupKindSectionView.setGroupMetaData(groupMetaData);
+ if (mIsExpanded) {
+ groupKindSectionView.setHideWhenEmpty(false);
+ groupKindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
}
}
@@ -629,13 +577,13 @@
mIsEditingReadOnlyRawContactWithNewContact = isEditingReadOnlyRawContactWithNewContact;
mKindSectionDataMap.clear();
- mKindSectionViewsMap.clear();
+ mKindSectionViewMap.clear();
mKindSectionViews.removeAllViews();
mMoreFields.setVisibility(View.VISIBLE);
mMaterialPalette = materialPalette;
mViewIdGenerator = viewIdGenerator;
- mPhotoId = photoId;
+ mAggregatePhotoId = photoId;
mHasNewContact = hasNewContact;
mIsUserProfile = isUserProfile;
@@ -658,15 +606,12 @@
return;
}
-
- // Get the primary name kind section data
- mPrimaryNameKindSectionData =
- getOrCreateKindSectionDataList(StructuredName.CONTENT_ITEM_TYPE)
- .getEntryToWrite(/* id =*/ -1, mPrimaryAccount, mIsUserProfile);
- if (mPrimaryNameKindSectionData != null) {
- // Ensure that a structured name and photo exists
+ final KindSectionData nameSectionData =
+ mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
+ // Ensure that a structured name and photo exists
+ if (nameSectionData != null) {
final RawContactDelta rawContactDelta =
- mPrimaryNameKindSectionData.first.getRawContactDelta();
+ nameSectionData.getRawContactDelta();
RawContactModifier.ensureKindExists(
rawContactDelta,
rawContactDelta.getAccountType(mAccountTypeManager),
@@ -700,7 +645,7 @@
// TODO: Don't render any input fields. Eventually we will show a list of account
// types and names but for now just show the account selector and hide the "More fields"
// link.
- addAccountInfo(rawContactDeltas);
+ addAccountInfo();
mMoreFields.setVisibility(View.GONE);
} else {
setupCompactEditorNormally();
@@ -709,7 +654,7 @@
}
private void setupCompactEditorNormally() {
- addAccountInfo(mRawContactDeltas);
+ addAccountInfo();
addKindSectionViews();
mMoreFields.setVisibility(hasMoreFields() ? View.VISIBLE : View.GONE);
@@ -750,11 +695,45 @@
continue;
}
- final KindSectionDataList kindSectionDataList =
- getOrCreateKindSectionDataList(mimeType);
+ // Skip custom fields
+ // TODO: Handle them when we implement editing custom fields.
+ if (CustomDataItem.MIMETYPE_CUSTOM_FIELD.equals(mimeType)) {
+ vlog("parse: " + i + " " + dataKind.mimeType + " dropped custom field");
+ continue;
+ }
+
+ // Add all photo data.
+ if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final KindSectionData photoKindSectionData =
+ new KindSectionData(accountType, dataKind, rawContactDelta);
+ mPhotoKindSectionDataList.add(photoKindSectionData);
+ vlog("parse: " + i + " " + dataKind.mimeType + " " +
+ photoKindSectionData.getValuesDeltas().size() + " value(s) " +
+ photoKindSectionData.getNonEmptyValuesDeltas().size() +
+ " non-empty value(s) " +
+ photoKindSectionData.getVisibleValuesDeltas().size() +
+ " visible value(s)");
+ continue;
+ }
+
+ // Skip the non-writable names when we're auto creating a new writable contact.
+ if (mIsEditingReadOnlyRawContactWithNewContact
+ && !accountType.areContactsWritable()
+ && StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ vlog("parse: " + i + " " + dataKind.mimeType + " dropped non-writable name");
+ continue;
+ }
+
+ // Skip non-photo data that doesn't belong to the single raw contact we're editing.
+ if (mRawContactIdToDisplayAlone > 0 &&
+ !rawContactDelta.getRawContactId().equals(mRawContactIdToDisplayAlone)) {
+ continue;
+ }
+
final KindSectionData kindSectionData =
new KindSectionData(accountType, dataKind, rawContactDelta);
- kindSectionDataList.add(kindSectionData);
+ mKindSectionDataMap.put(mimeType, kindSectionData);
+ mSortedMimetypes.add(mimeType);
vlog("parse: " + i + " " + dataKind.mimeType + " " +
kindSectionData.getValuesDeltas().size() + " value(s) " +
@@ -765,43 +744,161 @@
}
}
- private KindSectionDataList getOrCreateKindSectionDataList(String mimeType) {
- KindSectionDataList kindSectionDataList = mKindSectionDataMap.get(mimeType);
- if (kindSectionDataList == null) {
- kindSectionDataList = new KindSectionDataList();
- mKindSectionDataMap.put(mimeType, kindSectionDataList);
- }
- return kindSectionDataList;
- }
-
private void addReadOnlyRawContactEditorViews() {
- final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
+ mKindSectionViews.removeAllViews();
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
getContext());
+ final RawContactDelta state = mRawContactDeltas
+ .getByRawContactId(mRawContactIdToDisplayAlone);
+ final AccountType type = state.getAccountType(accountTypes);
- for (int i = 0; i < mRawContactDeltas.size(); i++) {
- final RawContactDelta rawContactDelta = mRawContactDeltas.get(i);
- if (!rawContactDelta.isVisible()) continue;
- final AccountType type = rawContactDelta.getAccountType(accountTypes);
- if (type.areContactsWritable()) continue;
+ // Bail if invalid state or source
+ if (state == null || type == null) return;
- final BaseRawContactEditorView editor = (BaseRawContactEditorView) inflater.inflate(
- R.layout.raw_contact_readonly_editor_view, mKindSectionViews, false);
- editor.setCollapsed(false);
- mKindSectionViews.addView(editor);
- editor.setState(rawContactDelta, type, mViewIdGenerator, mIsUserProfile);
+ // Make sure we have StructuredName
+ RawContactModifier.ensureKindExists(state, type, StructuredName.CONTENT_ITEM_TYPE);
+
+ final AccountDisplayInfo account = mAccountDisplayInfoFactory
+ .getAccountDisplayInfoFor(state);
+
+ final String accountTypeLabel;
+ final String accountNameLabel;
+ if (mIsUserProfile) {
+ accountTypeLabel = EditorUiUtils.getAccountHeaderLabelForMyProfile(
+ getContext(), account);
+ accountNameLabel = account.getNameLabel().toString();
+ } else {
+ accountTypeLabel = account.getTypeLabel().toString();
+ accountNameLabel = account.getNameLabel().toString();
}
+
+ if (!account.hasDistinctName()) {
+ // Hide this view so the other view will be centered vertically
+ mAccountHeaderName.setVisibility(View.GONE);
+ } else {
+ mAccountHeaderName.setVisibility(View.VISIBLE);
+ mAccountHeaderName.setText(accountNameLabel);
+ }
+ mAccountHeaderType.setText(accountTypeLabel);
+ updateAccountHeaderContentDescription();
+
+ mAccountHeaderIcon.setImageDrawable(state.getRawContactAccountType(getContext())
+ .getDisplayIcon(getContext()));
+
+ ValuesDelta primary;
+
+ // Name
+ final Context context = getContext();
+ final Resources res = context.getResources();
+ primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
+ final String name = primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
+ getContext().getString(R.string.missing_name);
+ final Drawable nameDrawable = context.getDrawable(R.drawable.ic_person_24dp);
+ final String nameContentDescription = res.getString(R.string.header_name_entry);
+ bindData(nameDrawable, nameContentDescription, name, /* type */ null,
+ /* isFirstEntry */ true);
+
+ // Phones
+ final ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
+ final Drawable phoneDrawable = context.getDrawable(R.drawable.ic_phone_24dp);
+ final String phoneContentDescription = res.getString(R.string.header_phone_entry);
+ if (phones != null) {
+ boolean isFirstPhoneBound = true;
+ for (ValuesDelta phone : phones) {
+ final String phoneNumber = phone.getPhoneNumber();
+ if (TextUtils.isEmpty(phoneNumber)) {
+ continue;
+ }
+ final String formattedNumber = PhoneNumberUtilsCompat.formatNumber(
+ phoneNumber, phone.getPhoneNormalizedNumber(),
+ GeoUtil.getCurrentCountryIso(getContext()));
+ CharSequence phoneType = null;
+ if (phone.hasPhoneType()) {
+ phoneType = Phone.getTypeLabel(
+ res, phone.getPhoneType(), phone.getPhoneLabel());
+ }
+ bindData(phoneDrawable, phoneContentDescription, formattedNumber, phoneType,
+ isFirstPhoneBound, true);
+ isFirstPhoneBound = false;
+ }
+ }
+
+ // Emails
+ final ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE);
+ final Drawable emailDrawable = context.getDrawable(R.drawable.ic_email_24dp);
+ final String emailContentDescription = res.getString(R.string.header_email_entry);
+ if (emails != null) {
+ boolean isFirstEmailBound = true;
+ for (ValuesDelta email : emails) {
+ final String emailAddress = email.getEmailData();
+ if (TextUtils.isEmpty(emailAddress)) {
+ continue;
+ }
+ CharSequence emailType = null;
+ if (email.hasEmailType()) {
+ emailType = Email.getTypeLabel(
+ res, email.getEmailType(), email.getEmailLabel());
+ }
+ bindData(emailDrawable, emailContentDescription, emailAddress, emailType,
+ isFirstEmailBound);
+ isFirstEmailBound = false;
+ }
+ }
+
+ mKindSectionViews.setVisibility(mKindSectionViews.getChildCount() > 0 ? VISIBLE : GONE);
+ // Hide the "More fields" link
+ mMoreFields.setVisibility(GONE);
}
- // TODO: we have mRawContactDeltas, we don't need to pass the RawContactDeltaList to this method
- private void addAccountInfo(RawContactDeltaList rawContactDeltas) {
+ protected void updateAccountHeaderContentDescription() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(EditorUiUtils.getAccountInfoContentDescription(
+ mAccountHeaderName.getText(), mAccountHeaderType.getText()));
+ mAccountHeaderContainer.setContentDescription(builder);
+ }
+
+ private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
+ CharSequence type, boolean isFirstEntry) {
+ bindData(icon, iconContentDescription, data, type, isFirstEntry, false);
+ }
+
+ private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
+ CharSequence type, boolean isFirstEntry, boolean forceLTR) {
+ final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ final View field = inflater.inflate(R.layout.item_read_only_field, mKindSectionViews,
+ false);
+ if (isFirstEntry) {
+ final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
+ imageView.setImageDrawable(icon);
+ imageView.setContentDescription(iconContentDescription);
+ } else {
+ final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
+ imageView.setVisibility(View.INVISIBLE);
+ imageView.setContentDescription(null);
+ }
+ final TextView dataView = (TextView) field.findViewById(R.id.data);
+ dataView.setText(data);
+ if (forceLTR) {
+ dataView.setTextDirection(View.TEXT_DIRECTION_LTR);
+ }
+ final TextView typeView = (TextView) field.findViewById(R.id.type);
+ if (!TextUtils.isEmpty(type)) {
+ typeView.setText(type);
+ } else {
+ typeView.setVisibility(View.GONE);
+ }
+ mKindSectionViews.addView(field);
+ }
+
+ private void addAccountInfo() {
mAccountHeaderContainer.setVisibility(View.GONE);
mRawContactContainer.setVisibility(View.GONE);
-
- if (mPrimaryNameKindSectionData == null) return;
+ final KindSectionData nameSectionData =
+ mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
+ if (nameSectionData == null) return;
final RawContactDelta rawContactDelta =
- mPrimaryNameKindSectionData.first.getRawContactDelta();
+ nameSectionData.getRawContactDelta();
final AccountDisplayInfo account =
mAccountDisplayInfoFactory.getAccountDisplayInfoFor(rawContactDelta);
@@ -820,14 +917,14 @@
} else {
addAccountHeader(accountLabel);
}
- } else if (mIsUserProfile || !shouldHideAccountContainer(rawContactDeltas)) {
+ } else if (mIsUserProfile || !shouldHideAccountContainer(mRawContactDeltas)) {
addAccountHeader(accountLabel);
}
// The raw contact selector should only display linked raw contacts that can be edited in
// the full editor (i.e. they are not newly created raw contacts)
final RawContactAccountListAdapter adapter = new RawContactAccountListAdapter(getContext(),
- getRawContactDeltaListForSelector(rawContactDeltas));
+ getRawContactDeltaListForSelector(mRawContactDeltas));
if (adapter.getCount() > 0) {
final String accountsSummary = getResources().getQuantityString(
R.plurals.compact_editor_linked_contacts_selector_title,
@@ -842,7 +939,8 @@
Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
final RawContactDeltaList result = new RawContactDeltaList();
- for (RawContactDelta rawContactDelta : rawContactDeltas) {
+ for (int i = 0; i < rawContactDeltas.size(); i++) {
+ final RawContactDelta rawContactDelta = rawContactDeltas.get(i);
if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
// Only add raw contacts that can be opened in the editor
result.add(rawContactDelta);
@@ -888,9 +986,11 @@
mAccountHeaderType.setText(selectorTitle);
// Set the icon
- if (mPrimaryNameKindSectionData != null) {
+ final KindSectionData nameSectionData =
+ mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
+ if (nameSectionData != null) {
final RawContactDelta rawContactDelta =
- mPrimaryNameKindSectionData.first.getRawContactDelta();
+ nameSectionData.getRawContactDelta();
if (rawContactDelta != null) {
final AccountType accountType =
rawContactDelta.getRawContactAccountType(getContext());
@@ -983,12 +1083,14 @@
}
private void addPhotoView() {
- // Get the kind section data and values delta that we will display in the photo view
- final KindSectionDataList kindSectionDataList =
- getOrCreateKindSectionDataList(Photo.CONTENT_ITEM_TYPE);
- final Pair<KindSectionData,ValuesDelta> photoToDisplay =
- kindSectionDataList.getEntryToDisplay(mPhotoId);
- if (photoToDisplay == null) {
+ // Get the kind section data and values delta that we will display in the photo view. Either
+ // the aggregate photo or the photo from the raw contact that is being edited.
+ final Pair<KindSectionData, ValuesDelta> photo =
+ mPhotoKindSectionDataList.getEntryToDisplay(
+ mRawContactIdToDisplayAlone > 0
+ ? mRawContactIdToDisplayAlone
+ : mAggregatePhotoId);
+ if (photo == null) {
wlog("photo: no kind section data parsed");
mPhotoView.setVisibility(View.GONE);
return;
@@ -997,92 +1099,57 @@
}
// Set the photo view
- mPhotoView.setPhoto(photoToDisplay.second, mMaterialPalette);
+ mPhotoView.setPhoto(photo.second, mMaterialPalette);
- // Find the raw contact ID and values delta that will be written when the photo is edited
- final Pair<KindSectionData, ValuesDelta> photoToWrite = kindSectionDataList.getEntryToWrite(
- mPhotoId, mPrimaryAccount, mIsUserProfile);
- if (photoToWrite == null) {
+ // If we're showing an aggregate photo, set it to read only.
+ if (mRawContactIdToDisplayAlone < 1) {
mPhotoView.setReadOnly(true);
return;
}
mPhotoView.setReadOnly(false);
- mPhotoRawContactId = photoToWrite.first.getRawContactDelta().getRawContactId();
- mPhotoValuesDelta = photoToWrite.second;
+ mPhotoRawContactId = photo.first.getRawContactDelta().getRawContactId();
+ mPhotoValuesDelta = photo.second;
}
private void addKindSectionViews() {
- // Sort the kinds
- final TreeSet<Map.Entry<String,KindSectionDataList>> entries =
- new TreeSet<>(KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR);
- entries.addAll(mKindSectionDataMap.entrySet());
-
- vlog("kind: " + entries.size() + " kindSection(s)");
int i = -1;
- for (Map.Entry<String, KindSectionDataList> entry : entries) {
+
+ for (String mimeType : mSortedMimetypes) {
i++;
-
- final String mimeType = entry.getKey();
-
+ final CompactKindSectionView kindSectionView;
+ // TODO: Since we don't have a primary name kind anymore, refactor and collapse
+ // these two branches and the following code paths.
if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
- if (mPrimaryNameKindSectionData == null) {
+ final KindSectionData nameSectionData = mKindSectionDataMap.get(mimeType);
+ if (nameSectionData == null) {
vlog("kind: " + i + " " + mimeType + " dropped");
continue;
}
vlog("kind: " + i + " " + mimeType + " using first entry only");
- final KindSectionDataList kindSectionDataList = new KindSectionDataList();
- kindSectionDataList.add(mPrimaryNameKindSectionData.first);
- final CompactKindSectionView kindSectionView = inflateKindSectionView(
- mKindSectionViews, kindSectionDataList, mimeType,
- mPrimaryNameKindSectionData.second);
- mKindSectionViews.addView(kindSectionView);
-
- // Keep a pointer to all the KindSectionsViews for each mimeType
- getKindSectionViews(mimeType).add(kindSectionView);
+ kindSectionView = inflateKindSectionView(
+ mKindSectionViews, nameSectionData, mimeType,
+ nameSectionData.getValuesDeltas().get(0));
} else {
- final KindSectionDataList kindSectionDataList = entry.getValue();
+ final KindSectionData kindSectionData = mKindSectionDataMap.get(mimeType);
// Ignore mime types that we've already handled
if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
vlog("kind: " + i + " " + mimeType + " dropped");
continue;
}
-
- // Don't show more than one group editor on the compact editor.
- // Groups will still be editable for each raw contact individually on the full editor.
- if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)
- && kindSectionDataList.size() > 1) {
- vlog("kind: " + i + " " + mimeType + " dropped");
- continue;
- }
-
- if (kindSectionDataList != null && !kindSectionDataList.isEmpty()) {
- vlog("kind: " + i + " " + mimeType + " " + kindSectionDataList.size() +
- " kindSectionData(s)");
-
- final CompactKindSectionView kindSectionView = inflateKindSectionView(
- mKindSectionViews, kindSectionDataList, mimeType,
- /* primaryValueDelta =*/ null);
- mKindSectionViews.addView(kindSectionView);
-
- // Keep a pointer to all the KindSectionsViews for each mimeType
- getKindSectionViews(mimeType).add(kindSectionView);
- }
+ kindSectionView = inflateKindSectionView(
+ mKindSectionViews, kindSectionData, mimeType,
+ /* primaryValueDelta =*/ null);
}
- }
- }
+ mKindSectionViews.addView(kindSectionView);
- private List<CompactKindSectionView> getKindSectionViews(String mimeType) {
- List<CompactKindSectionView> kindSectionViews = mKindSectionViewsMap.get(mimeType);
- if (kindSectionViews == null) {
- kindSectionViews = new ArrayList<>();
- mKindSectionViewsMap.put(mimeType, kindSectionViews);
+ // Keep a pointer to the KindSectionView for each mimeType
+ mKindSectionViewMap.put(mimeType, kindSectionView);
}
- return kindSectionViews;
}
private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
- KindSectionDataList kindSectionDataList, String mimeType,
+ KindSectionData kindSectionData, String mimeType,
ValuesDelta primaryValuesDelta) {
final CompactKindSectionView kindSectionView = (CompactKindSectionView)
mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
@@ -1100,12 +1167,7 @@
// they will be the only types you add new values to initially for new contacts
kindSectionView.setShowOneEmptyEditor(true);
- // Sort non-name editors so they wind up in the order we want
- if (!StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
- Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
- }
-
- kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener,
+ kindSectionView.setState(kindSectionData, mViewIdGenerator, mListener,
primaryValuesDelta);
return kindSectionView;
@@ -1121,10 +1183,7 @@
}
private CompactKindSectionView getPrimaryNameKindSectionView() {
- final List<CompactKindSectionView> kindSectionViews
- = mKindSectionViewsMap.get(StructuredName.CONTENT_ITEM_TYPE);
- return kindSectionViews == null || kindSectionViews.isEmpty()
- ? null : kindSectionViews.get(0);
+ return mKindSectionViewMap.get(StructuredName.CONTENT_ITEM_TYPE);
}
private void showAllFields() {
@@ -1142,11 +1201,9 @@
}
private boolean hasMoreFields() {
- for (List<CompactKindSectionView> sections : mKindSectionViewsMap.values()) {
- for (CompactKindSectionView section : sections) {
- if (section.getVisibility() != View.VISIBLE) {
- return true;
- }
+ for (CompactKindSectionView section : mKindSectionViewMap.values()) {
+ if (section.getVisibility() != View.VISIBLE) {
+ return true;
}
}
return false;
diff --git a/src/com/android/contacts/editor/KindSectionData.java b/src/com/android/contacts/editor/KindSectionData.java
index 7e2899f..03aa667 100644
--- a/src/com/android/contacts/editor/KindSectionData.java
+++ b/src/com/android/contacts/editor/KindSectionData.java
@@ -16,14 +16,14 @@
package com.android.contacts.editor;
+import android.text.TextUtils;
+
import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.ValuesDelta;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountType.EditField;
import com.android.contacts.common.model.dataitem.DataKind;
-import android.text.TextUtils;
-
import java.util.ArrayList;
import java.util.List;
@@ -118,4 +118,8 @@
public RawContactDelta getRawContactDelta() {
return mRawContactDelta;
}
+
+ public String getMimeType() {
+ return mDataKind.mimeType;
+ }
}
diff --git a/src/com/android/contacts/editor/KindSectionView.java b/src/com/android/contacts/editor/KindSectionView.java
deleted file mode 100644
index dddc6aa..0000000
--- a/src/com/android/contacts/editor/KindSectionView.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2009 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.contacts.editor;
-
-import android.content.Context;
-import android.provider.ContactsContract.Data;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.contacts.R;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.model.RawContactModifier;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.dataitem.DataKind;
-import com.android.contacts.editor.Editor.EditorListener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Custom view for an entire section of data as segmented by
- * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a
- * section header and a trigger for adding new {@link Data} rows.
- */
-public class KindSectionView extends LinearLayout implements EditorListener {
-
- public interface Listener {
-
- /**
- * Invoked when any editor that is displayed in this section view is deleted by the user.
- */
- public void onDeleteRequested(Editor editor);
- }
-
- private ViewGroup mEditors;
- private ImageView mIcon;
-
- private DataKind mKind;
- private RawContactDelta mState;
- private boolean mReadOnly;
-
- private ViewIdGenerator mViewIdGenerator;
-
- private LayoutInflater mInflater;
-
- private Listener mListener;
-
- public KindSectionView(Context context) {
- this(context, null);
- }
-
- public KindSectionView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- if (mEditors != null) {
- int childCount = mEditors.getChildCount();
- for (int i = 0; i < childCount; i++) {
- mEditors.getChildAt(i).setEnabled(enabled);
- }
- }
-
- updateEmptyEditors(/* shouldAnimate = */ true);
- }
-
- public boolean isReadOnly() {
- return mReadOnly;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- mEditors = (ViewGroup) findViewById(R.id.kind_editors);
- mIcon = (ImageView) findViewById(R.id.kind_icon);
- }
-
- @Override
- public void onDeleteRequested(Editor editor) {
- if (getEditorCount() == 1) {
- // If there is only 1 editor in the section, then don't allow the user to delete it.
- // Just clear the fields in the editor.
- editor.clearAllFields();
- } else {
- // If there is a listener, let it decide whether to delete the Editor or the entire
- // KindSectionView so that there is no jank from both animations happening in succession.
- if (mListener != null) {
- editor.markDeleted();
- mListener.onDeleteRequested(editor);
- } else {
- editor.deleteEditor();
- }
- }
- }
-
- @Override
- public void onRequest(int request) {
- // If a field has become empty or non-empty, then check if another row
- // can be added dynamically.
- if (request == FIELD_TURNED_EMPTY || request == FIELD_TURNED_NON_EMPTY) {
- updateEmptyEditors(/* shouldAnimate = */ true);
- }
- }
-
- public void setListener(Listener listener) {
- mListener = listener;
- }
-
- public void setState(DataKind kind, RawContactDelta state, boolean readOnly,
- ViewIdGenerator vig) {
- mKind = kind;
- mState = state;
- mReadOnly = readOnly;
- mViewIdGenerator = vig;
-
- setId(mViewIdGenerator.getId(state, kind, null, ViewIdGenerator.NO_VIEW_INDEX));
-
- // TODO: handle resources from remote packages
- final String titleString = (kind.titleRes == -1 || kind.titleRes == 0)
- ? ""
- : getResources().getString(kind.titleRes);
- mIcon.setContentDescription(titleString);
-
- mIcon.setImageDrawable(EditorUiUtils.getMimeTypeDrawable(getContext(), kind.mimeType));
- if (mIcon.getDrawable() == null) {
- mIcon.setContentDescription(null);
- }
-
- rebuildFromState();
- updateEmptyEditors(/* shouldAnimate = */ false);
- }
-
- /**
- * Build editors for all current {@link #mState} rows.
- */
- private void rebuildFromState() {
- // Remove any existing editors
- mEditors.removeAllViews();
-
- // Check if we are displaying anything here
- boolean hasEntries = mState.hasMimeEntries(mKind.mimeType);
-
- if (hasEntries) {
- for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) {
- // Skip entries that aren't visible
- if (!entry.isVisible()) continue;
-
- createEditorView(entry);
- }
- }
- }
-
-
- /**
- * Creates an EditorView for the given entry. This function must be used while constructing
- * the views corresponding to the the object-model. The resulting EditorView is also added
- * to the end of mEditors
- */
- private View createEditorView(ValuesDelta entry) {
- final View view;
- final int layoutResId = EditorUiUtils.getLayoutResourceId(mKind.mimeType);
- try {
- view = mInflater.inflate(layoutResId, mEditors, false);
- } catch (Exception e) {
- throw new RuntimeException(
- "Cannot allocate editor with layout resource ID " +
- layoutResId + " for MIME type " + mKind.mimeType +
- " with error " + e.toString());
- }
- view.setEnabled(isEnabled());
- if (view instanceof Editor) {
- Editor editor = (Editor) view;
- editor.setDeletable(true);
- editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator);
- editor.setEditorListener(this);
- }
- mEditors.addView(view);
- return view;
- }
-
- /**
- * Updates the editors being displayed to the user removing extra empty
- * {@link Editor}s, so there is only max 1 empty {@link Editor} view at a time.
- */
- public void updateEmptyEditors(boolean shouldAnimate) {
-
- final List<View> emptyEditors = getEmptyEditors();
-
- // If there is more than 1 empty editor, then remove it from the list of editors.
- if (emptyEditors.size() > 1) {
- for (final View emptyEditorView : emptyEditors) {
- // If no child {@link View}s are being focused on within this {@link View}, then
- // remove this empty editor. We can assume that at least one empty editor has focus.
- // The only way to get two empty editors is by deleting characters from a non-empty
- // editor, in which case this editor has focus.
- if (emptyEditorView.findFocus() == null) {
- final Editor editor = (Editor) emptyEditorView;
- if (shouldAnimate) {
- editor.deleteEditor();
- } else {
- mEditors.removeView(emptyEditorView);
- }
- }
- }
- } else if (mKind == null) {
- // There is nothing we can do.
- return;
- } else if (isReadOnly()) {
- // We don't show empty editors for read only data kinds.
- return;
- } else if (!RawContactModifier.canInsert(mState, mKind)) {
- // We have already reached the maximum number of editors. Lets not add any more.
- return;
- } else if (emptyEditors.size() == 1) {
- // We have already reached the maximum number of empty editors. Lets not add any more.
- return;
- } else {
- final ValuesDelta values = RawContactModifier.insertChild(mState, mKind);
- final View newField = createEditorView(values);
- if (shouldAnimate) {
- newField.setVisibility(View.GONE);
- EditorAnimator.getInstance().showFieldFooter(newField);
- }
- }
- }
-
- /**
- * Returns a list of empty editor views in this section.
- */
- private List<View> getEmptyEditors() {
- List<View> emptyEditorViews = new ArrayList<View>();
- for (int i = 0; i < mEditors.getChildCount(); i++) {
- View view = mEditors.getChildAt(i);
- if (((Editor) view).isEmpty()) {
- emptyEditorViews.add(view);
- }
- }
- return emptyEditorViews;
- }
-
- public int getEditorCount() {
- return mEditors.getChildCount();
- }
-
- public DataKind getKind() {
- return mKind;
- }
-}
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
deleted file mode 100644
index f69c935..0000000
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2009 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.contacts.editor;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.DisplayPhoto;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-
-import com.android.contacts.R;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageProvider;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.dataitem.DataKind;
-import com.android.contacts.util.ContactPhotoUtils;
-
-/**
- * Simple editor for {@link Photo}.
- */
-public class PhotoEditorView extends LinearLayout implements Editor {
-
- private ImageView mPhotoImageView;
- private Button mChangeButton;
- private RadioButton mPrimaryCheckBox;
-
- private ValuesDelta mEntry;
- private EditorListener mListener;
- private ContactPhotoManager mContactPhotoManager;
-
- private boolean mHasSetPhoto = false;
-
- public PhotoEditorView(Context context) {
- super(context);
- }
-
- public PhotoEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- }
-
- @Override
- public void editNewlyAddedField() {
- // Never called, since the user never adds a new photo-editor;
- // you can only change the picture in an existing editor.
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mContactPhotoManager = ContactPhotoManager.getInstance(getContext());
- mPhotoImageView = (ImageView) findViewById(R.id.photo);
- mPrimaryCheckBox = (RadioButton) findViewById(R.id.primary_checkbox);
- mChangeButton = (Button) findViewById(R.id.change_button);
- mPrimaryCheckBox = (RadioButton) findViewById(R.id.primary_checkbox);
- if (mChangeButton != null) {
- mChangeButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mListener != null) {
- mListener.onRequest(EditorListener.REQUEST_PICK_PHOTO);
- }
- }
- });
- }
- // Turn off own state management. We do this ourselves on rotation.
- mPrimaryCheckBox.setSaveEnabled(false);
- mPrimaryCheckBox.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mListener != null) {
- mListener.onRequest(EditorListener.REQUEST_PICK_PRIMARY_PHOTO);
- }
- }
- });
- }
-
- /** {@inheritDoc} */
- @Override
- public void onFieldChanged(String column, String value) {
- throw new UnsupportedOperationException("Photos don't support direct field changes");
- }
-
- /** {@inheritDoc} */
- @Override
- public void setValues(DataKind kind, ValuesDelta values, RawContactDelta state, boolean readOnly,
- ViewIdGenerator vig) {
- mEntry = values;
-
- setId(vig.getId(state, kind, values, 0));
-
- mPrimaryCheckBox.setChecked(values != null && values.isSuperPrimary());
-
- if (values != null) {
- // Try decoding photo if actual entry
- final byte[] photoBytes = values.getAsByteArray(Photo.PHOTO);
- if (photoBytes != null) {
- final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
- photoBytes.length);
-
- mPhotoImageView.setImageBitmap(photo);
- mHasSetPhoto = true;
- mEntry.setFromTemplate(false);
-
- if (values.getAfter() == null || values.getAfter().get(Photo.PHOTO) == null) {
- // If the user hasn't updated the PHOTO value, then PHOTO_FILE_ID may contain
- // a reference to a larger version of PHOTO that we can bind to the UI.
- // Otherwise, we need to wait for a call to #setFullSizedPhoto() to update
- // our full sized image.
- final Integer photoFileId = values.getAsInteger(Photo.PHOTO_FILE_ID);
- if (photoFileId != null) {
- final Uri photoUri = DisplayPhoto.CONTENT_URI.buildUpon()
- .appendPath(photoFileId.toString()).build();
- setFullSizedPhoto(photoUri);
- }
- }
-
- } else {
- resetDefault();
- }
- } else {
- resetDefault();
- }
- }
-
- /**
- * Whether to display a "Primary photo" RadioButton. This is only needed if there are multiple
- * candidate photos.
- */
- public void setShowPrimary(boolean showPrimaryCheckBox) {
- mPrimaryCheckBox.setVisibility(showPrimaryCheckBox ? View.VISIBLE : View.GONE);
- }
-
- /**
- * Return true if a valid {@link Photo} has been set.
- */
- public boolean hasSetPhoto() {
- return mHasSetPhoto;
- }
-
- /**
- * Assign the given {@link Bitmap} as the new value for the sake of building
- * {@link ValuesDelta}. We may as well bind a thumbnail to the UI while we are at it.
- */
- public void setPhotoEntry(Bitmap photo) {
- if (photo == null) {
- // Clear any existing photo and return
- mEntry.put(Photo.PHOTO, (byte[])null);
- resetDefault();
- return;
- }
-
- final int size = ContactsUtils.getThumbnailSize(getContext());
- final Bitmap scaled = Bitmap.createScaledBitmap(photo, size, size, false);
-
- mPhotoImageView.setImageBitmap(scaled);
- mHasSetPhoto = true;
- mEntry.setFromTemplate(false);
-
- // When the user chooses a new photo mark it as super primary
- mEntry.setSuperPrimary(true);
-
- // Even though high-res photos cannot be saved by passing them via
- // an EntityDeltaList (since they cause the Bundle size limit to be
- // exceeded), we still pass a low-res thumbnail. This simplifies
- // code all over the place, because we don't have to test whether
- // there is a change in EITHER the delta-list OR a changed photo...
- // this way, there is always a change in the delta-list.
- final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled);
- if (compressed != null) {
- mEntry.setPhoto(compressed);
- }
- }
-
- /**
- * Bind the {@param photoUri}'s photo to editor's UI. This doesn't affect {@link ValuesDelta}.
- */
- public void setFullSizedPhoto(Uri photoUri) {
- if (photoUri != null) {
- final DefaultImageProvider fallbackToPreviousImage = new DefaultImageProvider() {
- @Override
- public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
- DefaultImageRequest defaultImageRequest) {
- // Before we finish setting the full sized image, don't change the current
- // image that is set in any way.
- }
- };
- mContactPhotoManager.loadPhoto(mPhotoImageView, photoUri,
- mPhotoImageView.getWidth(), /* darkTheme = */ false, /* isCircular = */ false,
- /* defaultImageRequest = */ null, fallbackToPreviousImage);
- }
- }
-
- /**
- * Set the super primary bit on the photo.
- */
- public void setSuperPrimary(boolean superPrimary) {
- mEntry.put(Photo.IS_SUPER_PRIMARY, superPrimary ? 1 : 0);
- }
-
- protected void resetDefault() {
- // Invalid photo, show default "add photo" place-holder
- mPhotoImageView.setImageDrawable(
- ContactPhotoManager.getDefaultAvatarDrawableForContact(getResources(), false, null));
- mHasSetPhoto = false;
- mEntry.setFromTemplate(true);
- }
-
- /** {@inheritDoc} */
- @Override
- public void setEditorListener(EditorListener listener) {
- mListener = listener;
- }
-
- @Override
- public void setDeletable(boolean deletable) {
- // Photo is not deletable
- }
-
- @Override
- public boolean isEmpty() {
- return !mHasSetPhoto;
- }
-
- @Override
- public void markDeleted() {
- // Photo is not deletable
- }
-
- @Override
- public void deleteEditor() {
- // Photo is not deletable
- }
-
- @Override
- public void clearAllFields() {
- resetDefault();
- }
-
- /**
- * The change drop down menu should be anchored to this view.
- */
- public View getChangeAnchorView() {
- return mChangeButton;
- }
-}
diff --git a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
deleted file mode 100644
index d55403b..0000000
--- a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2009 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.contacts.editor;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.RawContacts;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.contacts.common.model.account.AccountDisplayInfo;
-import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
-import com.android.contacts.R;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.contacts.common.model.RawContactModifier;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.account.AccountType;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.contacts.common.model.dataitem.DataKind;
-
-import java.util.ArrayList;
-
-/**
- * Custom view that displays external contacts in the edit screen.
- */
-public class RawContactReadOnlyEditorView extends BaseRawContactEditorView
- implements OnClickListener {
- private LayoutInflater mInflater;
-
- private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
-
- private TextView mName;
- private Button mEditExternallyButton;
- private ViewGroup mGeneral;
-
- private TextView mAccountHeaderTypeTextView;
- private TextView mAccountHeaderNameTextView;
- private ImageView mAccountIconImageView;
-
- private String mAccountName;
- private String mAccountType;
- private String mDataSet;
- private long mRawContactId = -1;
-
- public RawContactReadOnlyEditorView(Context context) {
- super(context);
- }
-
- public RawContactReadOnlyEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mInflater = (LayoutInflater)getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- mName = (TextView) findViewById(R.id.read_only_name);
- mEditExternallyButton = (Button) findViewById(R.id.button_edit_externally);
- mEditExternallyButton.setOnClickListener(this);
- mGeneral = (ViewGroup)findViewById(R.id.sect_general);
-
- mAccountHeaderTypeTextView = (TextView) findViewById(R.id.account_type);
- mAccountHeaderNameTextView = (TextView) findViewById(R.id.account_name);
- mAccountIconImageView = (ImageView) findViewById(R.id.account_type_icon);
-
- mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forAllAccounts(getContext());
- }
-
- /**
- * Set the internal state for this view, given a current
- * {@link RawContactDelta} state and the {@link AccountType} that
- * apply to that state.
- */
- @Override
- public void setState(RawContactDelta state, AccountType type, ViewIdGenerator vig,
- boolean isProfile) {
- // Remove any existing sections
- mGeneral.removeAllViews();
-
- // Bail if invalid state or source
- if (state == null || type == null) return;
-
- // Make sure we have StructuredName
- RawContactModifier.ensureKindExists(state, type, StructuredName.CONTENT_ITEM_TYPE);
-
- // Fill in the header info
- mAccountName = state.getAccountName();
- mAccountType = state.getAccountType();
- mDataSet = state.getDataSet();
-
-
- final AccountDisplayInfo account = mAccountDisplayInfoFactory
- .getAccountDisplayInfoFor(state);
-
- final String accountTypeLabel;
- final String accountNameLabel;
- if (isProfile) {
- accountTypeLabel = EditorUiUtils.getAccountHeaderLabelForMyProfile(
- getContext(), account);
- accountNameLabel = account.getNameLabel().toString();
- } else {
- accountTypeLabel = account.getTypeLabel().toString();
- accountNameLabel = account.getNameLabel().toString();
- }
-
- if (!account.hasDistinctName()) {
- // Hide this view so the other view will be centered vertically
- mAccountHeaderNameTextView.setVisibility(View.GONE);
- } else {
- mAccountHeaderNameTextView.setVisibility(View.VISIBLE);
- mAccountHeaderNameTextView.setText(accountNameLabel);
- }
- mAccountHeaderTypeTextView.setText(accountTypeLabel);
- updateAccountHeaderContentDescription();
-
- mAccountIconImageView.setImageDrawable(state.getRawContactAccountType(getContext())
- .getDisplayIcon(getContext()));
-
- // TODO: Expose data set in the UI somehow?
-
- mRawContactId = state.getRawContactId();
-
- ValuesDelta primary;
-
- // Photo
- DataKind kind = type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
- if (kind != null) {
- RawContactModifier.ensureKindExists(state, type, Photo.CONTENT_ITEM_TYPE);
- boolean hasPhotoEditor = type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null;
- setHasPhotoEditor(hasPhotoEditor);
- primary = state.getPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
- getPhotoEditor().setValues(kind, primary, state, !type.areContactsWritable(), vig);
- }
-
- // Name
- primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
- mName.setText(primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
- getContext().getString(R.string.missing_name));
-
- if (type.getEditContactActivityClassName() != null) {
- mEditExternallyButton.setVisibility(View.VISIBLE);
- } else {
- mEditExternallyButton.setVisibility(View.GONE);
- }
-
- final Resources res = getContext().getResources();
- // Phones
- final ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
- final Drawable phoneDrawable = getResources().getDrawable(R.drawable.ic_phone_24dp);
- final String phoneContentDescription = res.getString(R.string.header_phone_entry);
- if (phones != null) {
- boolean isFirstPhoneBound = true;
- for (ValuesDelta phone : phones) {
- final String phoneNumber = phone.getPhoneNumber();
- if (TextUtils.isEmpty(phoneNumber)) {
- continue;
- }
- final String formattedNumber = PhoneNumberUtilsCompat.formatNumber(
- phoneNumber, phone.getPhoneNormalizedNumber(),
- GeoUtil.getCurrentCountryIso(getContext()));
- CharSequence phoneType = null;
- if (phone.hasPhoneType()) {
- phoneType = Phone.getTypeLabel(
- res, phone.getPhoneType(), phone.getPhoneLabel());
- }
- bindData(phoneDrawable, phoneContentDescription, formattedNumber, phoneType,
- isFirstPhoneBound, true);
- isFirstPhoneBound = false;
- }
- }
-
- // Emails
- final ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE);
- final Drawable emailDrawable = getResources().getDrawable(R.drawable.ic_email_24dp);
- final String emailContentDescription = res.getString(R.string.header_email_entry);
- if (emails != null) {
- boolean isFirstEmailBound = true;
- for (ValuesDelta email : emails) {
- final String emailAddress = email.getEmailData();
- if (TextUtils.isEmpty(emailAddress)) {
- continue;
- }
- CharSequence emailType = null;
- if (email.hasEmailType()) {
- emailType = Email.getTypeLabel(
- res, email.getEmailType(), email.getEmailLabel());
- }
- bindData(emailDrawable, emailContentDescription, emailAddress, emailType,
- isFirstEmailBound);
- isFirstEmailBound = false;
- }
- }
-
- // Hide mGeneral if it's empty
- if (mGeneral.getChildCount() > 0) {
- mGeneral.setVisibility(View.VISIBLE);
- } else {
- mGeneral.setVisibility(View.GONE);
- }
- }
-
- private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
- CharSequence type, boolean isFirstEntry) {
- bindData(icon, iconContentDescription, data, type, isFirstEntry, false);
- }
-
- private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
- CharSequence type, boolean isFirstEntry, boolean forceLTR) {
- final View field = mInflater.inflate(R.layout.item_read_only_field, mGeneral, false);
- if (isFirstEntry) {
- final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
- imageView.setImageDrawable(icon);
- imageView.setContentDescription(iconContentDescription);
- } else {
- final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
- imageView.setVisibility(View.INVISIBLE);
- imageView.setContentDescription(null);
- }
- final TextView dataView = (TextView) field.findViewById(R.id.data);
- dataView.setText(data);
- if (forceLTR) {
- dataView.setTextDirection(View.TEXT_DIRECTION_LTR);
- }
- final TextView typeView = (TextView) field.findViewById(R.id.type);
- if (!TextUtils.isEmpty(type)) {
- typeView.setText(type);
- } else {
- typeView.setVisibility(View.GONE);
- }
-
- mGeneral.addView(field);
- }
-
- @Override
- public long getRawContactId() {
- return mRawContactId;
- }
-
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.button_edit_externally) {
- if (mListener != null) {
- mListener.onExternalEditorRequest(
- new AccountWithDataSet(mAccountName, mAccountType, mDataSet),
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactId));
- }
- }
- }
-}
diff --git a/src/com/android/contacts/editor/ViewIdGenerator.java b/src/com/android/contacts/editor/ViewIdGenerator.java
index e7e7948..ad99bf8 100644
--- a/src/com/android/contacts/editor/ViewIdGenerator.java
+++ b/src/com/android/contacts/editor/ViewIdGenerator.java
@@ -25,7 +25,7 @@
import com.android.contacts.common.model.dataitem.DataKind;
/**
- * A class that provides unique view ids for {@link ContentEditorView}, {@link KindSectionView},
+ * A class that provides unique view ids for {@link ContentEditorView},
* {@link LabeledEditorView} and {@link EditView} on {@link EditContactActivity}.
* It is used to assign a unique but consistent id to each view across {@link EditContactActivity}'s
* lifecycle, so that we can re-construct view state (e.g. focused view) when the screen rotates.
diff --git a/src/com/android/contacts/group/GroupMembersFragment.java b/src/com/android/contacts/group/GroupMembersFragment.java
index 66bd509..f7897bc 100644
--- a/src/com/android/contacts/group/GroupMembersFragment.java
+++ b/src/com/android/contacts/group/GroupMembersFragment.java
@@ -318,7 +318,7 @@
final long[] contactIds = getAdapter().getSelectedContactIdsArray();
new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
- mGroupMetaData.accountType).execute();
+ mGroupMetaData.accountType, mGroupMetaData.dataSet).execute();
mActionBarAdapter.setSelectionMode(false);
}
@@ -340,7 +340,7 @@
new UpdateGroupMembersAsyncTask(
UpdateGroupMembersAsyncTask.TYPE_ADD,
getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
- mGroupMetaData.accountType).execute();
+ mGroupMetaData.accountType, mGroupMetaData.dataSet).execute();
}
}
@@ -636,7 +636,7 @@
contactIds[0] = contactId;
new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
- mGroupMetaData.accountType).execute();
+ mGroupMetaData.accountType, mGroupMetaData.dataSet).execute();
}
}
diff --git a/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java b/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java
index 46aa674..9b255eb 100644
--- a/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java
+++ b/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java
@@ -22,6 +22,7 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
import android.widget.Toast;
import com.android.contacts.ContactSaveService;
@@ -42,15 +43,17 @@
private final long mGroupId;
private final String mAccountName;
private final String mAccountType;
+ private final String mDataSet;
public UpdateGroupMembersAsyncTask(int type, Context context, long[] contactIds,
- long groupId, String accountName, String accountType) {
+ long groupId, String accountName, String accountType, String dataSet) {
mContext = context;
mType = type;
mContactIds = contactIds;
mGroupId = groupId;
mAccountName = accountName;
mAccountType = accountType;
+ mDataSet = dataSet;
}
@Override
@@ -81,10 +84,16 @@
// TODO(wjang): prune raw contacts that are already in the group; ContactSaveService will
// log a warning if the raw contact is already a member and keep going but it is not ideal.
private long[] getRawContactIds() {
- final Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon()
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountName)
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountType)
- .build();
+ final Uri.Builder builder = RawContacts.CONTENT_URI.buildUpon();
+ // null account names are not valid, see ContactsProvider2#appendAccountFromParameter
+ if (mAccountName != null) {
+ builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccountName);
+ builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccountType);
+ }
+ if (mDataSet != null) {
+ builder.appendQueryParameter(RawContacts.DATA_SET, mDataSet);
+ }
+ final Uri rawContactUri = builder.build();
final String[] projection = new String[]{ContactsContract.RawContacts._ID};
final StringBuilder selection = new StringBuilder();
final String[] selectionArgs = new String[mContactIds.length];
diff --git a/src/com/android/contacts/interactions/CallLogInteraction.java b/src/com/android/contacts/interactions/CallLogInteraction.java
index 06fd273..9e6b5a2 100644
--- a/src/com/android/contacts/interactions/CallLogInteraction.java
+++ b/src/com/android/contacts/interactions/CallLogInteraction.java
@@ -16,6 +16,8 @@
package com.android.contacts.interactions;
import com.android.contacts.R;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.contacts.common.util.BitmapUtil;
import com.android.contacts.common.util.ContactDisplayUtils;
@@ -66,7 +68,14 @@
@Override
public String getViewHeader(Context context) {
- return getNumber();
+ String number = mValues.getAsString(Calls.NUMBER);
+ if (number != null) {
+ number = PhoneNumberUtilsCompat.formatNumber(number,
+ PhoneNumberUtilsCompat.normalizeNumber(number),
+ GeoUtil.getCurrentCountryIso(context));
+ return sBidiFormatter.unicodeWrap(number, TextDirectionHeuristics.LTR);
+ }
+ return null;
}
@Override
diff --git a/src/com/android/contacts/list/ContactsUnavailableFragment.java b/src/com/android/contacts/list/ContactsUnavailableFragment.java
index aa08a32..c567dd2 100644
--- a/src/com/android/contacts/list/ContactsUnavailableFragment.java
+++ b/src/com/android/contacts/list/ContactsUnavailableFragment.java
@@ -17,9 +17,11 @@
import android.app.Fragment;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.PorterDuff;
import android.os.Bundle;
+import android.provider.ContactsContract.ProviderStatus;
import android.support.v4.content.ContextCompat;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -34,6 +36,8 @@
import com.android.contacts.R;
import com.android.contacts.common.compat.ProviderStatusCompat;
+import com.android.contacts.common.interactions.ImportExportDialogFragment;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
/**
* Fragment shown when contacts are unavailable. It contains provider status
@@ -48,18 +52,10 @@
private Button mImportContactsButton;
private ProgressBar mProgress;
private View mButtonsContainer;
- private int mNoContactsMsgResId = -1;
-
- private OnContactsUnavailableActionListener mListener;
private Integer mProviderStatus;
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.contacts_unavailable_fragment, null);
@@ -99,11 +95,6 @@
return mView;
}
- public void setOnContactsUnavailableActionListener(
- OnContactsUnavailableActionListener listener) {
- mListener = listener;
- }
-
public void updateStatus(int providerStatus) {
mProviderStatus = providerStatus;
if (mView == null) {
@@ -123,6 +114,7 @@
* Update views in the fragment when provider status is empty.
*/
private void updateViewsForEmptyStatus() {
+ updateButtonVisibility(View.VISIBLE);
mProgress.setVisibility(View.GONE);
}
@@ -135,7 +127,7 @@
mMessageView.setText(resId);
mMessageView.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.GONE);
- updateButtonVisibilty(View.GONE);
+ updateButtonVisibility(View.GONE);
mProgress.setVisibility(View.VISIBLE);
final ViewGroup.MarginLayoutParams layoutParams =
@@ -148,20 +140,25 @@
@Override
public void onClick(View v) {
- if (mListener == null) {
- return;
- }
switch (v.getId()) {
case R.id.add_account_button:
- mListener.onAddAccountAction();
+ final Intent intent = ImplicitIntentsUtil.getIntentForAddingGoogleAccount();
+ ImplicitIntentsUtil.startActivityOutsideApp(getActivity(), intent);
break;
case R.id.import_contacts_button:
- mListener.onImportContactsFromFileAction();
+ ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
+ getActivity().getClass(),
+ ImportExportDialogFragment.EXPORT_MODE_ALL_CONTACTS);
break;
}
}
- private void updateButtonVisibilty(int visibility) {
+ private boolean areContactsAvailable() {
+ return (mProviderStatus != null) && mProviderStatus.equals(ProviderStatus.STATUS_NORMAL);
+ }
+
+
+ private void updateButtonVisibility(int visibility) {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
mAddAccountButton.setVisibility(visibility);
mImportContactsButton.setVisibility(visibility);
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index f92e0ab..310d4d9 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -269,10 +269,6 @@
}
}
- public boolean tryRemoveHighlight() {
- return FeatureHighlightHelper.tryRemoveHighlight(mActivity);
- }
-
private void bindListHeader(int numberOfContacts) {
final ContactListFilter filter = getFilter();
// If the phone has at least one Google account whose sync status is unsyncable or pending
diff --git a/src/com/android/contacts/list/OnContactsUnavailableActionListener.java b/src/com/android/contacts/list/OnContactsUnavailableActionListener.java
deleted file mode 100644
index cc381e4..0000000
--- a/src/com/android/contacts/list/OnContactsUnavailableActionListener.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2010 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.contacts.list;
-
-/**
- * Action callbacks that can be sent by the "contacts unavailable" fragment.
- */
-public interface OnContactsUnavailableActionListener {
-
- /**
- * Creates a new contact.
- */
- void onCreateNewContactAction();
-
- /**
- * Initiates addition of a contacts account.
- */
- void onAddAccountAction();
-
- /**
- * Initiates contact import from a file.
- */
- void onImportContactsFromFileAction();
-}
diff --git a/tests/src/com/android/contacts/common/model/DeviceLocalAccountLocatorTests.java b/tests/src/com/android/contacts/common/model/DeviceLocalAccountLocatorTests.java
index f1a2714..e8c4e2f 100644
--- a/tests/src/com/android/contacts/common/model/DeviceLocalAccountLocatorTests.java
+++ b/tests/src/com/android/contacts/common/model/DeviceLocalAccountLocatorTests.java
@@ -140,24 +140,6 @@
assertEquals(2, sut.getDeviceLocalAccounts().size());
}
- public void test_getDeviceLocalAccounts_onlyQueriesRawContactsIfNecessary() {
- final DeviceLocalAccountTypeFactory stubFactory = new FakeDeviceAccountTypeFactory()
- .withDeviceTypes(null, "vnd.sec.contact.phone")
- .withSimTypes("vnd.sec.contact.sim");
- final FakeContactsProvider contactsProvider = new FakeContactsProvider()
- .withQueryResult(ContactsContract.Groups.CONTENT_URI, queryResult(
- "phone_account", "vnd.sec.contact.phone",
- "sim_account", "vnd.sec.contact.sim"
- ));
- final DeviceLocalAccountLocator sut = new DeviceLocalAccountLocator(
- createContentResolverWithProvider(contactsProvider), stubFactory,
- Collections.<AccountWithDataSet>emptyList());
-
- sut.getDeviceLocalAccounts();
-
- assertEquals(0, contactsProvider.getQueryCountFor(RawContacts.CONTENT_URI));
- }
-
private DeviceLocalAccountLocator createWithQueryResult(
Cursor cursor) {
final DeviceLocalAccountLocator locator = new DeviceLocalAccountLocator(
@@ -194,7 +176,6 @@
private static class FakeContactsProvider extends MockContentProvider {
public Cursor mNextQueryResult;
public Map<Uri, Cursor> mNextResultMapping = new HashMap<>();
- public Map<Uri, Integer> mQueryCountMapping = new HashMap<>();
public FakeContactsProvider() {}
@@ -214,17 +195,10 @@
return query(uri, projection, selection, selectionArgs, sortOrder, null);
}
- public int getQueryCountFor(Uri uri) {
- ensureCountInitialized(uri);
- return mQueryCountMapping.get(uri);
- }
-
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder, CancellationSignal cancellationSignal) {
- incrementQueryCount(uri);
-
final Cursor result = mNextResultMapping.get(uri);
if (result == null) {
return mNextQueryResult;
@@ -232,17 +206,5 @@
return result;
}
}
-
- private void ensureCountInitialized(Uri uri) {
- if (!mQueryCountMapping.containsKey(uri)) {
- mQueryCountMapping.put(uri, 0);
- }
- }
-
- private void incrementQueryCount(Uri uri) {
- ensureCountInitialized(uri);
- final int count = mQueryCountMapping.get(uri);
- mQueryCountMapping.put(uri, count + 1);
- }
}
}