Add FeatureHighlight library to proguard.
am: 4a2abe9db2

Change-Id: Icfca14dca3a1eb8bf3b194fd208e4cbbf043f3b8
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 230fe9d..ea61e7b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.contacts"
-    android:versionCode="10511"
-    android:versionName="1.5.11">
+    android:versionCode="20000"
+    android:versionName="2.0.0">
 
     <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25" />
 
@@ -49,6 +49,8 @@
     <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
     <!-- Following used for Contact metadata syncing -->
     <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
+    <!-- Following used for getting the status of the contacts sync adapter -->
+    <uses-permission android:name="android.permission.READ_SYNC_STATS" />
 
     <uses-feature android:name="android.hardware.telephony" android:required="false"/>
 
diff --git a/proguard.flags b/proguard.flags
index baff4b6..7b9bc89 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -15,6 +15,7 @@
 # For test:
 -keep class com.android.contacts.commonbind.analytics.AnalyticsUtil { *;}
 -keep class com.android.contacts.interactions.** { *;}
+-keep class com.android.contacts.util.SyncUtil { *;}
 -keep class com.google.common.base.Objects { *;}
 -keep class com.google.common.base.Preconditions { *;}
 -keep class com.google.common.collect.Lists { *;}
diff --git a/res/layout/floating_action_button.xml b/res/layout/floating_action_button.xml
index 95c76ae..2dc8955 100644
--- a/res/layout/floating_action_button.xml
+++ b/res/layout/floating_action_button.xml
@@ -14,24 +14,26 @@
      limitations under the License.
 -->
 
-<!-- This expects to be included inside a RelativeLayout -->
+<!-- This expects to be included inside a RelativeLayout or a CoordinatorLayout -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/floating_action_button_container"
     android:layout_width="@dimen/floating_action_button_width"
     android:layout_height="@dimen/floating_action_button_height"
-    android:layout_marginEnd="@dimen/floating_action_button_margin_right"
-    android:layout_marginBottom="@dimen/floating_action_button_margin_bottom"
-    android:background="@drawable/fab_blue"
+    android:layout_alignParentBottom="true"
     android:layout_alignParentEnd="true"
-    android:layout_alignParentBottom="true">
+    android:layout_gravity="bottom|end"
+    android:layout_marginBottom="@dimen/floating_action_button_margin_bottom"
+    android:layout_marginEnd="@dimen/floating_action_button_margin_right"
+    android:background="@drawable/fab_blue"
+    android:elevation="@dimen/design_fab_elevation">
 
     <ImageButton
         android:id="@+id/floating_action_button"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="@drawable/floating_action_button"
-        android:tint="@color/floating_action_button_icon_color"
         android:contentDescription="@string/action_menu_add_new_contact_button"
-        android:src="@drawable/ic_add"/>
+        android:src="@drawable/ic_add"
+        android:tint="@color/floating_action_button_icon_color"/>
 </FrameLayout>
\ No newline at end of file
diff --git a/res/layout/group_name_dialog.xml b/res/layout/group_name_dialog.xml
deleted file mode 100644
index f19df86..0000000
--- a/res/layout/group_name_dialog.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingLeft="25dip"
-    android:paddingRight="25dip"
-    android:paddingStart="25dip"
-    android:paddingEnd="25dip"
-    android:paddingTop="25dip"
-    android:paddingBottom="25dip">
-    <EditText
-        android:id="@+id/group_label"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:minHeight="@dimen/group_name_edit_text_min_height"/>
-</FrameLayout>
diff --git a/res/layout/group_name_edit_dialog.xml b/res/layout/group_name_edit_dialog.xml
index 9a8c69f..5df8c9d 100644
--- a/res/layout/group_name_edit_dialog.xml
+++ b/res/layout/group_name_edit_dialog.xml
@@ -14,24 +14,28 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.design.widget.TextInputLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/text_input_layout"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:paddingBottom="24dp"
+    android:paddingEnd="24dp"
     android:paddingStart="24dp"
-    android:paddingEnd="24dp">
+    app:errorEnabled="true"
+    app:hintEnabled="false">
 
-    <EditText android:id="@android:id/text1"
-        xmlns:android="http://schemas.android.com/apk/res/android"
+    <android.support.design.widget.TextInputEditText
+        android:id="@android:id/text1"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/group_name_edit_text_min_height"
         android:layout_marginBottom="4dp"
         android:layout_marginRight="4dp"
         android:layout_marginTop="16dp"
         android:hint="@string/group_name_dialog_hint"
         android:inputType="text"
-        android:singleLine="true"
-        android:maxLength="@integer/group_name_max_length"/>
-</LinearLayout>
\ No newline at end of file
+        android:maxLength="@integer/group_name_max_length"
+        android:minHeight="@dimen/group_name_edit_text_min_height"
+        android:singleLine="true"/>
+</android.support.design.widget.TextInputLayout>
\ No newline at end of file
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 3078976..b3c57d3 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -52,7 +52,7 @@
     <string name="menu_call" msgid="3992595586042260618">"Pozovi kontakt"</string>
     <string name="menu_sendSMS" msgid="5535886767547006515">"Pošalji SMS kontaktu"</string>
     <string name="menu_splitAggregate" msgid="2627252205317945563">"Razdvoji"</string>
-    <string name="menu_editGroup" msgid="5062005185370983720">"Izmeni"</string>
+    <string name="menu_editGroup" msgid="6696843438454341063">"Ukloni kontakte"</string>
     <string name="menu_renameGroup" msgid="7169512355179757182">"Preimenuj oznaku"</string>
     <string name="menu_deleteGroup" msgid="1126469629233412249">"Izbriši oznaku"</string>
     <string name="menu_addToGroup" msgid="3267409983764370041">"Dodaj kontakt"</string>
@@ -80,10 +80,15 @@
       <item quantity="few">Kontakti su izbrisani</item>
       <item quantity="other">Kontakti su izbrisani</item>
     </plurals>
-    <plurals name="contacts_count" formatted="false" msgid="3287407967505649458">
-      <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> kontakt</item>
-      <item quantity="few"><xliff:g id="COUNT">%d</xliff:g> kontakta</item>
-      <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> kontakata</item>
+    <plurals name="contacts_count" formatted="false" msgid="8696793457340503668">
+      <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> kontakt</item>
+      <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> kontakta</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> kontakata</item>
+    </plurals>
+    <plurals name="contacts_count_with_account" formatted="false" msgid="7402583111980220575">
+      <item quantity="one"><xliff:g id="COUNT_2">%d</xliff:g> kontakt · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
+      <item quantity="few"><xliff:g id="COUNT_2">%d</xliff:g> kontakta · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
+      <item quantity="other"><xliff:g id="COUNT_2">%d</xliff:g> kontakata · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
     </plurals>
     <string name="title_from_google" msgid="4664084747121207202">"Sa Google-a"</string>
     <string name="title_from_other_accounts" msgid="8307885412426754288">"Sa <xliff:g id="ACCOUNT">%s</xliff:g>"</string>
@@ -111,10 +116,15 @@
     <string name="customLabelPickerTitle" msgid="1081475101983255212">"Naziv prilagođene oznake"</string>
     <string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Šalji pozive direktno u glasovnu poštu"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Ukloni fotografiju"</string>
-    <string name="noContacts" msgid="4955659076981974652">"Nema kontakata"</string>
+    <!-- no translation found for noContacts (2228592924476426108) -->
+    <skip />
     <string name="noGroups" msgid="4607906327968232225">"Nema oznaka."</string>
     <string name="noAccounts" msgid="7768267764545265909">"Morate da imate nalog da biste mogli da pravite grupe."</string>
     <string name="emptyGroup" msgid="5102411903247859575">"Nema kontakata sa ovom oznakom"</string>
+    <!-- no translation found for emptyAccount (6873962901497975964) -->
+    <skip />
+    <!-- no translation found for emptyMainList (2772242747899664460) -->
+    <skip />
     <string name="contactSavedToast" msgid="9171862279493213075">"Kontakt je sačuvan"</string>
     <string name="contactUnlinkedToast" msgid="7122823195786012553">"Kontakti su razdvojeni"</string>
     <string name="contactSavedErrorToast" msgid="3207250533172944892">"Nije moguće sačuvati izmene kontakata."</string>
@@ -223,7 +233,8 @@
     <string name="social_widget_loading" msgid="5327336597364074608">"Učitava se…"</string>
     <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Napravi novi kontakt"</string>
     <string name="contacts_unavailable_add_account" msgid="4347232421410561500">"Dodajte nalog"</string>
