Use Toolbar for QC
* Moved star, add and edit buttons into Toolbar
* Fixed implementation of add/edit: copied the
ContractDetailFragment implementation
* Added unimplemented Share and Place on Home menu items
* Deleted some of the quickcontact_activity.xml files
* No longer handle click events on Contact photo view
Change-Id: Id333b11e89d8755ace600700be15077fd47b6172
diff --git a/res/drawable-hdpi/ic_star_24dp.png b/res/drawable-hdpi/ic_star_24dp.png
new file mode 100644
index 0000000..d053adb
--- /dev/null
+++ b/res/drawable-hdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_star_outline_24dp.png b/res/drawable-hdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..809f1ee
--- /dev/null
+++ b/res/drawable-hdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_star_24dp.png b/res/drawable-mdpi/ic_star_24dp.png
new file mode 100644
index 0000000..f68b428
--- /dev/null
+++ b/res/drawable-mdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_star_outline_24dp.png b/res/drawable-mdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..743aaae
--- /dev/null
+++ b/res/drawable-mdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_star_24dp.png b/res/drawable-xhdpi/ic_star_24dp.png
new file mode 100644
index 0000000..49faab8
--- /dev/null
+++ b/res/drawable-xhdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_star_outline_24dp.png b/res/drawable-xhdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..0ce8d31
--- /dev/null
+++ b/res/drawable-xhdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_star_24dp.png b/res/drawable-xxhdpi/ic_star_24dp.png
new file mode 100644
index 0000000..0aa8a26
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_star_outline_24dp.png b/res/drawable-xxhdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..1f9a7a2
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_star_24dp.png b/res/drawable-xxxhdpi/ic_star_24dp.png
new file mode 100644
index 0000000..7477c14
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_star_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_star_outline_24dp.png b/res/drawable-xxxhdpi/ic_star_outline_24dp.png
new file mode 100644
index 0000000..3ae24c7
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_star_outline_24dp.png
Binary files differ
diff --git a/res/layout-land/quickcontact_activity.xml b/res/layout-land/quickcontact_activity.xml
index 497e3dd..552f568 100644
--- a/res/layout-land/quickcontact_activity.xml
+++ b/res/layout-land/quickcontact_activity.xml
@@ -27,7 +27,23 @@
android:layout_height="match_parent"
ex:ratio="1.0"
ex:direction="heightToWidth">
- <include layout="@layout/quickcontact_photo_container" />
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"
+ android:contentDescription="@string/description_contact_photo" />
+ <!-- Need to set a non null background on Toolbar in order for MenuItem
+ ripples to be drawn on this view, instead of another-->
+ <Toolbar
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:background="#00000000"
+ android:id="@+id/toolbar"/>
+ </FrameLayout>
</view>
<com.android.contacts.quickcontact.ExpandingEntryCardView
style="@style/ExpandingEntryCardStyle"
diff --git a/res/layout-sw600dp-land/quickcontact_activity.xml b/res/layout-sw600dp-land/quickcontact_activity.xml
deleted file mode 100644
index 239c50c..0000000
--- a/res/layout-sw600dp-land/quickcontact_activity.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="32dip"
- android:orientation="horizontal">
- <FrameLayout
- android:layout_width="360dip"
- android:layout_height="360dip">
- <include layout="@layout/quickcontact_photo_container" />
- </FrameLayout>
- <com.android.contacts.quickcontact.ExpandingEntryCardView
- style="@style/ExpandingEntryCardStyle"
- android:id="@+id/communication_card"
- android:layout_marginTop="@dimen/communication_card_marginTop"
- android:visibility="gone" />
- <com.android.contacts.quickcontact.ExpandingEntryCardView
- style="@style/ExpandingEntryCardStyle"
- android:id="@+id/recent_card"
- android:visibility="gone" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw600dp/quickcontact_activity.xml b/res/layout-sw600dp/quickcontact_activity.xml
deleted file mode 100644
index 6c275b1..0000000
--- a/res/layout-sw600dp/quickcontact_activity.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="32dip"
- android:orientation="vertical" >
- <FrameLayout
- android:layout_width="360dip"
- android:layout_height="@dimen/quick_contact_photo_container_height">
- <include layout="@layout/quickcontact_photo_container" />
- </FrameLayout>
- <com.android.contacts.quickcontact.ExpandingEntryCardView
- style="@style/ExpandingEntryCardStyle"
- android:id="@+id/communication_card"
- android:layout_marginTop="@dimen/communication_card_marginTop"
- android:visibility="gone" />
- <com.android.contacts.quickcontact.ExpandingEntryCardView
- style="@style/ExpandingEntryCardStyle"
- android:id="@+id/recent_card"
- android:visibility="gone" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw720dp-land/quickcontact_activity.xml b/res/layout-sw720dp-land/quickcontact_activity.xml
deleted file mode 100644
index 239c50c..0000000
--- a/res/layout-sw720dp-land/quickcontact_activity.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="32dip"
- android:orientation="horizontal">
- <FrameLayout
- android:layout_width="360dip"
- android:layout_height="360dip">
- <include layout="@layout/quickcontact_photo_container" />
- </FrameLayout>
- <com.android.contacts.quickcontact.ExpandingEntryCardView
- style="@style/ExpandingEntryCardStyle"
- android:id="@+id/communication_card"
- android:layout_marginTop="@dimen/communication_card_marginTop"
- android:visibility="gone" />
- <com.android.contacts.quickcontact.ExpandingEntryCardView
- style="@style/ExpandingEntryCardStyle"
- android:id="@+id/recent_card"
- android:visibility="gone" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/quickcontact_activity.xml b/res/layout/quickcontact_activity.xml
index 9af1079..a7c12dd 100644
--- a/res/layout/quickcontact_activity.xml
+++ b/res/layout/quickcontact_activity.xml
@@ -24,14 +24,28 @@
android:focusableInTouchMode="true"
android:descendantFocusability="afterDescendants" >
- <!-- Will contain ToolBar and image behind ToolBar -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/quickcontact_maximum_header_height"
android:layout_marginTop="@dimen/quickcontact_starting_empty_height"
android:background="@color/card_margin_color"
android:id="@+id/toolbar_parent">
- <include layout="@layout/quickcontact_photo_container" />
+
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"
+ android:contentDescription="@string/description_contact_photo" />
+
+ <!-- Need to set a non null background on Toolbar in order for MenuItem
+ ripples to be drawn on this view, instead of another-->
+ <Toolbar
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:background="#00000000"
+ android:id="@+id/toolbar"/>
+
</FrameLayout>
<com.android.contacts.widget.TouchlessScrollView
diff --git a/res/layout/quickcontact_photo_container.xml b/res/layout/quickcontact_photo_container.xml
deleted file mode 100644
index 5685427..0000000
--- a/res/layout/quickcontact_photo_container.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<merge
- xmlns:android="http://schemas.android.com/apk/res/android">
- <RelativeLayout
- android:id="@+id/photo_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_vertical">
- <ImageView
- android:id="@+id/photo"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:clickable="true"
- android:contentDescription="@string/description_contact_photo" />
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_alignParentTop="true"
- android:background="#4CFFFFFF" />
- <View
- android:id="@+id/photo_text_bar"
- android:layout_width="0dip"
- android:layout_height="42dip"
- android:layout_alignBottom="@id/photo"
- android:layout_alignLeft="@id/photo"
- android:layout_alignRight="@id/photo"
- android:layout_alignStart="@id/photo"
- android:layout_alignEnd="@id/photo"
- android:background="@color/quickcontact_name_detail_background" />
- <ImageView
- android:id="@+id/contact_edit_image"
- android:src="@drawable/ic_create_24dp"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginRight="16dip"
- android:layout_marginEnd="16dip"
- android:layout_marginBottom="5dip"
- android:layout_alignBottom="@id/photo_text_bar"
- android:layout_alignRight="@id/photo_text_bar"
- android:layout_alignEnd="@id/photo_text_bar"
- android:clickable="true"
- android:contentDescription="@string/editContactDescription" />
- <ImageView
- android:id="@+id/quickcontact_star_button"
- android:src="@drawable/ic_favorite_off_lt"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginBottom="5dip"
- android:layout_marginRight="16dip"
- android:layout_marginEnd="16dip"
- android:layout_alignBottom="@id/photo_text_bar"
- android:layout_toLeftOf="@id/contact_edit_image"
- android:layout_toStartOf="@id/contact_edit_image"
- android:clickable="true"
- android:contentDescription="@string/menu_addStar" />
- <TextView
- android:id="@+id/name"
- android:layout_width="match_parent"
- android:layout_height="42dip"
- android:layout_alignBottom="@id/photo"
- android:layout_alignLeft="@id/photo"
- android:layout_alignStart="@id/photo"
- android:layout_toLeftOf="@id/quickcontact_star_button"
- android:layout_toStartOf="@id/quickcontact_star_button"
- android:gravity="center_vertical"
- android:paddingLeft="8dip"
- android:paddingStart="8dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@android:color/white"
- android:textAppearance="?android:attr/textAppearanceMedium" />
- </RelativeLayout>
-</merge>
diff --git a/res/menu/quickcontact.xml b/res/menu/quickcontact.xml
new file mode 100644
index 0000000..34a6a10
--- /dev/null
+++ b/res/menu/quickcontact.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_star"
+ android:showAsAction="always" />
+
+ <item
+ android:id="@+id/menu_edit"
+ android:icon="@drawable/ic_create_24dp"
+ android:showAsAction="always" />
+
+ <item
+ android:id="@+id/menu_share"
+ android:title="@string/menu_share"
+ android:alphabeticShortcut="s" />
+
+ <item
+ android:id="@+id/menu_create_contact_shortcut"
+ android:title="@string/menu_create_contact_shortcut" />
+</menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6be304b..c1bd3c0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -658,6 +658,10 @@
<string name="timestamp_string_yesterday">Yesterday</string>
<!-- Timestamp string for interactions from tomorrow. [CHAR LIMIT=40] -->
<string name="timestamp_string_tomorrow">Tomorrow</string>
+
<!-- Title of sms action entry. [CHAR LIMIT=60] -->
<string name="send_message">Send message</string>
+
+ <!-- Toast that appears when you are copying a directory contact into your personal contacts -->
+ <string name="toast_making_personal_copy">Creating a personal copy...</string>
</resources>
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 48aa0f7..67d14e2 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -176,8 +176,8 @@
if (!isDirectoryEntry && !isUserProfile) {
starredMenuItem.setVisible(true);
final int resId = isStarred
- ? R.drawable.btn_star_on_normal_holo_light
- : R.drawable.btn_star_off_normal_holo_light;
+ ? R.drawable.ic_star_24dp
+ : R.drawable.ic_star_outline_24dp;
starredMenuItem.setIcon(resId);
starredMenuItem.setChecked(isStarred);
starredMenuItem.setTitle(isStarred ? R.string.menu_removeStar : R.string.menu_addStar);
diff --git a/src/com/android/contacts/quickcontact/DirectoryContactUtil.java b/src/com/android/contacts/quickcontact/DirectoryContactUtil.java
new file mode 100644
index 0000000..d483af3
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/DirectoryContactUtil.java
@@ -0,0 +1,83 @@
+package com.android.contacts.quickcontact;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+import com.android.contacts.common.editor.SelectAccountDialogFragment;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.Contact;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
+import com.android.contacts.quickcontact.QuickContactActivity.SelectAccountDialogFragmentListener;
+
+import android.app.FragmentManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.ContactsContract.Directory;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class to support adding directory contacts.
+ *
+ * This class is coupled with {@link QuickContactActivity}, but is left out of
+ * QuickContactActivity.java to avoid ballooning the size of the file.
+ */
+public class DirectoryContactUtil {
+
+ public static boolean isDirectoryContact(Contact contactData) {
+ // Not a directory contact? Nothing to fix here
+ if (contactData == null || !contactData.isDirectoryEntry()) return false;
+
+ // No export support? Too bad
+ return contactData.getDirectoryExportSupport() != Directory.EXPORT_SUPPORT_NONE;
+ }
+
+ public static void addToMyContacts(Contact contactData, Context context,
+ FragmentManager fragmentManager,
+ SelectAccountDialogFragmentListener selectAccountCallbacks) {
+ int exportSupport = contactData.getDirectoryExportSupport();
+ switch (exportSupport) {
+ case Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY: {
+ createCopy(contactData.getContentValues(),
+ new AccountWithDataSet(contactData.getDirectoryAccountName(),
+ contactData.getDirectoryAccountType(), null),
+ context);
+ break;
+ }
+ case Directory.EXPORT_SUPPORT_ANY_ACCOUNT: {
+ final List<AccountWithDataSet> accounts =
+ AccountTypeManager.getInstance(context).getAccounts(true);
+ if (accounts.isEmpty()) {
+ createCopy(contactData.getContentValues(), null, context);
+ return; // Don't show a dialog.
+ }
+
+ // In the common case of a single writable account, auto-select
+ // it without showing a dialog.
+ if (accounts.size() == 1) {
+ createCopy(contactData.getContentValues(), accounts.get(0), context);
+ return; // Don't show a dialog.
+ }
+
+ SelectAccountDialogFragment.show(fragmentManager,
+ selectAccountCallbacks, R.string.dialog_new_contact_account,
+ AccountListFilter.ACCOUNTS_CONTACT_WRITABLE, null);
+ break;
+ }
+ }
+ }
+
+ public static void createCopy(
+ ArrayList<ContentValues> values, AccountWithDataSet account,
+ Context context) {
+ Toast.makeText(context, R.string.toast_making_personal_copy,
+ Toast.LENGTH_LONG).show();
+ Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
+ context, values, account,
+ QuickContactActivity.class, Intent.ACTION_VIEW);
+ context.startService(serviceIntent);
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/InvisibleContactUtil.java b/src/com/android/contacts/quickcontact/InvisibleContactUtil.java
new file mode 100644
index 0000000..3609fbc
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/InvisibleContactUtil.java
@@ -0,0 +1,113 @@
+package com.android.contacts.quickcontact;
+
+
+import com.google.common.collect.Iterables;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.common.GroupMetaData;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.Contact;
+import com.android.contacts.common.model.RawContact;
+import com.android.contacts.common.model.RawContactDelta;
+import com.android.contacts.common.model.RawContactDeltaList;
+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.DataItem;
+import com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.model.dataitem.GroupMembershipDataItem;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+
+import java.util.List;
+
+/**
+ * Utility class to support adding invisible contacts. Ie, contacts that don't belong to the
+ * default group.
+ */
+public class InvisibleContactUtil {
+
+ public static boolean isInvisibleAndAddable(Contact contactData, Context context) {
+ // Only local contacts
+ if (contactData == null || contactData.isDirectoryEntry()) return false;
+
+ // User profile cannot be added to contacts
+ if (contactData.isUserProfile()) return false;
+
+ // Only if exactly one raw contact
+ if (contactData.getRawContacts().size() != 1) return false;
+
+ // test if the default group is assigned
+ final List<GroupMetaData> groups = contactData.getGroupMetaData();
+
+ // For accounts without group support, groups is null
+ if (groups == null) return false;
+
+ // remember the default group id. no default group? bail out early
+ final long defaultGroupId = getDefaultGroupId(groups);
+ if (defaultGroupId == -1) return false;
+
+ final RawContact rawContact = (RawContact) contactData.getRawContacts().get(0);
+ final AccountType type = rawContact.getAccountType(context);
+ // Offline or non-writeable account? Nothing to fix
+ if (type == null || !type.areContactsWritable()) return false;
+
+ // Check whether the contact is in the default group
+ boolean isInDefaultGroup = false;
+ for (DataItem dataItem : Iterables.filter(
+ rawContact.getDataItems(), GroupMembershipDataItem.class)) {
+ GroupMembershipDataItem groupMembership = (GroupMembershipDataItem) dataItem;
+ final Long groupId = groupMembership.getGroupRowId();
+ if (groupId != null && groupId == defaultGroupId) {
+ isInDefaultGroup = true;
+ break;
+ }
+ }
+
+ return !isInDefaultGroup;
+ }
+
+ public static void addToDefaultGroup(Contact contactData, Context context) {
+ final long defaultGroupId = getDefaultGroupId(contactData.getGroupMetaData());
+ // there should always be a default group (otherwise the button would be invisible),
+ // but let's be safe here
+ if (defaultGroupId == -1) return;
+
+ // add the group membership to the current state
+ final RawContactDeltaList contactDeltaList = contactData.createRawContactDeltaList();
+ final RawContactDelta rawContactEntityDelta = contactDeltaList.get(0);
+
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
+ context);
+ final AccountType type = rawContactEntityDelta.getAccountType(accountTypes);
+ final DataKind groupMembershipKind = type.getKindForMimetype(
+ GroupMembership.CONTENT_ITEM_TYPE);
+ final ValuesDelta entry = RawContactModifier.insertChild(rawContactEntityDelta,
+ groupMembershipKind);
+ if (entry == null) return;
+ entry.setGroupRowId(defaultGroupId);
+
+ // and fire off the intent. we don't need a callback, as the database listener
+ // should update the ui
+ final Intent intent = ContactSaveService.createSaveContactIntent(
+ context,
+ contactDeltaList, "", 0, false, QuickContactActivity.class,
+ Intent.ACTION_VIEW, null);
+ context.startService(intent);
+ }
+
+ /** return default group id or -1 if no group or several groups are marked as default */
+ private static long getDefaultGroupId(List<GroupMetaData> groups) {
+ long defaultGroupId = -1;
+ for (GroupMetaData group : groups) {
+ if (group.isDefaultGroup()) {
+ // two default groups? return neither
+ if (defaultGroupId != -1) return -1;
+ defaultGroupId = group.getGroupId();
+ }
+ }
+ return defaultGroupId;
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index b672d93..1e703a0 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -19,9 +19,9 @@
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.app.Activity;
+import android.app.Fragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ContentUris;
-import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.content.pm.PackageManager;
@@ -42,38 +42,40 @@
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.provider.ContactsContract.Intents.Insert;
-import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsContract.RawContacts;
import android.support.v7.graphics.Palette;
import android.text.TextUtils;
import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.ImageView;
-import android.widget.TextView;
import android.widget.Toast;
+import android.widget.Toolbar;
import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.common.Collapser;
import com.android.contacts.R;
+import com.android.contacts.common.editor.SelectAccountDialogFragment;
import com.android.contacts.common.lettertiles.LetterTileDrawable;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.Contact;
import com.android.contacts.common.model.ContactLoader;
import com.android.contacts.common.model.RawContact;
import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.model.dataitem.DataItem;
import com.android.contacts.common.model.dataitem.DataKind;
import com.android.contacts.common.model.dataitem.EmailDataItem;
import com.android.contacts.common.model.dataitem.ImDataItem;
import com.android.contacts.common.model.dataitem.PhoneDataItem;
import com.android.contacts.common.util.DataStatus;
-import com.android.contacts.common.util.UriUtils;
+import com.android.contacts.detail.ContactDetailDisplayUtils;
import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.interactions.ContactInteraction;
import com.android.contacts.interactions.SmsInteractionsLoader;
@@ -101,7 +103,7 @@
* data asynchronously, and then shows a popup with details centered around
* {@link Intent#getSourceBounds()}.
*/
-public class QuickContactActivity extends Activity {
+public class QuickContactActivity extends ContactsActivity {
/**
* QuickContacts immediately takes up the full screen. All possible information is shown.
@@ -128,14 +130,11 @@
private int mStatusBarColor;
private boolean mHasAlreadyBeenOpened;
- private View mPhotoContainer;
-
private ImageView mPhotoView;
- private ImageView mEditOrAddContactImage;
- private ImageView mStarImage;
private ExpandingEntryCardView mCommunicationCard;
private ExpandingEntryCardView mRecentCard;
private MultiShrinkScroller mScroller;
+ private SelectAccountDialogFragmentListener mSelectAccountFragmentListener;
private AsyncTask<Void, Void, Void> mEntriesAndActionsTask;
private static final int MIN_NUM_COMMUNICATION_ENTRIES_SHOWN = 3;
@@ -191,35 +190,7 @@
private static final int[] mRecentLoaderIds = new int[LOADER_SMS_ID];
private Map<Integer, List<ContactInteraction>> mRecentLoaderResults;
- final OnClickListener mEditContactClickHandler = new OnClickListener() {
- @Override
- public void onClick(View v) {
- final Intent intent = new Intent(Intent.ACTION_EDIT, mLookupUri);
- mContactLoader.cacheResult();
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- startActivityForResult(intent, REQUEST_CODE_CONTACT_EDITOR_ACTIVITY);
- }
- };
-
- final OnClickListener mAddToContactsClickHandler = new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mContactData == null) {
- Log.e(TAG, "Empty contact data when trying to add to contact");
- return;
- }
- final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- intent.setType(Contacts.CONTENT_ITEM_TYPE);
-
- // Only pre-fill the name field if the provided display name is an organization
- // name or better (e.g. structured name, nickname)
- if (mContactData.getDisplayNameSource() >= DisplayNameSources.ORGANIZATION) {
- intent.putExtra(Insert.NAME, mContactData.getDisplayName());
- }
- intent.putExtra(Insert.DATA, mContactData.getContentValues());
- startActivity(intent);
- }
- };
+ private static final String FRAGMENT_TAG_SELECT_ACCOUNT = "select_account_fragment";
final OnClickListener mEntryClickHandler = new OnClickListener() {
@Override
@@ -233,6 +204,36 @@
}
};
+ /**
+ * Headless fragment used to handle account selection callbacks invoked from
+ * {@link DirectoryContactUtil}.
+ */
+ public static class SelectAccountDialogFragmentListener extends Fragment
+ implements SelectAccountDialogFragment.Listener {
+
+ private QuickContactActivity mQuickContactActivity;
+
+ public SelectAccountDialogFragmentListener() {}
+
+ @Override
+ public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) {
+ DirectoryContactUtil.createCopy(mQuickContactActivity.mContactData.getContentValues(),
+ account, mQuickContactActivity);
+ }
+
+ @Override
+ public void onAccountSelectorCancelled() {}
+
+ /**
+ * Set the parent activity. Since rotation can cause this fragment to be used across
+ * more than one activity instance, we need to explicitly set this value instead
+ * of making this class non-static.
+ */
+ public void setQuickContactActivity(QuickContactActivity quickContactActivity) {
+ mQuickContactActivity = quickContactActivity;
+ }
+ }
+
final MultiShrinkScrollerListener mMultiShrinkScrollerListener
= new MultiShrinkScrollerListener() {
@Override
@@ -261,27 +262,7 @@
// silliness of the animation by setting the navigation bar transparent.
getWindow().setNavigationBarColor(Color.TRANSPARENT);
- // Parse intent
- final Intent intent = getIntent();
-
- Uri lookupUri = intent.getData();
-
- // Check to see whether it comes from the old version.
- if (lookupUri != null && LEGACY_AUTHORITY.equals(lookupUri.getAuthority())) {
- final long rawContactId = ContentUris.parseId(lookupUri);
- lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
- }
-
- mExtraMode = getIntent().getIntExtra(QuickContact.EXTRA_MODE,
- QuickContact.MODE_LARGE);
-
- mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
-
- mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
-
- mContactLoader = (ContactLoader) getLoaderManager().initLoader(
- LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
+ processIntent(getIntent());
// Show QuickContact in front of soft input
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
@@ -289,14 +270,10 @@
setContentView(R.layout.quickcontact_activity);
- mEditOrAddContactImage = (ImageView) findViewById(R.id.contact_edit_image);
- mStarImage = (ImageView) findViewById(R.id.quickcontact_star_button);
mCommunicationCard = (ExpandingEntryCardView) findViewById(R.id.communication_card);
mRecentCard = (ExpandingEntryCardView) findViewById(R.id.recent_card);
mScroller = (MultiShrinkScroller) findViewById(R.id.multiscroller);
- mEditOrAddContactImage.setOnClickListener(mEditContactClickHandler);
-
mCommunicationCard.setOnClickListener(mEntryClickHandler);
mCommunicationCard.setTitle(getResources().getString(R.string.communication_card_title));
mCommunicationCard.setExpandButtonText(
@@ -305,13 +282,11 @@
mRecentCard.setOnClickListener(mEntryClickHandler);
mRecentCard.setTitle(getResources().getString(R.string.recent_card_title));
- // find and prepare correct header view
- mPhotoContainer = findViewById(R.id.photo_container);
+ mPhotoView = (ImageView) findViewById(R.id.photo);
- setHeaderNameText(R.id.name, R.string.missing_name);
-
- mPhotoView = (ImageView) mPhotoContainer.findViewById(R.id.photo);
- mPhotoView.setOnClickListener(mEditContactClickHandler);
+ final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setActionBar(toolbar);
+ setHeaderNameText(R.string.missing_name);
mHasAlreadyBeenOpened = savedInstanceState != null;
@@ -332,8 +307,16 @@
}
}
-
mDrawablesToTint = new ArrayList<>();
+ mSelectAccountFragmentListener= (SelectAccountDialogFragmentListener) getFragmentManager()
+ .findFragmentByTag(FRAGMENT_TAG_SELECT_ACCOUNT);
+ if (mSelectAccountFragmentListener == null) {
+ mSelectAccountFragmentListener = new SelectAccountDialogFragmentListener();
+ getFragmentManager().beginTransaction().add(0, mSelectAccountFragmentListener,
+ FRAGMENT_TAG_SELECT_ACCOUNT).commit();
+ mSelectAccountFragmentListener.setRetainInstance(true);
+ }
+ mSelectAccountFragmentListener.setQuickContactActivity(this);
Trace.endSection();
}
@@ -351,6 +334,33 @@
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mHasAlreadyBeenOpened = true;
+ processIntent(intent);
+ }
+
+ private void processIntent(Intent intent) {
+ Uri lookupUri = intent.getData();
+
+ // Check to see whether it comes from the old version.
+ if (lookupUri != null && LEGACY_AUTHORITY.equals(lookupUri.getAuthority())) {
+ final long rawContactId = ContentUris.parseId(lookupUri);
+ lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+ mExtraMode = getIntent().getIntExtra(QuickContact.EXTRA_MODE,
+ QuickContact.MODE_LARGE);
+ final Uri oldLookupUri = mLookupUri;
+
+ mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
+ mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
+ if (oldLookupUri == null) {
+ mContactLoader = (ContactLoader) getLoaderManager().initLoader(
+ LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
+ } else if (oldLookupUri != mLookupUri) {
+ // After copying a directory contact, the contact URI changes. Therefore,
+ // we need to restart the loader and reload the new contact.
+ mContactLoader = (ContactLoader) getLoaderManager().restartLoader(
+ LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
+ }
}
private void runEntranceAnimation() {
@@ -367,17 +377,14 @@
}
/** Assign this string to the view if it is not empty. */
- private void setHeaderNameText(int id, int resId) {
- setHeaderNameText(id, getText(resId));
+ private void setHeaderNameText(int resId) {
+ getActionBar().setTitle(getText(resId));
}
/** Assign this string to the view if it is not empty. */
- private void setHeaderNameText(int id, CharSequence value) {
- final View view = mPhotoContainer.findViewById(id);
- if (view instanceof TextView) {
- if (!TextUtils.isEmpty(value)) {
- ((TextView)view).setText(value);
- }
+ private void setHeaderNameText(CharSequence value) {
+ if (!TextUtils.isEmpty(value)) {
+ getActionBar().setTitle(value);
}
}
@@ -401,58 +408,7 @@
private void bindContactData(final Contact data) {
Trace.beginSection("bindContactData");
mContactData = data;
- final Context context = this;
-
- mEditOrAddContactImage.setVisibility(isMimeExcluded(Contacts.CONTENT_ITEM_TYPE) ?
- View.GONE : View.VISIBLE);
- final boolean isStarred = data.getStarred();
- if (isStarred) {
- mStarImage.setImageResource(R.drawable.ic_favorite_on_lt);
- mStarImage.setContentDescription(
- getResources().getString(R.string.menu_removeStar));
- } else {
- mStarImage.setImageResource(R.drawable.ic_favorite_off_lt);
- mStarImage.setContentDescription(
- getResources().getString(R.string.menu_addStar));
- }
- final Uri lookupUri = data.getLookupUri();
-
- // If this is a json encoded URI, there is no local contact to star
- if (UriUtils.isEncodedContactUri(lookupUri)) {
- mStarImage.setVisibility(View.GONE);
-
- // If directory export support is not allowed, then don't allow the user to add
- // to contacts
- if (mContactData.getDirectoryExportSupport() == Directory.EXPORT_SUPPORT_NONE) {
- configureHeaderClickActions(false);
- } else {
- configureHeaderClickActions(true);
- }
- } else {
- configureHeaderClickActions(false);
- mStarImage.setVisibility(View.VISIBLE);
- mStarImage.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- // Toggle "starred" state
- // Make sure there is a contact
- if (lookupUri != null) {
- // Changes the state of the image already before sending updates to the
- // database
- if (isStarred) {
- mStarImage.setImageResource(R.drawable.ic_favorite_off_lt);
- } else {
- mStarImage.setImageResource(R.drawable.ic_favorite_on_lt);
- }
-
- // Now perform the real save
- final Intent intent = ContactSaveService.createSetStarredIntent(context,
- lookupUri, !isStarred);
- context.startService(intent);
- }
- }
- });
- }
+ invalidateOptionsMenu();
mDefaultsMap.clear();
@@ -461,7 +417,7 @@
mPhotoSetter.setupContactPhoto(data, mPhotoView);
extractAndApplyTintFromPhotoViewAsynchronously();
- setHeaderNameText(R.id.name, data.getDisplayName());
+ setHeaderNameText(data.getDisplayName());
Trace.endSection();
@@ -517,7 +473,7 @@
}
final boolean hasData = !sortedActionMimeTypes.isEmpty();
- mCommunicationCard.setVisibility(hasData ? View.VISIBLE: View.GONE);
+ mCommunicationCard.setVisibility(hasData ? View.VISIBLE : View.GONE);
Trace.endSection();
}
@@ -748,24 +704,6 @@
}
/**
- * Bind the correct image resource and click handlers to the header views
- *
- * @param canAdd Whether or not the user can directly add information in this quick contact
- * to their local contacts
- */
- private void configureHeaderClickActions(boolean canAdd) {
- if (canAdd) {
- mEditOrAddContactImage.setImageResource(R.drawable.ic_person_add_24dp);
- mEditOrAddContactImage.setOnClickListener(mAddToContactsClickHandler);
- mPhotoView.setOnClickListener(mAddToContactsClickHandler);
- } else {
- mEditOrAddContactImage.setImageResource(R.drawable.ic_create_24dp);
- mEditOrAddContactImage.setOnClickListener(mEditContactClickHandler);
- mPhotoView.setOnClickListener(mEditContactClickHandler);
- }
- }
-
- /**
* Converts a list of Action into a list of Entry
* @param actions The list of Action to convert
* @return The converted list of Entry
@@ -869,8 +807,10 @@
if (mLookupUri == null) {
Log.wtf(TAG, "Lookup uri wasn't initialized. Loader was started too early");
}
+ // Load all contact data. We need loadGroupMetaData=true to determine whether the
+ // contact is invisible. If it is, we need to display an "Add to Contacts" MenuItem.
return new ContactLoader(getApplicationContext(), mLookupUri,
- false /*loadGroupMetaData*/, false /*loadInvitableAccountTypes*/,
+ true /*loadGroupMetaData*/, false /*loadInvitableAccountTypes*/,
false /*postViewNotification*/, true /*computeFormattedPhoneNumber*/);
}
};
@@ -974,7 +914,7 @@
/**
* Applies the theme color as extracted in
- * {@link extractAndApplyTintFromPhotoViewAsynchonously()} if available. If the color is not
+ * {@link #extractAndApplyTintFromPhotoViewAsynchronously()} if available. If the color is not
* available, store a reference to the drawable to tint when a color becomes available.
*/
private Drawable applyThemeColorIfAvailable(Drawable drawable) {
@@ -983,6 +923,90 @@
} else {
mDrawablesToTint.add(drawable);
}
- return drawable;
+ return drawable;
+ }
+
+ /**
+ * Returns true if it is possible to edit the current contact.
+ */
+ private boolean isContactEditable() {
+ return mContactData != null && !mContactData.isDirectoryEntry();
+ }
+
+ private void editContact() {
+ final Intent intent = new Intent(Intent.ACTION_EDIT, mLookupUri);
+ mContactLoader.cacheResult();
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ startActivityForResult(intent, REQUEST_CODE_CONTACT_EDITOR_ACTIVITY);
+ }
+
+ private void toggleStar(MenuItem starredMenuItem) {
+ // Make sure there is a contact
+ if (mLookupUri != null) {
+ // Read the current starred value from the UI instead of using the last
+ // loaded state. This allows rapid tapping without writing the same
+ // value several times
+ final boolean isStarred = starredMenuItem.isChecked();
+
+ // To improve responsiveness, swap out the picture (and tag) in the UI already
+ ContactDetailDisplayUtils.configureStarredMenuItem(starredMenuItem,
+ mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
+ !isStarred);
+
+ // Now perform the real save
+ Intent intent = ContactSaveService.createSetStarredIntent(
+ QuickContactActivity.this, mLookupUri, !isStarred);
+ startService(intent);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.quickcontact, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (mContactData != null) {
+ final MenuItem starredMenuItem = menu.findItem(R.id.menu_star);
+ ContactDetailDisplayUtils.configureStarredMenuItem(starredMenuItem,
+ mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
+ mContactData.getStarred());
+ // Configure edit MenuItem
+ final MenuItem editMenuItem = menu.findItem(R.id.menu_edit);
+ editMenuItem.setVisible(true);
+ if (DirectoryContactUtil.isDirectoryContact(mContactData) || InvisibleContactUtil
+ .isInvisibleAndAddable(mContactData, this)) {
+ editMenuItem.setIcon(R.drawable.ic_person_add_24dp);
+ } else if (isContactEditable()) {
+ editMenuItem.setIcon(R.drawable.ic_create_24dp);
+ } else {
+ editMenuItem.setVisible(false);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_star:
+ toggleStar(item);
+ return true;
+ case R.id.menu_edit:
+ if (DirectoryContactUtil.isDirectoryContact(mContactData)) {
+ DirectoryContactUtil.addToMyContacts(mContactData, this, getFragmentManager(),
+ mSelectAccountFragmentListener);
+ } else if (InvisibleContactUtil.isInvisibleAndAddable(mContactData, this)) {
+ InvisibleContactUtil.addToDefaultGroup(mContactData, this);
+ } else if (isContactEditable()) {
+ editContact();
+ }
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
}
}