Merge "Accessibility: Announce star/unstar actions" into lmp-dev
diff --git a/res/drawable-hdpi/ic_person_add_24dp.png b/res/drawable-hdpi/ic_person_add_24dp.png
deleted file mode 100644
index 1b71ff1..0000000
--- a/res/drawable-hdpi/ic_person_add_24dp.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_person_add_24dp.png b/res/drawable-mdpi/ic_person_add_24dp.png
deleted file mode 100644
index b6cf02d..0000000
--- a/res/drawable-mdpi/ic_person_add_24dp.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_person_add_24dp.png b/res/drawable-xhdpi/ic_person_add_24dp.png
deleted file mode 100644
index 2d265e1..0000000
--- a/res/drawable-xhdpi/ic_person_add_24dp.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_person_add_24dp.png b/res/drawable-xxhdpi/ic_person_add_24dp.png
deleted file mode 100644
index 6091312..0000000
--- a/res/drawable-xxhdpi/ic_person_add_24dp.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_person_add_tinted_24dp.xml b/res/drawable/ic_person_add_tinted_24dp.xml
deleted file mode 100644
index 6d79663..0000000
--- a/res/drawable/ic_person_add_tinted_24dp.xml
+++ /dev/null
@@ -1,20 +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
-  -->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_person_add_24dp"
-    android:autoMirrored="true"
-    android:tint="@color/actionbar_icon_color" />
\ No newline at end of file
diff --git a/res/layout/contact_picker.xml b/res/layout/contact_picker.xml
index b6741cf..2efe471 100644
--- a/res/layout/contact_picker.xml
+++ b/res/layout/contact_picker.xml
@@ -14,16 +14,9 @@
      limitations under the License.
 -->
 
-<RelativeLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="@style/ContactPickerLayout"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent">
-
-    <FrameLayout
-        android:id="@+id/list_container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-
-    <include layout="@layout/floating_action_button" />
-</RelativeLayout>
+    android:id="@+id/list_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/res/layout/editor_custom_action_bar.xml b/res/layout/editor_custom_action_bar.xml
index 7a31b92..7764674 100644
--- a/res/layout/editor_custom_action_bar.xml
+++ b/res/layout/editor_custom_action_bar.xml
@@ -30,7 +30,7 @@
         android:layout_marginEnd="8dip"
         android:src="@drawable/ic_done_wht_24dp"
         style="?android:attr/actionButtonStyle"
-        android:description="@string/menu_done" />
+        android:contentDescription="@string/menu_done" />
 
     <TextView
         android:id="@+id/title"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 17b95a6..9583fcb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -46,6 +46,9 @@
     <!-- Activity title when the user is selecting a contact.  [CHAR LIMIT=128] -->
     <string name="contactPickerActivityTitle">Choose a contact</string>
 
+    <!-- Activity title when the user is selecting a contact.  [CHAR LIMIT=30] -->
+    <string name="header_entry_contact_list_adapter_header_title">Select new contact</string>
+
     <!-- Title for the activity that shows only starred contacts -->
     <string name="starredList">Starred</string>
 
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index 4375cb7..c67a50e 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -35,7 +35,6 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnFocusChangeListener;
 import android.view.inputmethod.InputMethodManager;
-import android.widget.ImageButton;
 import android.widget.SearchView;
 import android.widget.SearchView.OnCloseListener;
 import android.widget.SearchView.OnQueryTextListener;
@@ -44,7 +43,6 @@
 import com.android.contacts.ContactsActivity;
 import com.android.contacts.R;
 import com.android.contacts.common.list.ContactEntryListFragment;
-import com.android.contacts.common.util.ViewUtil;
 import com.android.contacts.list.ContactPickerFragment;
 import com.android.contacts.list.ContactsIntentResolver;
 import com.android.contacts.list.ContactsRequest;
@@ -129,33 +127,19 @@
         }
 
         prepareSearchViewAndActionBar();