-    <string name="contacts_unavailable_import_contacts" msgid="4957393255392437529">"Uvezi kontakte"</string>
+    <!-- no translation found for contacts_unavailable_import_contacts (3182801738595937144) -->
+    <skip />
     <string name="create_group_dialog_title" msgid="5363404287877384473">"Napravite novu oznaku"</string>
     <string name="create_group_item_label" msgid="3263064599743742865">"Napravi novu…"</string>
     <string name="delete_group_dialog_message" msgid="335713829185261371">"Želite da izbrišete oznaku „<xliff:g id="GROUP_LABEL">%1$s</xliff:g>“? (Kontakti neće biti izbrisani.)"</string>
@@ -237,19 +248,18 @@
     <string name="cancel_confirmation_dialog_keep_editing_button" msgid="7737724111972855348">"Nastavi izmene"</string>
     <string name="call_type_and_date" msgid="747163730039311423">"<xliff:g id="CALL_TYPE">%1$s</xliff:g> <xliff:g id="CALL_SHORT_DATE">%2$s</xliff:g>"</string>
     <string name="enter_contact_name" msgid="4594274696120278368">"Pretražite kontakte"</string>
-    <!-- no translation found for title_edit_group (1889302367574226969) -->
-    <skip />
+    <string name="title_edit_group" msgid="8602752287270586734">"Uklonite kontakte"</string>
     <string name="local_profile_title" msgid="2021416826991393684">"Moj lokalni profil"</string>
     <string name="external_profile_title" msgid="8034998767621359438">"Moj <xliff:g id="EXTERNAL_SOURCE">%1$s</xliff:g> profil"</string>
     <string name="toast_displaying_all_contacts" msgid="2737388783898593875">"Prikazani su svi kontakti"</string>
     <string name="generic_no_account_prompt" msgid="7218827704367325460">"Očuvajte bezbednost kontakata čak i ako izgubite telefon – sinhronizujte sa uslugom na mreži."</string>
     <string name="generic_no_account_prompt_title" msgid="753783911899054860">"Dodavanje naloga"</string>
-    <string name="contact_editor_prompt_zero_accounts" msgid="1785345895691886499">"Neće biti napravljena rezervna kopija novog kontakta. Želite li da dodate nalog koji pravi rezervnu kopiju kontakata onlajn?"</string>
+    <!-- no translation found for contact_editor_prompt_zero_accounts (6648376557574360096) -->
+    <skip />
     <string name="contact_editor_prompt_one_account" msgid="3087691056345099310">"Novi kontakti će biti sačuvani na nalogu <xliff:g id="ACCOUNT_NAME">%1$s</xliff:g>."</string>
     <string name="contact_editor_prompt_multiple_accounts" msgid="8565761674283473549">"Izaberite podrazumevani nalog za nove kontakte:"</string>
     <string name="contact_editor_title_new_contact" msgid="7192223018128934940">"Dodaj novi kontakt"</string>
     <string name="contact_editor_title_existing_contact" msgid="4898475703683187798">"Izmeni kontakt"</string>
-    <string name="keep_local" msgid="1258761699192993322">"Zadrži lokalno"</string>
     <string name="add_account" msgid="8201790677994503186">"Dodaj nalog"</string>
     <string name="add_new_account" msgid="5748627740680940264">"Dodaj novi nalog"</string>
     <string name="menu_export_database" msgid="2659719297530170820">"Izvezi datoteke baze podataka"</string>
@@ -346,4 +356,8 @@
     <string name="permission_explanation_subheader_calendar_and_SMS" msgid="630115334220569184">"Događaji i poruke"</string>
     <string name="permission_explanation_subheader_calendar" msgid="8785323496211704613">"Događaji"</string>
     <string name="permission_explanation_subheader_SMS" msgid="1904552086449525567">"Poruke"</string>
+    <!-- no translation found for hamburger_feature_highlight_header (7442308698936786415) -->
+    <skip />
+    <!-- no translation found for hamburger_feature_highlight_body (6268711111318172098) -->
+    <skip />
 </resources>
diff --git a/res/values-be-rBY/strings.xml b/res/values-be-rBY/strings.xml
index 2eee399..90530cb 100644
--- a/res/values-be-rBY/strings.xml
+++ b/res/values-be-rBY/strings.xml
@@ -52,7 +52,7 @@
     <string name="menu_call" msgid="3992595586042260618">"Выклікаць кантакт"</string>
     <string name="menu_sendSMS" msgid="5535886767547006515">"Паведамленне кантакту"</string>
     <string name="menu_splitAggregate" msgid="2627252205317945563">"Выдаліць сувязь"</string>
-    <string name="menu_editGroup" msgid="5062005185370983720">"Рэдагаваць"</string>
+    <string name="menu_editGroup" msgid="6696843438454341063">"Выдаліць кантакты"</string>
     <string name="menu_renameGroup" msgid="7169512355179757182">"Перайменаваць метку"</string>
     <string name="menu_deleteGroup" msgid="1126469629233412249">"Выдаліць метку"</string>
     <string name="menu_addToGroup" msgid="3267409983764370041">"Дадаць кантакт"</string>
@@ -81,11 +81,17 @@
       <item quantity="many">Кантакты выдалены</item>
       <item quantity="other">Кантакты выдалены</item>
     </plurals>
-    <plurals name="contacts_count" formatted="false" msgid="3287407967505649458">
-      <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> кантакт</item>
-      <item quantity="few"><xliff:g id="COUNT">%d</xliff:g> кантакты</item>
-      <item quantity="many"><xliff:g id="COUNT">%d</xliff:g> кантактаў</item>
-      <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> кантакту</item>
+    <plurals name="contacts_count" formatted="false" msgid="8696793457340503668">
+      <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> кантакт</item>
+      <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> кантакты</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> кантактаў</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> кантакту</item>
+    </plurals>
+    <plurals name="contacts_count_with_account" formatted="false" msgid="7402583111980220575">
+      <item quantity="one"><xliff:g id="COUNT_2">%d</xliff:g> кантакт · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
+      <item quantity="few"><xliff:g id="COUNT_2">%d</xliff:g> кантакты · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
+      <item quantity="many"><xliff:g id="COUNT_2">%d</xliff:g> кантактаў · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
+      <item quantity="other"><xliff:g id="COUNT_2">%d</xliff:g> кантакту · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
     </plurals>
     <string name="title_from_google" msgid="4664084747121207202">"З Google"</string>
     <string name="title_from_other_accounts" msgid="8307885412426754288">"З <xliff:g id="ACCOUNT">%s</xliff:g>"</string>
@@ -113,10 +119,15 @@
     <string name="customLabelPickerTitle" msgid="1081475101983255212">"Імя карыстальніцкага цэтліка"</string>
     <string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Адпраўляць выклікі непасрэдна на галасавую пошту"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Выдаліць фота"</string>
-    <string name="noContacts" msgid="4955659076981974652">"Няма кантактаў"</string>
+    <!-- no translation found for noContacts (2228592924476426108) -->
+    <skip />
     <string name="noGroups" msgid="4607906327968232225">"Метак няма."</string>
     <string name="noAccounts" msgid="7768267764545265909">"Для стварэння групы патрабуецца ўліковы запіс."</string>
     <string name="emptyGroup" msgid="5102411903247859575">"Няма кантактаў з гэтай меткай"</string>
+    <!-- no translation found for emptyAccount (6873962901497975964) -->
+    <skip />
+    <!-- no translation found for emptyMainList (2772242747899664460) -->
+    <skip />
     <string name="contactSavedToast" msgid="9171862279493213075">"Кантакт захаваны"</string>
     <string name="contactUnlinkedToast" msgid="7122823195786012553">"Кантакты адлучаны"</string>
     <string name="contactSavedErrorToast" msgid="3207250533172944892">"Немагчыма захаваць змены кантакта."</string>
@@ -229,7 +240,8 @@
     <string name="social_widget_loading" msgid="5327336597364074608">"Загрузка..."</string>
     <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Стварыць новы кантакт"</string>
     <string name="contacts_unavailable_add_account" msgid="4347232421410561500">"Дадаць уліковы запіс"</string>
-    <string name="contacts_unavailable_import_contacts" msgid="4957393255392437529">"Імпартаваць кантакты"</string>
+    <!-- no translation found for contacts_unavailable_import_contacts (3182801738595937144) -->
+    <skip />
     <string name="create_group_dialog_title" msgid="5363404287877384473">"Стварыць новую метку"</string>
     <string name="create_group_item_label" msgid="3263064599743742865">"Стварыць новую..."</string>
     <string name="delete_group_dialog_message" msgid="335713829185261371">"Выдаліць метку \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? (Самі кантакты не будуць выдалены.)"</string>
@@ -243,19 +255,18 @@
     <string name="cancel_confirmation_dialog_keep_editing_button" msgid="7737724111972855348">"Працягнуць рэдагаванне"</string>
     <string name="call_type_and_date" msgid="747163730039311423">"<xliff:g id="CALL_TYPE">%1$s</xliff:g> <xliff:g id="CALL_SHORT_DATE">%2$s</xliff:g>"</string>
     <string name="enter_contact_name" msgid="4594274696120278368">"Пошук кантактаў"</string>
-    <!-- no translation found for title_edit_group (1889302367574226969) -->
-    <skip />
+    <string name="title_edit_group" msgid="8602752287270586734">"Выдаліць кантакты"</string>
     <string name="local_profile_title" msgid="2021416826991393684">"Мой лакальны профіль"</string>
     <string name="external_profile_title" msgid="8034998767621359438">"Мой профіль у <xliff:g id="EXTERNAL_SOURCE">%1$s</xliff:g>"</string>
     <string name="toast_displaying_all_contacts" msgid="2737388783898593875">"Адлюстраванне ўсіх кантактаў"</string>
     <string name="generic_no_account_prompt" msgid="7218827704367325460">"Абаранiце свае кантакты, нават калі страціце тэлефон: сінхранізуйце iх з вэб-службай."</string>
     <string name="generic_no_account_prompt_title" msgid="753783911899054860">"Дадаць уліковы запіс"</string>
-    <string name="contact_editor_prompt_zero_accounts" msgid="1785345895691886499">"Рэзервовая копiя вашага новага кантакту не будзе створана. Дадаць улiковы запiс, у якiм будуць захоўвацца рэзервовыя копii кантактаў у Iнтэрнэце?"</string>
+    <!-- no translation found for contact_editor_prompt_zero_accounts (6648376557574360096) -->
+    <skip />
     <string name="contact_editor_prompt_one_account" msgid="3087691056345099310">"Новыя кантакты будуць захаваны ва ўліковы запіс <xliff:g id="ACCOUNT_NAME">%1$s</xliff:g>."</string>
     <string name="contact_editor_prompt_multiple_accounts" msgid="8565761674283473549">"Выбраць уліковы запіс па змаўчанні для новых кантактаў:"</string>
     <string name="contact_editor_title_new_contact" msgid="7192223018128934940">"Дадаць новы кантакт"</string>
     <string name="contact_editor_title_existing_contact" msgid="4898475703683187798">"Рэдагаваць кантакт"</string>
-    <string name="keep_local" msgid="1258761699192993322">"Захоўваць лакальна"</string>
     <string name="add_account" msgid="8201790677994503186">"Дадаць уліковы запіс"</string>
     <string name="add_new_account" msgid="5748627740680940264">"Дадаць новы ўліковы запіс"</string>
     <string name="menu_export_database" msgid="2659719297530170820">"Экспарт базы дадзеных файлаў"</string>
@@ -356,4 +367,8 @@
     <string name="permission_explanation_subheader_calendar_and_SMS" msgid="630115334220569184">"Падзеі і паведамленні"</string>
     <string name="permission_explanation_subheader_calendar" msgid="8785323496211704613">"Падзеі"</string>
     <string name="permission_explanation_subheader_SMS" msgid="1904552086449525567">"Паведамленні"</string>
+    <!-- no translation found for hamburger_feature_highlight_header (7442308698936786415) -->
+    <skip />
+    <!-- no translation found for hamburger_feature_highlight_body (6268711111318172098) -->
+    <skip />
 </resources>
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
index 67f5166..6a3ea47 100644
--- a/res/values-bs-rBA/strings.xml
+++ b/res/values-bs-rBA/strings.xml
@@ -52,7 +52,7 @@
     <string name="menu_call" msgid="3992595586042260618">"Pozovi kontakt"</string>
     <string name="menu_sendSMS" msgid="5535886767547006515">"Pošalji tekstualnu poruku kontaktu"</string>
     <string name="menu_splitAggregate" msgid="2627252205317945563">"Razdvoji"</string>
-    <string name="menu_editGroup" msgid="5062005185370983720">"Uredi"</string>
+    <string name="menu_editGroup" msgid="6696843438454341063">"Ukloni kontakte"</string>
     <string name="menu_renameGroup" msgid="7169512355179757182">"Preimenuj oznaku"</string>
     <string name="menu_deleteGroup" msgid="1126469629233412249">"Izbriši oznaku"</string>
     <string name="menu_addToGroup" msgid="3267409983764370041">"Dodaj kontakt"</string>
@@ -80,10 +80,15 @@
       <item quantity="few">Kontakti su izbrisani</item>
       <item quantity="other">Kontakti su izbrisani</item>
     </plurals>
-    <plurals name="contacts_count" formatted="false" msgid="3287407967505649458">
-      <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> kontakt</item>
-      <item quantity="few"><xliff:g id="COUNT">%d</xliff:g> kontakta</item>
-      <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> kontakata</item>
+    <plurals name="contacts_count" formatted="false" msgid="8696793457340503668">
+      <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> kontakt</item>
+      <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> kontakta</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> kontakata</item>
+    </plurals>
+    <plurals name="contacts_count_with_account" formatted="false" msgid="7402583111980220575">
+      <item quantity="one"><xliff:g id="COUNT_2">%d</xliff:g> kontakt · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
+      <item quantity="few"><xliff:g id="COUNT_2">%d</xliff:g> kontakta · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
+      <item quantity="other"><xliff:g id="COUNT_2">%d</xliff:g> kontakata · <xliff:g id="ACCOUNT_3">%s</xliff:g></item>
     </plurals>
     <string name="title_from_google" msgid="4664084747121207202">"Sa Googlea"</string>
     <string name="title_from_other_accounts" msgid="8307885412426754288">"Sa računa <xliff:g id="ACCOUNT">%s</xliff:g>"</string>
@@ -111,10 +116,15 @@
     <string name="customLabelPickerTitle" msgid="1081475101983255212">"Prilagođeni naziv oznake"</string>
     <string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Pošalji pozive direktno na govornu poštu"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Ukloni fotografiju"</string>
-    <string name="noContacts" msgid="4955659076981974652">"Nema kontakata"</string>
+    <!-- no translation found for noContacts (2228592924476426108) -->
+    <skip />
     <string name="noGroups" msgid="4607906327968232225">"Nema oznaka."</string>
     <string name="noAccounts" msgid="7768267764545265909">"Potreban vam je račun da napravite grupe."</string>
     <string name="emptyGroup" msgid="5102411903247859575">"Nema kontakata s ovom oznakom"</string>
+    <!-- no translation found for emptyAccount (6873962901497975964) -->
+    <skip />
+    <!-- no translation found for emptyMainList (2772242747899664460) -->
+    <skip />
     <string name="contactSavedToast" msgid="9171862279493213075">"Kontakt je sačuvan"</string>
     <string name="contactUnlinkedToast" msgid="7122823195786012553">"Kontakti su razdvojeni"</string>
     <string name="contactSavedErrorToast" msgid="3207250533172944892">"Nije moguće sačuvati promjene kontakta."</string>
@@ -223,7 +233,8 @@
     <string name="social_widget_loading" msgid="5327336597364074608">"Učitavanje…"</string>
     <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Napravi novi kontakt"</string>
     <string name="contacts_unavailable_add_account" msgid="4347232421410561500">"Dodaj račun"</string>
-    <string name="contacts_unavailable_import_contacts" msgid="4957393255392437529">"Uvezi kontakte"</string>
+    <!-- no translation found for contacts_unavailable_import_contacts (3182801738595937144) -->
+    <skip />
     <string name="create_group_dialog_title" msgid="5363404287877384473">"Napravi novu oznaku"</string>
     <string name="create_group_item_label" msgid="3263064599743742865">"Napravi novu…"</string>
     <string name="delete_group_dialog_message" msgid="335713829185261371">"Izbrisati oznaku \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? (Kontakti neće biti izbrisani.)"</string>
@@ -237,19 +248,18 @@
     <string name="cancel_confirmation_dialog_keep_editing_button" msgid="7737724111972855348">"Nastavi uređivanje"</string>
     <string name="call_type_and_date" msgid="747163730039311423">"<xliff:g id="CALL_TYPE">%1$s</xliff:g> <xliff:g id="CALL_SHORT_DATE">%2$s</xliff:g>"</string>
     <string name="enter_contact_name" msgid="4594274696120278368">"Traži kontakte"</string>