-
-        // Configure action button
-        final View floatingActionButtonContainer = findViewById(
-                R.id.floating_action_button_container);
-        if (shouldShowCreateNewContactButton()) {
-            ViewUtil.setupFloatingActionButton(floatingActionButtonContainer, getResources());
-            final ImageButton floatingActionButton
-                    = (ImageButton) findViewById(R.id.floating_action_button);
-            floatingActionButton.setOnClickListener(this);
-        } else {
-            floatingActionButtonContainer.setVisibility(View.GONE);
-        }
-    }
-
-    private boolean shouldShowCreateNewContactButton() {
-        return (mActionCode == ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT
-                || (mActionCode == ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT
-                        && !mRequest.isSearchMode()));
     }
 
     private void prepareSearchViewAndActionBar() {
+        final ActionBar actionBar = getActionBar();
+        final View searchViewContainer = LayoutInflater.from(actionBar.getThemedContext())
+                .inflate(R.layout.custom_action_bar, null);
+        mSearchView = (SearchView) searchViewContainer.findViewById(R.id.search_view);
+
         // Postal address pickers (and legacy pickers) don't support search, so just show
         // "HomeAsUp" button and title.
         if (mRequest.getActionCode() == ContactsRequest.ACTION_PICK_POSTAL ||
                 mRequest.isLegacyCompatibilityMode()) {
-            findViewById(R.id.search_view).setVisibility(View.GONE);
-            final ActionBar actionBar = getActionBar();
+            mSearchView.setVisibility(View.GONE);
             if (actionBar != null) {
                 actionBar.setDisplayShowHomeEnabled(true);
                 actionBar.setDisplayHomeAsUpEnabled(true);
@@ -164,11 +148,6 @@
             return;
         }
 
-        final ActionBar actionBar = getActionBar();
-        final View searchViewContainer = LayoutInflater.from(actionBar.getThemedContext())
-                .inflate(R.layout.custom_action_bar, null);
-        mSearchView = (SearchView) searchViewContainer.findViewById(R.id.search_view);
-
         // In order to make the SearchView look like "shown via search menu", we need to
         // manually setup its state. See also DialtactsActivity.java and ActionBarAdapter.java.
         mSearchView.setIconifiedByDefault(true);
@@ -272,6 +251,7 @@
                 ContactPickerFragment fragment = new ContactPickerFragment();
                 fragment.setEditMode(true);
                 fragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
+                fragment.setCreateContactEnabled(!mRequest.isSearchMode());
                 mListFragment = fragment;
                 break;
             }
@@ -286,6 +266,7 @@
 
             case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: {
                 ContactPickerFragment fragment = new ContactPickerFragment();
+                fragment.setCreateContactEnabled(!mRequest.isSearchMode());
                 mListFragment = fragment;
                 break;
             }
diff --git a/src/com/android/contacts/interactions/CallLogInteractionsLoader.java b/src/com/android/contacts/interactions/CallLogInteractionsLoader.java
index 2a9bcea..9cbc0b4 100644
--- a/src/com/android/contacts/interactions/CallLogInteractionsLoader.java
+++ b/src/com/android/contacts/interactions/CallLogInteractionsLoader.java
@@ -101,7 +101,7 @@
     }
 
     private List<ContactInteraction> getCallLogInteractions(String phoneNumber) {
-        final Uri uri = Uri.withAppendedPath(Calls.CONTENT_FILTER_URI, phoneNumber);
+        final Uri uri = Uri.withAppendedPath(Calls.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
         // Append the LIMIT clause onto the ORDER BY clause. This won't cause crashes as long
         // as we don't also set the {@link android.provider.CallLog.Calls.LIMIT_PARAM_KEY} that
         // becomes available in KK.
diff --git a/src/com/android/contacts/list/ContactPickerFragment.java b/src/com/android/contacts/list/ContactPickerFragment.java
index 3dc4330..442f5c7 100644
--- a/src/com/android/contacts/list/ContactPickerFragment.java
+++ b/src/com/android/contacts/list/ContactPickerFragment.java
@@ -28,7 +28,6 @@
 import com.android.contacts.common.list.ContactEntryListFragment;
 import com.android.contacts.common.list.ContactListAdapter;
 import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.common.list.DefaultContactListAdapter;
 import com.android.contacts.common.list.DirectoryListLoader;
 import com.android.contacts.common.list.ShortcutIntentBuilder;
 import com.android.contacts.common.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
@@ -105,7 +104,9 @@
     @Override
     protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
         super.onCreateView(inflater, container);
-        if (mCreateContactEnabled) {
+        if (mCreateContactEnabled && isLegacyCompatibilityMode()) {
+            // Since we are using the legacy adapter setShowCreateContact(true) isn't supported.
+            // So we need to add an ugly header above the list.
             getListView().addHeaderView(inflater.inflate(R.layout.create_new_contact, null, false));
         }
     }
@@ -152,12 +153,14 @@
     @Override
     protected ContactEntryListAdapter createListAdapter() {
         if (!isLegacyCompatibilityMode()) {
-            DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity());
+            HeaderEntryContactListAdapter adapter
+                    = new HeaderEntryContactListAdapter(getActivity());
             adapter.setFilter(ContactListFilter.createFilterWithType(
                     ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
             adapter.setSectionHeaderDisplayEnabled(true);
             adapter.setDisplayPhotos(true);
             adapter.setQuickContactEnabled(false);
+            adapter.setShowCreateContact(mCreateContactEnabled);
             return adapter;
         } else {
             LegacyContactListAdapter adapter = new LegacyContactListAdapter(getActivity());
diff --git a/src/com/android/contacts/list/HeaderEntryContactListAdapter.java b/src/com/android/contacts/list/HeaderEntryContactListAdapter.java
new file mode 100644
index 0000000..6d6c3c3
--- /dev/null
+++ b/src/com/android/contacts/list/HeaderEntryContactListAdapter.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+package com.android.contacts.list;
+
+import com.android.contacts.R;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.DefaultContactListAdapter;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Equivalent to DefaultContactListAdapter, except with an optional header entry that has the same
+ * formatting as the other entries in the list.
+ *
+ * This header entry is hidden when in search mode. Should not be used with lists that contain a
+ * "Me" contact.
+ */
+public class HeaderEntryContactListAdapter extends DefaultContactListAdapter {
+
+    private boolean mShowCreateContact;
+
+    public HeaderEntryContactListAdapter(Context context) {
+        super(context);
+    }
+
+    private int getHeaderEntryCount() {
+        return isSearchMode() || !mShowCreateContact ? 0 : 1;
+    }
+
+    /**
+     * Whether the first entry should be "Create contact", when not in search mode.
+     */
+    public void setShowCreateContact(boolean showCreateContact) {
+        mShowCreateContact = showCreateContact;
+        invalidate();
+    }
+
+    @Override
+    public int getCount() {
+        return super.getCount() + getHeaderEntryCount();
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        if (position == 0 && getHeaderEntryCount() > 0) {
+            final ContactListItemView itemView;
+            if (convertView == null) {
+                // Pass the cursor down. Don't worry, it isn't used.
+                itemView = newView(getContext(), 0, getCursor(0), 0, parent);
+            } else {
+                itemView = (ContactListItemView ) convertView;
+            }
+            itemView.setDrawableResource(R.drawable.search_shortcut_background,
+                    R.drawable.ic_search_add_contact);
+            itemView.setDisplayName(getContext().getResources().getString(
+                    R.string.header_entry_contact_list_adapter_header_title));
+            return itemView;
+        }
+        return super.getView(position - getHeaderEntryCount(), convertView, parent);
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return super.getItem(position - getHeaderEntryCount());
+    }
+
+    @Override
+    protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+        super.bindView(itemView, partition, cursor, position + getHeaderEntryCount());
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (position == 0 && getHeaderEntryCount() > 0) {
+            return getViewTypeCount() - 1;
+        }
+        return super.getItemViewType(position - getHeaderEntryCount());
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        // One additional view type, for the header entry.
+        return super.getViewTypeCount() + 1;
+    }
+
+    @Override
+    protected boolean getExtraStartingSection() {
+        return getHeaderEntryCount() > 0;
+    }
+}