-    <!-- no translation found for title_edit_group (1889302367574226969) -->
-    <skip />
+    <string name="title_edit_group" msgid="8602752287270586734">"Ukloni kontakte"</string>
     <string name="local_profile_title" msgid="2021416826991393684">"Moj lokalni profil"</string>
     <string name="external_profile_title" msgid="8034998767621359438">"Moj profil <xliff:g id="EXTERNAL_SOURCE">%1$s</xliff:g>"</string>
     <string name="toast_displaying_all_contacts" msgid="2737388783898593875">"Prikazuju se svi kontakti"</string>
     <string name="generic_no_account_prompt" msgid="7218827704367325460">"Očuvajte sigurnost svojih kontakata čak i ako izgubite telefon: sinhronizirajte ih s mrežnom uslugom."</string>
     <string name="generic_no_account_prompt_title" msgid="753783911899054860">"Dodajte račun"</string>
-    <string name="contact_editor_prompt_zero_accounts" msgid="1785345895691886499">"Neće biti napravljena sigurnosna kopija novog računa. Dodati račun za pravljenje sigurnosnih kopija kontakata na mreži?"</string>
+    <!-- no translation found for contact_editor_prompt_zero_accounts (6648376557574360096) -->
+    <skip />
     <string name="contact_editor_prompt_one_account" msgid="3087691056345099310">"Novi kontakti će biti sačuvani na račun <xliff:g id="ACCOUNT_NAME">%1$s</xliff:g>."</string>
     <string name="contact_editor_prompt_multiple_accounts" msgid="8565761674283473549">"Izaberite zadani račun za nove kontakte:"</string>
     <string name="contact_editor_title_new_contact" msgid="7192223018128934940">"Dodaj novi kontakt"</string>
     <string name="contact_editor_title_existing_contact" msgid="4898475703683187798">"Uredi kontakt"</string>
-    <string name="keep_local" msgid="1258761699192993322">"Zadrži na uređaju"</string>
     <string name="add_account" msgid="8201790677994503186">"Dodajte račun"</string>
     <string name="add_new_account" msgid="5748627740680940264">"Dodaj novi račun"</string>
     <string name="menu_export_database" msgid="2659719297530170820">"Izvezi fajlove baze podataka"</string>
@@ -346,4 +356,8 @@
     <string name="permission_explanation_subheader_calendar_and_SMS" msgid="630115334220569184">"Događaji i poruke"</string>
     <string name="permission_explanation_subheader_calendar" msgid="8785323496211704613">"Događaji"</string>
     <string name="permission_explanation_subheader_SMS" msgid="1904552086449525567">"Poruke"</string>
+    <!-- no translation found for hamburger_feature_highlight_header (7442308698936786415) -->
+    <skip />
+    <!-- no translation found for hamburger_feature_highlight_body (6268711111318172098) -->
+    <skip />
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index f5bf4cc..3efa80f 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -81,6 +81,12 @@
     <!-- Color of background of all empty states. -->
     <color name="empty_state_background">#efefef</color>
 
+    <!-- Colors of swipeRefreshLayout's spinning circle. -->
+    <color name="swipe_refresh_color1">#0f9d58</color>
+    <color name="swipe_refresh_color2">#dd4b37</color>
+    <color name="swipe_refresh_color3">#4285f4</color>
+    <color name="swipe_refresh_color4">#f4b400</color>
+
     <!-- Color of hamburger icon in promo -->
     <color name="hamburger_feature_highlight_inner_color">#00ffffff</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a73e449..36ada5a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -295,6 +295,9 @@
     <!-- Minimum height for group name EditText -->
     <dimen name="group_name_edit_text_min_height">48dp</dimen>
 
+    <!-- Distance to pull down before causing a refresh. -->
+    <dimen name="pull_to_refresh_distance">40dp</dimen>
+
     <!-- Elevation of contact list header -->
     <dimen name="contact_list_header_elevation">2dp</dimen>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7a81940..132064c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -369,6 +369,9 @@
     <!-- Toast displayed when saving a label failed [CHAR LIMIT=70] -->
     <string name="groupSavedErrorToast">Couldn\'t save label changes.</string>
 
+    <!-- Message displayed when creating a group with the same name as an existing group -->
+    <string name="groupExistsErrorMessage">That label already exists</string>
+
     <!-- Displayed at the top of the contacts showing the total number of contacts visible when "Only contacts with phones" is selected -->
     <plurals name="listTotalPhoneContacts">
         <item quantity="one">1 contact with phone number</item>
@@ -632,9 +635,6 @@
     Initiates a contact import dialog [CHAR LIMIT=128] -->
     <string name="contacts_unavailable_import_contacts">Import</string>
 
-    <!-- Title of the dialog that allows creation of a contact label [CHAR LIMIT=50] -->
-    <string name="create_group_dialog_title">Create new label</string>
-
     <!-- An item in the popup list of labels that triggers creation of a contact label [CHAR LIMIT=128] -->
     <string name="create_group_item_label">Create new&#8230;</string>
 
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index e4eef51..bfdc2fb 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -88,8 +88,7 @@
         AccountFiltersListener,
         GroupsListener,
         NavigationView.OnNavigationItemSelectedListener,
-        SelectAccountDialogFragment.Listener,
-        GroupNameEditDialogFragment.Listener {
+        SelectAccountDialogFragment.Listener {
 
     protected static String TAG = "ContactsDrawerActivity";
 
@@ -650,26 +649,11 @@
     @Override
     public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) {
         mNewGroupAccount = account;
-        GroupNameEditDialogFragment.showInsertDialog(
-                getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG);
+        GroupNameEditDialogFragment.newInstanceForCreation(mNewGroupAccount, ACTION_CREATE_GROUP)
+                .show(getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG);
     }
 
     @Override
     public void onAccountSelectorCancelled() {
     }
-
-    @Override
-    public void onGroupNameEdit(String groupName, boolean isInsert) {
-        if (mNewGroupAccount == null) {
-            Toast.makeText(this, R.string.groupCreateFailedToast, Toast.LENGTH_SHORT).show();
-            return;
-        }
-        startService(ContactSaveService.createNewGroupIntent(this,
-                mNewGroupAccount, groupName, /* rawContactsToAdd */ null, getClass(),
-                ACTION_CREATE_GROUP));
-    }
-
-    @Override
-    public void onGroupNameEditCancelled() {
-    }
 }
diff --git a/src/com/android/contacts/activities/GroupMembersActivity.java b/src/com/android/contacts/activities/GroupMembersActivity.java
index 093c8b6..68f2f44 100644
--- a/src/com/android/contacts/activities/GroupMembersActivity.java
+++ b/src/com/android/contacts/activities/GroupMembersActivity.java
@@ -36,6 +36,7 @@
 import com.android.contacts.common.logging.ListEvent;
 import com.android.contacts.common.logging.Logger;
 import com.android.contacts.common.logging.ScreenEvent.ScreenType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.group.GroupMembersFragment;
 import com.android.contacts.group.GroupMetadata;
@@ -53,8 +54,7 @@
 public class GroupMembersActivity extends ContactsDrawerActivity implements
         ActionBarAdapter.Listener,
         MultiSelectContactsListFragment.OnCheckBoxListActionListener,
-        GroupMembersFragment.GroupMembersListener,
-        GroupNameEditDialogFragment.Listener {
+        GroupMembersFragment.GroupMembersListener {
 
     private static final String TAG = "GroupMembers";
 
@@ -382,8 +382,11 @@
                 return true;
             }
             case R.id.menu_rename_group: {
-                GroupNameEditDialogFragment.showUpdateDialog(
-                        getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG, mGroupMetadata.groupName);
+                GroupNameEditDialogFragment.newInstanceForUpdate(
+                        new AccountWithDataSet(mGroupMetadata.accountName,
+                                mGroupMetadata.accountType, mGroupMetadata.dataSet),
+                        ACTION_UPDATE_GROUP, mGroupMetadata.groupId, mGroupMetadata.groupName)
+                        .show(getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG);
                 return true;
             }
             case R.id.menu_delete_group: {
@@ -567,23 +570,6 @@
         mActionBarAdapter.setSelectionMode(false);
     }
 
-    // GroupNameEditDialogFragment.Listener callbacks
-
-    @Override
-    public void onGroupNameEdit(String groupName, boolean isInsert) {
-        if (isInsert) {
-            super.onGroupNameEdit(groupName, isInsert);
-            return;
-        }
-        startService(ContactSaveService.createGroupRenameIntent(this,
-                mGroupMetadata.groupId, groupName, GroupMembersActivity.class,
-                ACTION_UPDATE_GROUP));
-    }
-
-    @Override
-    public void onGroupNameEditCancelled() {
-    }
-
     // GroupMembersFragment callbacks
 
     @Override
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 2e32e11..84b6685 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -21,13 +21,16 @@
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.Intent;
+import android.content.SyncStatusObserver;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.Parcelable;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents;
@@ -37,6 +40,7 @@
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
+import android.support.v4.widget.SwipeRefreshLayout;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyCharacterMap;
@@ -55,6 +59,7 @@
 import com.android.contacts.ContactsDrawerActivity;
 import com.android.contacts.R;
 import com.android.contacts.activities.ActionBarAdapter.TabState;
+import com.android.contacts.common.Experiments;
 import com.android.contacts.common.activity.RequestPermissionsActivity;
 import com.android.contacts.common.compat.CompatUtils;
 import com.android.contacts.common.interactions.ImportExportDialogFragment;
@@ -74,6 +79,7 @@
 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.commonbind.experiments.Flags;
 import com.android.contacts.editor.EditorIntents;
 import com.android.contacts.interactions.ContactDeletionInteraction;
 import com.android.contacts.interactions.ContactMultiDeletionInteraction;
@@ -88,6 +94,7 @@
 import com.android.contacts.quickcontact.QuickContactActivity;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.util.SharedPreferenceUtil;
+import com.android.contacts.util.SyncUtil;
 import com.google.android.libraries.material.featurehighlight.FeatureHighlight;
 
 import java.util.List;
@@ -166,6 +173,54 @@
     private final int mInstanceId;
     private static final AtomicInteger sNextInstanceId = new AtomicInteger();
 
+    private Object mStatusChangeListenerHandle;
+
+    private final Handler mHandler = new Handler();
+
+    private SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() {
+        public void onStatusChanged(int which) {
+            mHandler.post(new Runnable() {
+                public void run() {
+                    onSyncStateUpdated();
+                }
+            });
+        }
+    };
+
+    // Update sync status for accounts in current ContactListFilter
+    private void onSyncStateUpdated() {
+        if (mActionBarAdapter.isSearchMode() || mActionBarAdapter.isSelectionMode()) {
+            return;
+        }
+
+        final ContactListFilter filter = mContactListFilterController.getFilter();
+        if (filter != null) {
+            final SwipeRefreshLayout swipeRefreshLayout = mAllFragment.getSwipeRefreshLayout();
+            if (swipeRefreshLayout == null) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Can not load swipeRefreshLayout, swipeRefreshLayout is null");
+                }
+                return;
+            }
+
+            final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(this)
+                    .getAccounts(/* contactsWritableOnly */ true);
+            final List<Account> syncableAccounts = filter.getSyncableAccounts(accounts);
+            // If one of the accounts is active or pending, use spinning circle to indicate one of
+            // the syncs is in progress.
+            if (syncableAccounts != null && syncableAccounts.size() > 0) {
+                for (Account account: syncableAccounts) {
+                    if (SyncUtil.isSyncStatusPendingOrActive(account)
+                            || SyncUtil.isUnsyncableGoogleAccount(account)) {
+                        swipeRefreshLayout.setRefreshing(true);
+                        return;
+                    }
+                }
+            }
+            swipeRefreshLayout.setRefreshing(false);
+        }
+    }
+
     public PeopleActivity() {
         mInstanceId = sNextInstanceId.getAndIncrement();
         mIntentResolver = new ContactsIntentResolver(this);
@@ -412,6 +467,11 @@
         mOptionsMenuContactsAvailable = false;
         mProviderStatusWatcher.stop();
         super.onPause();
+
+        if (Flags.getInstance(this).getBoolean(Experiments.PULL_TO_REFRESH)) {
+            ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
+            onSyncStateUpdated();
+        }
     }
 
     @Override
@@ -431,6 +491,15 @@
         // Current tab may have changed since the last onSaveInstanceState().  Make sure
         // the actual contents match the tab.
         updateFragmentsVisibility();
+
+        if (Flags.getInstance(this).getBoolean(Experiments.PULL_TO_REFRESH)) {
+            mStatusChangeListenerHandle = ContentResolver.addStatusChangeListener(
+                    ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE
+                            | ContentResolver.SYNC_OBSERVER_TYPE_PENDING
+                            | ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+                    mSyncStatusObserver);
+            onSyncStateUpdated();
+        }
         maybeShowHamburgerFeatureHighlight();
     }
 
@@ -583,6 +652,10 @@
                 maybeShowHamburgerFeatureHighlight();
                 invalidateOptionsMenu();
                 showFabWithAnimation(shouldShowFabForAccount());
+                // Determine whether the account has pullToRefresh feature
+                if (Flags.getInstance(this).getBoolean(Experiments.PULL_TO_REFRESH)) {
+                    setSwipeRefreshLayoutEnabledOrNot(mContactListFilterController.getFilter());
+                }
                 break;
             case ActionBarAdapter.Listener.Action.CHANGE_SEARCH_QUERY:
                 final String queryString = mActionBarAdapter.getQueryString();
@@ -1461,6 +1534,33 @@
                         .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             }
         }
+
+        // Determine whether the account has pullToRefresh feature
+        if (Flags.getInstance(this).getBoolean(Experiments.PULL_TO_REFRESH)) {
+            setSwipeRefreshLayoutEnabledOrNot(filter);
+        }
+    }
+
+    private void setSwipeRefreshLayoutEnabledOrNot(ContactListFilter filter) {
+        final SwipeRefreshLayout swipeRefreshLayout = mAllFragment.getSwipeRefreshLayout();
+        if (swipeRefreshLayout == null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Can not load swipeRefreshLayout, swipeRefreshLayout is null");
+            }
+            return;
+        }
+
+        swipeRefreshLayout.setRefreshing(false);
+        swipeRefreshLayout.setEnabled(false);
+
+        if (filter != null && !mActionBarAdapter.isSearchMode()
+                && !mActionBarAdapter.isSelectionMode()) {
+            final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(this)
+                    .getAccounts(/* contactsWritableOnly */ true);
+            if (filter.isSyncable(accounts)) {
+                swipeRefreshLayout.setEnabled(true);
+            }
+        }
     }
 
     private String getActionBarTitleForAccount(ContactListFilter filter) {
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index d41991c..ba0c2c6 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -38,9 +38,9 @@
 
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
+import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.model.dataitem.DataKind;
-import com.android.contacts.interactions.GroupCreationDialogFragment;
-import com.android.contacts.interactions.GroupCreationDialogFragment.OnGroupCreatedListener;
+import com.android.contacts.group.GroupNameEditDialogFragment;
 import com.android.contacts.common.model.RawContactDelta;
 import com.android.contacts.common.model.ValuesDelta;
 import com.android.contacts.common.model.RawContactModifier;
@@ -56,6 +56,8 @@
 public class GroupMembershipView extends LinearLayout
         implements OnClickListener, OnItemClickListener {
 
+    public static final String TAG_CREATE_GROUP_FRAGMENT = "createGroupDialog";
+
     private static final int CREATE_NEW_GROUP_GROUP_ID = 133;
 
     public static final class GroupSelectionItem {
@@ -449,17 +451,23 @@
         UiClosables.closeQuietly(mPopup);
         mPopup = null;
 
-        GroupCreationDialogFragment.show(
+        final GroupNameEditDialogFragment dialog =
+                GroupNameEditDialogFragment.newInstanceForCreation(
+                        new AccountWithDataSet(mAccountName, mAccountType, mDataSet), null);
+
+        // If the device is rotated after the dialog is shown, the listener will become null,
+        // so that the popup from GroupMembershipView will not be shown.
+        dialog.setListener(new GroupNameEditDialogFragment.Listener() {
+            @Override
+            public void onGroupNameEditStarted(String groupName) {
+                mCreatedNewGroup = true;
+            }
+            @Override
+            public void onGroupNameEditCancelled() { }
+        });
+        dialog.show(
                 ((Activity) getContext()).getFragmentManager(),
-                mAccountType,
-                mAccountName,
-                mDataSet,
-                new OnGroupCreatedListener() {
-                    @Override
-                    public void onGroupCreated() {
-                        mCreatedNewGroup = true;
-                    }
-                });
+                TAG_CREATE_GROUP_FRAGMENT);
     }
 
 }
diff --git a/src/com/android/contacts/group/GroupNameEditDialogFragment.java b/src/com/android/contacts/group/GroupNameEditDialogFragment.java
index 235a878..b2bfd0b 100644
--- a/src/com/android/contacts/group/GroupNameEditDialogFragment.java
+++ b/src/com/android/contacts/group/GroupNameEditDialogFragment.java
@@ -17,11 +17,18 @@
 
 import android.app.Dialog;
 import android.app.DialogFragment;
-import android.app.FragmentManager;
+import android.app.LoaderManager;
 import android.content.Context;
+import android.content.CursorLoader;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.support.design.widget.TextInputLayout;
 import android.support.v7.app.AlertDialog;
 import android.text.Editable;
 import android.text.TextUtils;
@@ -33,60 +40,99 @@
 import android.widget.EditText;
 import android.widget.TextView;
 
+import com.android.contacts.ContactSaveService;
 import com.android.contacts.R;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.google.common.base.Strings;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Edits the name of a group.
  */
-public final class GroupNameEditDialogFragment extends DialogFragment {
+public final class GroupNameEditDialogFragment extends DialogFragment implements
+        LoaderManager.LoaderCallbacks<Cursor> {
 
-    private static final String KEY_IS_INSERT = "isInsert";
     private static final String KEY_GROUP_NAME = "groupName";
 
     private static final String ARG_IS_INSERT = "isInsert";
     private static final String ARG_GROUP_NAME = "groupName";
+    private static final String ARG_ACCOUNT = "account";
+    private static final String ARG_CALLBACK_ACTION = "callbackAction";
+    private static final String ARG_GROUP_ID = "groupId";
+
+    private static final long NO_GROUP_ID = -1;
+
 
     /** Callbacks for hosts of the {@link GroupNameEditDialogFragment}. */
     public interface Listener {
-        void onGroupNameEdit(String groupName, boolean isInsert);
+        void onGroupNameEditStarted(String name);
         void onGroupNameEditCancelled();
+
+        public static final Listener None = new Listener() {
+            @Override
+            public void onGroupNameEditStarted(String name) { }
+
+            @Override
+            public void onGroupNameEditCancelled() { }
+        };
     }
 
     private boolean mIsInsert;
     private String mGroupName;
+    private long mGroupId;
+    private Listener mListener;
+    private AccountWithDataSet mAccount;
     private EditText mGroupNameEditText;
+    private TextInputLayout mGroupNameTextLayout;
+    private Set<String> mExistingGroups = Collections.emptySet();
 
-    public static void showInsertDialog(FragmentManager fragmentManager, String tag) {
-        showDialog(fragmentManager, tag, /* isInsert */ true, /* groupName */ null);
+    public static GroupNameEditDialogFragment newInstanceForCreation(
+            AccountWithDataSet account, String callbackAction) {
+        return newInstance(account, callbackAction, NO_GROUP_ID, null);
     }
 
-    public static void showUpdateDialog(FragmentManager fragmentManager,
-            String tag, String groupName) {
-        showDialog(fragmentManager, tag, /* isInsert */ false, groupName);
+    public static GroupNameEditDialogFragment newInstanceForUpdate(
+            AccountWithDataSet account, String callbackAction, long groupId, String groupName) {
+        return newInstance(account, callbackAction, groupId, groupName);
     }
 
-    private static void showDialog(FragmentManager fragmentManager,
-            String tag, boolean isInsert, String groupName) {
+    private static GroupNameEditDialogFragment newInstance(
+            AccountWithDataSet account, String callbackAction, long groupId, String groupName) {
+        if (account == null || account.name == null || account.type == null) {
+            throw new IllegalArgumentException("Invalid account");
+        }
+        final boolean isInsert = groupId == NO_GROUP_ID;
         final Bundle args = new Bundle();
         args.putBoolean(ARG_IS_INSERT, isInsert);
+        args.putLong(ARG_GROUP_ID, groupId);
         args.putString(ARG_GROUP_NAME, groupName);
+        args.putParcelable(ARG_ACCOUNT, account);
+        args.putString(ARG_CALLBACK_ACTION, callbackAction);
 
         final GroupNameEditDialogFragment dialog = new GroupNameEditDialogFragment();
         dialog.setArguments(args);
-        dialog.show(fragmentManager, tag);
+        return dialog;
     }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setStyle(STYLE_NORMAL, R.style.ContactsAlertDialogThemeAppCompat);
+        final Bundle args = getArguments();
         if (savedInstanceState == null) {
-            final Bundle args = getArguments();
-            mIsInsert = args.getBoolean(KEY_IS_INSERT);
             mGroupName = args.getString(KEY_GROUP_NAME);
         } else {
-            mIsInsert = savedInstanceState.getBoolean(ARG_IS_INSERT);
             mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
         }
+        mGroupId = args.getLong(ARG_GROUP_ID, NO_GROUP_ID);
+        mIsInsert = args.getBoolean(ARG_IS_INSERT, true);
+        mAccount = getArguments().getParcelable(ARG_ACCOUNT);
+
+        // There is only one loader so the id arg doesn't matter.
+        getLoaderManager().initLoader(0, null, this);
     }
 
     @Override
@@ -96,7 +142,7 @@
         title.setText(mIsInsert
                 ? R.string.group_name_dialog_insert_title
                 : R.string.group_name_dialog_update_title);
-        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), getTheme())
                 .setCustomTitle(title)
                 .setView(R.layout.group_name_edit_dialog)
                 .setNegativeButton(android.R.string.cancel, new OnClickListener() {
@@ -107,12 +153,9 @@
                         dismiss();
                     }
                 })
-                .setPositiveButton(android.R.string.ok, new OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        getListener().onGroupNameEdit(getGroupName(), mIsInsert);
-                    }
-                });
+                // The Positive button listener is defined below in the OnShowListener to
+                // allow for input validation
+                .setPositiveButton(android.R.string.ok, null);
 
         // Disable the create button when the name is empty
         final AlertDialog alertDialog = builder.create();
@@ -122,6 +165,8 @@
             @Override
             public void onShow(DialogInterface dialog) {
                 mGroupNameEditText = (EditText) alertDialog.findViewById(android.R.id.text1);
+                mGroupNameTextLayout =
+                        (TextInputLayout) alertDialog.findViewById(R.id.text_input_layout);
                 if (!TextUtils.isEmpty(mGroupName)) {
                     mGroupNameEditText.setText(mGroupName);
                     // Guard against already created group names that are longer than the max
@@ -134,6 +179,14 @@
 
                 final Button createButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
                 createButton.setEnabled(!TextUtils.isEmpty(getGroupName()));
+
+                // Override the click listener to prevent dismissal if creating a duplicate group.
+                createButton.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        maybePersistCurrentGroupName(v);
+                    }
+                });
                 mGroupNameEditText.addTextChangedListener(new TextWatcher() {
                     @Override
                     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -145,6 +198,7 @@
 
                     @Override
                     public void afterTextChanged(Editable s) {
+                        mGroupNameTextLayout.setError(null);
                         createButton.setEnabled(!TextUtils.isEmpty(s));
                     }
                 });
@@ -154,6 +208,56 @@
         return alertDialog;
     }
 
+    /**
+     * Sets the listener for the rename
+     *
+     * Setting a listener on a fragment is error prone since it will be lost if the fragment
+     * is recreated. This exists because it is used from a view class (GroupMembersView) which
+     * needs to modify it's state when this fragment updates the name.
+     *
+     * @param listener the listener. can be null
+     */
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    private boolean hasNameChanged() {
+        final String name = Strings.nullToEmpty(getGroupName());
+        final String originalName = getArguments().getString(ARG_GROUP_NAME);
+        return (mIsInsert && !name.isEmpty()) || !name.equals(originalName);
+    }
+
+    private void maybePersistCurrentGroupName(View button) {
+        if (!hasNameChanged()) {
+            dismiss();
+            return;
+        }
+        final String name = getGroupName();
+        // Note we don't check if the loader finished populating mExistingGroups. It's not the
+        // end of the world if the user ends up with a duplicate group and in practice it should
+        // never really happen (the query should complete much sooner than the user can edit the
+        // label)
+        if (mExistingGroups.contains(name)) {
+            mGroupNameTextLayout.setError(
+                    getString(R.string.groupExistsErrorMessage));
+            button.setEnabled(false);
+            return;
+        }
+        final String callbackAction = getArguments().getString(ARG_CALLBACK_ACTION);
+        final Intent serviceIntent;
+        if (mIsInsert) {
+            serviceIntent = ContactSaveService.createNewGroupIntent(getActivity(),
+                    new AccountWithDataSet(mAccount.name, mAccount.type, mAccount.dataSet),
+                    name, null, getActivity().getClass(), callbackAction);
+        } else {
+            serviceIntent = ContactSaveService.createGroupRenameIntent(getActivity(), mGroupId,
+                    name, getActivity().getClass(), callbackAction);
+        }
+        ContactSaveService.startService(getActivity(), serviceIntent);
+        getListener().onGroupNameEditStarted(name);
+        dismiss();
+    }
+
     @Override
     public void onCancel(DialogInterface dialog) {
         super.onCancel(dialog);
@@ -163,10 +267,61 @@
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putBoolean(KEY_IS_INSERT, mIsInsert);
         outState.putString(KEY_GROUP_NAME, getGroupName());
     }
 
+    @Override
+    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+        // Only a single loader so id is ignored.
+        return new CursorLoader(getActivity(), GroupNameQuery.URI,
+                GroupNameQuery.PROJECTION, GroupNameQuery.getSelection(mAccount),
+                GroupNameQuery.getSelectionArgs(mAccount), null);
+    }
+
+    @Override
+    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+        mExistingGroups = new HashSet<>();
+        while (data.moveToNext()) {
+            mExistingGroups.add(data.getString(GroupNameQuery.TITLE));
+        }
+    }
+
+    @Override
+    public void onLoaderReset(Loader<Cursor> loader) {
+    }
+
+    /**
+     * Defines the structure of the query performed by the CursorLoader created by
+     * GroupNameEditDialogFragment
+     */
+    private static class GroupNameQuery {
+
+        public static final int TITLE = 0;
+        public static final Uri URI = ContactsContract.Groups.CONTENT_URI;
+        public static final String[] PROJECTION = new String[] { ContactsContract.Groups.TITLE };
+
+        public static String getSelection(AccountWithDataSet account) {
+            final StringBuilder builder = new StringBuilder();
+            builder.append(ContactsContract.Groups.ACCOUNT_NAME).append("=? AND ")
+                    .append(ContactsContract.Groups.ACCOUNT_TYPE).append("=?");
+            if (account.dataSet != null) {
+                builder.append(" AND ").append(ContactsContract.Groups.DATA_SET).append("=?");
+            }
+            return builder.toString();
+        }
+
+        public static String[] getSelectionArgs(AccountWithDataSet account) {
+            final int len = account.dataSet == null ? 2 : 3;
+            final String[] args = new String[len];
+            args[0] = account.name;
+            args[1] = account.type;
+            if (account.dataSet != null) {
+                args[2] = account.dataSet;
+            }
+            return args;
+        }
+    }
+
     private void showInputMethod(View view) {
         final InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(
                 Context.INPUT_METHOD_SERVICE);
@@ -184,11 +339,13 @@
     }
 
     private Listener getListener() {
-        if (!(getActivity() instanceof Listener)) {
-            throw new ClassCastException(getActivity() + " must implement " +
-                    Listener.class.getName());
+        if (mListener != null) {
+            return mListener;
+        } else if (getActivity() instanceof Listener) {
+            return (Listener) getActivity();
+        } else {
+            return Listener.None;
         }
-        return (Listener) getActivity();
     }
 
     private String getGroupName() {
diff --git a/src/com/android/contacts/group/GroupsFragment.java b/src/com/android/contacts/group/GroupsFragment.java
index be1b44a..508c184 100644
--- a/src/com/android/contacts/group/GroupsFragment.java
+++ b/src/com/android/contacts/group/GroupsFragment.java
@@ -18,7 +18,6 @@
 
 import android.app.Fragment;
 import android.app.LoaderManager;
-import android.content.Context;
 import android.content.CursorLoader;
 import android.content.Loader;
 import android.database.Cursor;
diff --git a/src/com/android/contacts/interactions/GroupCreationDialogFragment.java b/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
deleted file mode 100644
index e998688..0000000
--- a/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
+++ /dev/null
@@ -1,100 +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.interactions;
-
-import android.app.Activity;
-import android.app.FragmentManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.EditText;
-
-import com.android.contacts.ContactSaveService;
-import com.android.contacts.R;
-import com.android.contacts.activities.ContactEditorBaseActivity;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-
-/**
- * A dialog for creating a new group.
- */
-public class GroupCreationDialogFragment extends GroupNameDialogFragment {
-    private static final String ARG_ACCOUNT_TYPE = "accountType";
-    private static final String ARG_ACCOUNT_NAME = "accountName";
-    private static final String ARG_DATA_SET = "dataSet";
-
-    public static final String FRAGMENT_TAG = "createGroupDialog";
-
-    private final OnGroupCreatedListener mListener;
-
-    public interface OnGroupCreatedListener {
-        public void onGroupCreated();
-    }
-
-    public static void show(
-            FragmentManager fragmentManager, String accountType, String accountName,
-            String dataSet, OnGroupCreatedListener listener) {
-        GroupCreationDialogFragment dialog = new GroupCreationDialogFragment(listener);
-        Bundle args = new Bundle();
-        args.putString(ARG_ACCOUNT_TYPE, accountType);
-        args.putString(ARG_ACCOUNT_NAME, accountName);
-        args.putString(ARG_DATA_SET, dataSet);
-        dialog.setArguments(args);
-        dialog.show(fragmentManager, FRAGMENT_TAG);
-    }
-
-    public GroupCreationDialogFragment() {
-        super();
-        mListener = null;
-    }
-
-    private GroupCreationDialogFragment(OnGroupCreatedListener listener) {
-        super();
-        mListener = listener;
-    }
-
-    public OnGroupCreatedListener getOnGroupCreatedListener() {
-        return mListener;
-    }
-
-    @Override
-    protected void initializeGroupLabelEditText(EditText editText) {
-    }
-
-    @Override
-    protected int getTitleResourceId() {
-        return R.string.create_group_dialog_title;
-    }
-
-    @Override
-    protected void onCompleted(String groupLabel) {
-        Bundle arguments = getArguments();
-        String accountType = arguments.getString(ARG_ACCOUNT_TYPE);
-        String accountName = arguments.getString(ARG_ACCOUNT_NAME);
-        String dataSet = arguments.getString(ARG_DATA_SET);
-
-        // Indicate to the listener that a new group will be created.
-        // If the device is rotated, mListener will become null, so that the
-        // popup from GroupMembershipView will not be shown.
-        if (mListener != null) {
-            mListener.onGroupCreated();
-        }
-
-        Activity activity = getActivity();
-        activity.startService(ContactSaveService.createNewGroupIntent(activity,
-                new AccountWithDataSet(accountName, accountType, dataSet), groupLabel,
-                null /* no new members to add */,
-                activity.getClass(), ContactEditorBaseActivity.ACTION_EDIT));
-    }
-}
diff --git a/src/com/android/contacts/interactions/GroupNameDialogFragment.java b/src/com/android/contacts/interactions/GroupNameDialogFragment.java
deleted file mode 100644
index 5efccfc..0000000
--- a/src/com/android/contacts/interactions/GroupNameDialogFragment.java
+++ /dev/null
@@ -1,101 +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.interactions;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnShowListener;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.InputFilter;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.EditText;
-
-import com.android.contacts.R;
-
-/**
- * A common superclass for creating and renaming groups.
- */
-// TODO: consolidate it with GroupNameEditDialogFragment
-public abstract class GroupNameDialogFragment extends DialogFragment {
-    protected abstract int getTitleResourceId();
-    protected abstract void initializeGroupLabelEditText(EditText editText);
-    protected abstract void onCompleted(String groupLabel);
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-        final LayoutInflater layoutInflater = LayoutInflater.from(builder.getContext());
-        final View view = layoutInflater.inflate(R.layout.group_name_dialog, null);
-        final EditText editText = (EditText) view.findViewById(R.id.group_label);
-        final int maxLength = getResources().getInteger(R.integer.group_name_max_length);
-        editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxLength) });
-        editText.setInputType(InputType.TYPE_CLASS_TEXT);
-        initializeGroupLabelEditText(editText);
-
-        builder.setTitle(getTitleResourceId());
-        builder.setView(view);
-        editText.requestFocus();
-        builder.setPositiveButton(android.R.string.ok,
-                new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialogInterface, int whichButton) {
-                        onCompleted(editText.getText().toString().trim());
-                    }
-                }
-            );
-
-        builder.setNegativeButton(android.R.string.cancel, null);
-        final AlertDialog dialog = builder.create();
-
-        dialog.setOnShowListener(new OnShowListener() {
-            @Override
-            public void onShow(DialogInterface dialogInterface) {
-                updateOkButtonState(dialog, editText);
-            }
-        });
-        editText.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-            }
-
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                updateOkButtonState(dialog, editText);
-            }
-        });
-        dialog.getWindow().setSoftInputMode(
-                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
-        return dialog;
-    }
-
-    /* package */ void updateOkButtonState(AlertDialog dialog, EditText editText) {
-        final Button okButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
-        okButton.setEnabled(!TextUtils.isEmpty(editText.getText().toString().trim()));
-    }
-}
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 2060068..da33b55 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -92,7 +92,7 @@
     private boolean mSelectionVerified;
     private int mLastSelectedPosition = -1;
     private boolean mRefreshingContactUri;
-    private ContactListFilter mFilter;
+    protected ContactListFilter mFilter;
     private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
 
     protected OnContactBrowserActionListener mListener;
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 1bd09f8..90dbc69 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -15,11 +15,16 @@
  */
 package com.android.contacts.list;
 
+import android.accounts.Account;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.CursorLoader;
 import android.content.Loader;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.support.v4.widget.SwipeRefreshLayout;
 import android.text.TextUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -34,12 +39,18 @@
 
 import com.android.contacts.R;
 import com.android.contacts.activities.PeopleActivity;
+import com.android.contacts.common.Experiments;
 import com.android.contacts.common.list.ContactListAdapter;
 import com.android.contacts.common.list.ContactListFilter;
 import com.android.contacts.common.list.ContactListItemView;
 import com.android.contacts.common.list.DefaultContactListAdapter;
 import com.android.contacts.common.list.FavoritesAndContactsLoader;
+import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.commonbind.experiments.Flags;
+import com.android.contacts.util.SyncUtil;
+
+import java.util.List;
 
 /**
  * Fragment containing a contact list used for browsing (as compared to
@@ -52,6 +63,7 @@
     private View mEmptyHomeView;
     private View mAccountFilterContainer;
     private TextView mSearchProgressText;
+    private SwipeRefreshLayout mSwipeRefreshLayout;
 
     public DefaultContactBrowseListFragment() {
         setPhotoLoaderEnabled(true);
@@ -76,7 +88,9 @@
 
     private void bindListHeader(int numberOfContacts) {
         final ContactListFilter filter = getFilter();
-        if (!isSearchMode() && numberOfContacts <= 0) {
+        // If the phone has at least one Google account whose sync status is unsyncable or pending
+        // or active, we have to make mAccountFilterContainer visible.
+        if (!isSearchMode() && numberOfContacts <= 0 && shouldShowEmptyView(filter)) {
             if (filter != null && filter.isContactsFilterType()) {
                 makeViewVisible(mEmptyHomeView);
             } else {
@@ -99,6 +113,38 @@
         }
     }
 
+    /**
+     * If at least one Google account is unsyncable or its sync status is pending or active, we
+     * should not show empty view even if the number of contacts is 0. We should show sync status
+     * with empty list instead.
+     */
+    private boolean shouldShowEmptyView(ContactListFilter filter) {
+        if (filter == null) {
+            return true;
+        }
+        // TODO(samchen) : Check ContactListFilter.FILTER_TYPE_CUSTOM
+        if (ContactListFilter.FILTER_TYPE_DEFAULT == filter.filterType
+                || ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS == filter.filterType) {
+            final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(getContext())
+                    .getAccounts(/* contactsWritableOnly */ true);
+            final List<Account> syncableAccounts = filter.getSyncableAccounts(accounts);
+
+            if (syncableAccounts != null && syncableAccounts.size() > 0) {
+                for (Account account : syncableAccounts) {
+                    if (SyncUtil.isSyncStatusPendingOrActive(account)
+                            || SyncUtil.isUnsyncableGoogleAccount(account)) {
+                        return false;
+                    }
+                }
+            }
+        } else if (ContactListFilter.FILTER_TYPE_ACCOUNT == filter.filterType) {
+            final Account account = new Account(filter.accountName, filter.accountType);
+            return !(SyncUtil.isSyncStatusPendingOrActive(account)
+                    || SyncUtil.isUnsyncableGoogleAccount(account));
+        }
+        return true;
+    }
+
     // Show the view that's specified by id and hide the other two.
     private void makeViewVisible(View view) {
         mEmptyAccountView.setVisibility(view == mEmptyAccountView ? View.VISIBLE : View.GONE);
@@ -200,6 +246,10 @@
     protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
         super.onCreateView(inflater, container);
 
+        if (Flags.getInstance(getActivity()).getBoolean(Experiments.PULL_TO_REFRESH)) {
+            initSwipeRefreshLayout();
+
+        }
         // Putting the header view inside a container will allow us to make
         // it invisible later. See checkHeaderViewVisibility()
         FrameLayout headerContainer = new FrameLayout(inflater.getContext());
@@ -212,6 +262,55 @@
         mSearchProgressText = (TextView) mSearchHeaderView.findViewById(R.id.totalContactsText);
     }
 
+    private void initSwipeRefreshLayout() {
+        mSwipeRefreshLayout = (SwipeRefreshLayout) mView.findViewById(R.id.swipe_refresh);
+        if (mSwipeRefreshLayout == null) {
+            return;
+        }
+
+        mSwipeRefreshLayout.setEnabled(true);
+        // Request sync contacts
+        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                syncContacts(mFilter);
+            }
+        });
+        mSwipeRefreshLayout.setColorSchemeResources(
+                R.color.swipe_refresh_color1,
+                R.color.swipe_refresh_color2,
+                R.color.swipe_refresh_color3,
+                R.color.swipe_refresh_color4);
+        mSwipeRefreshLayout.setDistanceToTriggerSync(
+                (int) getResources().getDimension(R.dimen.pull_to_refresh_distance));
+    }
+
+    /**
+     * Request sync for the Google accounts (not include Google+ accounts) specified by the given
+     * filter.
+     */
+    private void syncContacts(ContactListFilter filter) {
+        if (filter == null) {
+            return;
+        }
+        final Bundle bundle = new Bundle();
+        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+
+        final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(
+                getActivity()).getAccounts(/* contactsWritableOnly */ true);
+        final List<Account> syncableAccounts = filter.getSyncableAccounts(accounts);
+        if (syncableAccounts != null && syncableAccounts.size() > 0) {
+            for (Account account : syncableAccounts) {
+                 // We can prioritize Contacts sync if sync is not initialized yet.
+                if (!SyncUtil.isSyncStatusPendingOrActive(account)
+                        || SyncUtil.isUnsyncableGoogleAccount(account)) {
+                    ContentResolver.requestSync(account, ContactsContract.AUTHORITY, bundle);
+                }
+            }
+        }
+    }
+
     @Override
     protected void setSearchMode(boolean flag) {
         super.setSearchMode(flag);
@@ -260,4 +359,8 @@
             }
         }
     }
+
+    public SwipeRefreshLayout getSwipeRefreshLayout() {
+        return mSwipeRefreshLayout;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/contacts/util/SyncUtil.java b/src/com/android/contacts/util/SyncUtil.java
new file mode 100644
index 0000000..cef2223
--- /dev/null
+++ b/src/com/android/contacts/util/SyncUtil.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import android.accounts.Account;
+import android.content.ContentResolver;
+import android.provider.ContactsContract;
+
+import com.android.contacts.common.model.account.GoogleAccountType;
+
+import java.util.List;
+
+/**
+ * Utilities related to sync.
+ */
+public final class SyncUtil {
+    private static final String TAG = "SyncUtil";
+
+    private SyncUtil() {
+    }
+
+    public static final boolean isSyncStatusPendingOrActive(Account account) {
+        if (account == null) {
+            return false;
+        }
+        return ContentResolver.isSyncPending(account, ContactsContract.AUTHORITY)
+                || ContentResolver.isSyncActive(account, ContactsContract.AUTHORITY);
+    }
+
+    /**
+     * Returns true if the given Google account is not syncable.
+     */
+    public static final boolean isUnsyncableGoogleAccount(Account account) {
+        if (account == null || !GoogleAccountType.ACCOUNT_TYPE.equals(account.type)) {
+            return false;
+        }
+        return ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) <= 0;
+    }
+}
diff --git a/tests/src/com/android/contacts/util/SyncUtilTests.java b/tests/src/com/android/contacts/util/SyncUtilTests.java
new file mode 100644
index 0000000..372a652
--- /dev/null
+++ b/tests/src/com/android/contacts/util/SyncUtilTests.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import android.accounts.Account;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for SyncUtil.
+ */
+@SmallTest
+public class SyncUtilTests extends AndroidTestCase {
+    private static final String TAG = "SyncUtilTests";
+
+    private static final String GOOGLE_TYPE = "com.google";
+    private static final String NOT_GOOGLE_TYPE = "com.abc";
+    private static final String ACCOUNT_NAME = "ACCOUNT_NAME";
+
+    private final Account mGoogleAccount;
+    private final Account mOtherAccount;
+
+    public SyncUtilTests() {
+        mGoogleAccount = new Account(ACCOUNT_NAME, GOOGLE_TYPE);
+        mOtherAccount = new Account(ACCOUNT_NAME, NOT_GOOGLE_TYPE);
+    }
+
+    public void testIsUnsyncableGoogleAccount() throws Exception {
+        // The account names of mGoogleAccount and mOtherAccount are not valid, so both accounts
+        // are not syncable.
+        assertTrue(SyncUtil.isUnsyncableGoogleAccount(mGoogleAccount));
+        assertFalse(SyncUtil.isUnsyncableGoogleAccount(mOtherAccount));
+        assertFalse(SyncUtil.isUnsyncableGoogleAccount(null));
+    }
+}