Merge "Import translations. DO NOT MERGE"
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index 6ae6a59..e4eef51 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -71,6 +71,7 @@
 import com.android.contacts.interactions.AccountFiltersFragment;
 import com.android.contacts.interactions.AccountFiltersFragment.AccountFiltersListener;
 import com.android.contacts.quickcontact.QuickContactActivity;
+import com.android.contacts.util.SharedPreferenceUtil;
 import com.android.contactsbind.Assistants;
 import com.android.contactsbind.HelpUtils;
 
@@ -111,6 +112,8 @@
     private class ContactsActionBarDrawerToggle extends ActionBarDrawerToggle {
 
         private Runnable mRunnable;
+        private boolean mMenuClickedBefore = SharedPreferenceUtil.getHamburgerMenuClickedBefore(
+                ContactsDrawerActivity.this);
 
         public ContactsActionBarDrawerToggle(AppCompatActivity activity, DrawerLayout drawerLayout,
                 Toolbar toolbar, int openDrawerContentDescRes, int closeDrawerContentDescRes) {
@@ -121,6 +124,10 @@
         @Override
         public void onDrawerOpened(View drawerView) {
             super.onDrawerOpened(drawerView);
+            if (!mMenuClickedBefore) {
+                SharedPreferenceUtil.setHamburgerMenuClickedBefore(ContactsDrawerActivity.this);
+                mMenuClickedBefore = true;
+            }
             invalidateOptionsMenu();
         }
 
@@ -217,7 +224,7 @@
             clearCheckedMenus();
             mIdMenuMap.get(R.id.nav_find_duplicates).setCheckable(true);
             mIdMenuMap.get(R.id.nav_find_duplicates).setChecked(true);
-            updateScrollPosition(DUPLICATES_POSITION);
+            maybeUpdateScrollPosition(DUPLICATES_POSITION);
         }
 
         if (savedState != null && savedState.containsKey(KEY_NEW_GROUP_ACCOUNT)) {
@@ -226,7 +233,11 @@
         }
     }
 
-    private void updateScrollPosition(int position) {
+    private void maybeUpdateScrollPosition(int position) {
+        if (mDrawer.isDrawerOpen(GravityCompat.START)) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Don't scroll menu when drawer open");
+            return;
+        }
         final RecyclerView recyclerView = (RecyclerView) mNavigationView.getChildAt(0);
         final LinearLayoutManager layoutManager =
                 (LinearLayoutManager) recyclerView.getLayoutManager();
@@ -409,7 +420,7 @@
                 && mGroupMenuMap.get(groupMetadata.groupId) != null) {
             mGroupMenuMap.get(groupMetadata.groupId).setCheckable(true);
             mGroupMenuMap.get(groupMetadata.groupId).setChecked(true);
-            updateScrollPosition(mGroupMenuMap.get(groupMetadata.groupId).getOrder());
+            maybeUpdateScrollPosition(mGroupMenuMap.get(groupMetadata.groupId).getOrder());
         }
     }
 
@@ -513,13 +524,13 @@
             if (mIdMenuMap != null && mIdMenuMap.get(R.id.nav_all_contacts) != null) {
                 mIdMenuMap.get(R.id.nav_all_contacts).setCheckable(true);
                 mIdMenuMap.get(R.id.nav_all_contacts).setChecked(true);
-                updateScrollPosition(ALL_CONTACTS_POSITION);
+                maybeUpdateScrollPosition(ALL_CONTACTS_POSITION);
             }
         } else {
             if (mFilterMenuMap != null && mFilterMenuMap.get(filter) != null) {
                 mFilterMenuMap.get(filter).setCheckable(true);
                 mFilterMenuMap.get(filter).setChecked(true);
-                updateScrollPosition(mFilterMenuMap.get(filter).getOrder());
+                maybeUpdateScrollPosition(mFilterMenuMap.get(filter).getOrder());
             }
         }
     }
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 0ee83df..cc2428e 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -85,9 +85,6 @@
 
     private static final String PERSISTENT_LAST_TAB = "actionBarAdapter.lastTab";
 
-    private static final String PREFERENCE_KEY_SHOULD_SHOW_HAMBURGER_FEATURE_HIGHLIGHT =
-            "shouldShowHamburgerFeatureHighlight";
-
     private boolean mSelectionMode;
     private boolean mSearchMode;
     private String mQueryString;
@@ -239,7 +236,6 @@
             mQueryString = request.getQueryString();
             mCurrentTab = loadLastTabPreference();
             mSelectionMode = false;
-            addHamburgerHighlight();
         } else {
             mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
             mSelectionMode = savedState.getBoolean(EXTRA_KEY_SELECTED_MODE);
@@ -260,6 +256,7 @@
         if (mSearchMode && !TextUtils.isEmpty(mQueryString)) {
             setQueryString(mQueryString);
         }
+        addHamburgerFeatureHighlight();
     }
 
     public void setListener(Listener listener) {
@@ -434,7 +431,7 @@
         }
     }
 
-    private void addHamburgerHighlight() {
+    private void addHamburgerFeatureHighlight() {
         if (mHamburgerFeatureHighlight == null) {
             mHamburgerFeatureHighlight = FeatureHighlight.Builder
                     .forView(new ToolbarNavigationIconFinder())
@@ -452,19 +449,6 @@
         return mHamburgerFeatureHighlight;
     }
 
-    public boolean shouldShowHamburgerFeatureHighlight(Context context) {
-        final SharedPreferences prefs = context.getSharedPreferences(
-                context.getPackageName(), Context.MODE_PRIVATE);
-        return prefs.getBoolean(PREFERENCE_KEY_SHOULD_SHOW_HAMBURGER_FEATURE_HIGHLIGHT, true);
-    }
-
-    public void setHamburgerFeatureHighlightShown(Context context) {
-        final SharedPreferences prefs = context.getSharedPreferences(
-                context.getPackageName(), Context.MODE_PRIVATE);
-        prefs.edit().putBoolean(PREFERENCE_KEY_SHOULD_SHOW_HAMBURGER_FEATURE_HIGHLIGHT, false)
-                .apply();
-    }
-
     private void update(boolean skipAnimation) {
         updateStatusBarColor();
 
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index a29b98d..84b6685 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -93,6 +93,7 @@
 import com.android.contacts.list.OnContactsUnavailableActionListener;
 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;
 
@@ -209,7 +210,8 @@
             // the syncs is in progress.
             if (syncableAccounts != null && syncableAccounts.size() > 0) {
                 for (Account account: syncableAccounts) {
-                    if (SyncUtil.isSyncStatusPendingOrActive(account)) {
+                    if (SyncUtil.isSyncStatusPendingOrActive(account)
+                            || SyncUtil.isUnsyncableGoogleAccount(account)) {
                         swipeRefreshLayout.setRefreshing(true);
                         return;
                     }
@@ -291,13 +293,16 @@
             Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate finish");
         }
         getWindow().setBackgroundDrawable(null);
+    }
 
-        if (mActionBarAdapter.shouldShowHamburgerFeatureHighlight(this)) {
+    private void maybeShowHamburgerFeatureHighlight() {
+        if (!mActionBarAdapter.isSearchMode() && !mActionBarAdapter.isSelectionMode()
+                && SharedPreferenceUtil.getShouldShowHamburgerPromo(this)) {
             final FeatureHighlight hamburgerFeatureHighlight =
                     mActionBarAdapter.getHamburgerFeatureHighlight();
             if (hamburgerFeatureHighlight != null) {
                 hamburgerFeatureHighlight.show(this);
-                mActionBarAdapter.setHamburgerFeatureHighlightShown(this);
+                SharedPreferenceUtil.setHamburgerPromoDisplayedBefore(this);
             }
         }
     }
@@ -495,6 +500,7 @@
                     mSyncStatusObserver);
             onSyncStateUpdated();
         }
+        maybeShowHamburgerFeatureHighlight();
     }
 
     @Override
@@ -643,6 +649,7 @@
             case ActionBarAdapter.Listener.Action.STOP_SEARCH_AND_SELECTION_MODE:
                 setQueryTextToFragment("");
                 updateFragmentsVisibility();
+                maybeShowHamburgerFeatureHighlight();
                 invalidateOptionsMenu();
                 showFabWithAnimation(shouldShowFabForAccount());
                 // Determine whether the account has pullToRefresh feature
@@ -666,6 +673,9 @@
         updateFragmentsVisibility();
         invalidateOptionsMenu();
         showFabWithAnimation(/* showFab */ false);
+        if (!SharedPreferenceUtil.getHamburgerPromoTriggerActionHappenedBefore(this)) {
+            SharedPreferenceUtil.setHamburgerPromoTriggerActionHappenedBefore(this);
+        }
     }
 
     @Override
diff --git a/src/com/android/contacts/group/GroupListItem.java b/src/com/android/contacts/group/GroupListItem.java
index 3bfcdd3..f2359b6 100644
--- a/src/com/android/contacts/group/GroupListItem.java
+++ b/src/com/android/contacts/group/GroupListItem.java
@@ -15,8 +15,6 @@
  */
 package com.android.contacts.group;
 
-import java.util.Objects;
-
 /**
  * Meta-data for a contact group.  We load all groups associated with the contact's
  * constituent accounts.
@@ -85,36 +83,4 @@
     public String getSystemId() {
         return mSystemId;
     }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mAccountName, mAccountType, mDataSet, mGroupId, mTitle,
-                mIsFirstGroupInAccount, mMemberCount, mIsReadOnly, mSystemId);
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (this == other) {
-            return true;
-        }
-
-        if (!(other instanceof GroupListItem)) {
-            return false;
-        }
-
-        final GroupListItem otherGroup = (GroupListItem) other;
-        if (!Objects.equals(mAccountName, otherGroup.getAccountName())
-                || !Objects.equals(mAccountType, otherGroup.getAccountType())
-                || !Objects.equals(mDataSet, otherGroup.getDataSet())
-                || !(mGroupId == otherGroup.getGroupId())
-                || !Objects.equals(mTitle, otherGroup.getTitle())
-                || !(mIsFirstGroupInAccount == otherGroup.isFirstGroupInAccount())
-                || !(mMemberCount == otherGroup.getMemberCount())
-                || !(mIsReadOnly == otherGroup.isReadOnly())
-                || !Objects.equals(mSystemId, otherGroup.getSystemId())) {
-            return false;
-        }
-
-        return true;
-    }
 }
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupsFragment.java b/src/com/android/contacts/group/GroupsFragment.java
index 4c9e251..be1b44a 100644
--- a/src/com/android/contacts/group/GroupsFragment.java
+++ b/src/com/android/contacts/group/GroupsFragment.java
@@ -18,11 +18,11 @@
 
 import android.app.Fragment;
 import android.app.LoaderManager;
+import android.content.Context;
 import android.content.CursorLoader;
 import android.content.Loader;
 import android.database.Cursor;
 import android.os.Bundle;
-import android.util.Log;
 
 import com.android.contacts.GroupListLoader;
 
@@ -34,8 +34,6 @@
  */
 public final class GroupsFragment extends Fragment {
 
-    private static final String TAG = GroupsFragment.class.getSimpleName();
-
     private static final int LOADER_GROUPS = 1;
 
     /**
@@ -59,26 +57,12 @@
 
                 @Override
                 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-                    final List<GroupListItem> newGroupListItems = new ArrayList<>();
+                    mGroupListItems.clear();
                     for (int i = 0; i < data.getCount(); i++) {
                         if (data.moveToNext()) {
-                            newGroupListItems.add(GroupUtil.getGroupListItem(data, i));
+                            mGroupListItems.add(GroupUtil.getGroupListItem(data, i));
                         }
                     }
-
-                    if (mGroupListItems.equals(newGroupListItems)) {
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                            Log.v(TAG, "The same groups loaded, returning.");
-                        }
-                        return;
-                    }
-
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "New group(s) loaded.");
-                    }
-
-                    mGroupListItems.clear();
-                    mGroupListItems.addAll(newGroupListItems);
                     if (mListener != null) {
                         mListener.onGroupsLoaded(mGroupListItems);
                     }
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index bb706bf..90dbc69 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -88,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 {
@@ -111,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);
@@ -251,7 +285,10 @@
                 (int) getResources().getDimension(R.dimen.pull_to_refresh_distance));
     }
 
-    /** Request sync for Google accounts(not include Google+ accounts) in filter. */
+    /**
+     * Request sync for the Google accounts (not include Google+ accounts) specified by the given
+     * filter.
+     */
     private void syncContacts(ContactListFilter filter) {
         if (filter == null) {
             return;
@@ -265,7 +302,9 @@
         final List<Account> syncableAccounts = filter.getSyncableAccounts(accounts);
         if (syncableAccounts != null && syncableAccounts.size() > 0) {
             for (Account account : syncableAccounts) {
-                if (!SyncUtil.isSyncStatusPendingOrActive(account)) {
+                 // We can prioritize Contacts sync if sync is not initialized yet.
+                if (!SyncUtil.isSyncStatusPendingOrActive(account)
+                        || SyncUtil.isUnsyncableGoogleAccount(account)) {
                     ContentResolver.requestSync(account, ContactsContract.AUTHORITY, bundle);
                 }
             }
diff --git a/src/com/android/contacts/list/MultiSelectContactsListFragment.java b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
index 8b55047..f5c6d34 100644
--- a/src/com/android/contacts/list/MultiSelectContactsListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
@@ -330,7 +330,13 @@
         if (accountFilterContainer == null) {
             return;
         }
-        if (firstVisibleItem == 0) {
+
+        int firstCompletelyVisibleItem = firstVisibleItem;
+        if (view != null && view.getChildAt(0) != null && view.getChildAt(0).getTop() < 0) {
+            firstCompletelyVisibleItem++;
+        }
+
+        if (firstCompletelyVisibleItem == 0) {
             ViewCompat.setElevation(accountFilterContainer, 0);
         } else {
             ViewCompat.setElevation(accountFilterContainer,
diff --git a/src/com/android/contacts/list/UiIntentActions.java b/src/com/android/contacts/list/UiIntentActions.java
index 39d5007..6ea984f 100644
--- a/src/com/android/contacts/list/UiIntentActions.java
+++ b/src/com/android/contacts/list/UiIntentActions.java
@@ -29,6 +29,12 @@
             "com.android.contacts.action.LIST_DEFAULT";
 
     /**
+     * The action for contacts list tab.
+     */
+    public static final String LIST_CONTACTS =
+            "com.android.contacts.action.LIST_CONTACTS";
+
+    /**
      * The action for the contacts list tab.
      */
     public static final String LIST_GROUP_ACTION =
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 16f137e..e918233 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -171,6 +171,7 @@
 import com.android.contacts.util.ImageViewDrawableSetter;
 import com.android.contacts.util.PhoneCapabilityTester;
 import com.android.contacts.util.SchedulingUtils;
+import com.android.contacts.util.SharedPreferenceUtil;
 import com.android.contacts.util.StructuredPostalUtils;
 import com.android.contacts.widget.MultiShrinkScroller;
 import com.android.contacts.widget.MultiShrinkScroller.MultiShrinkScrollerListener;
@@ -2546,6 +2547,13 @@
 
     @Override
     public void onBackPressed() {
+        final int previousScreenType = getIntent().getIntExtra
+                (EXTRA_PREVIOUS_SCREEN_TYPE, ScreenType.UNKNOWN);
+        if ((previousScreenType == ScreenType.ALL_CONTACTS
+                || previousScreenType == ScreenType.FAVORITES)
+                && !SharedPreferenceUtil.getHamburgerPromoTriggerActionHappenedBefore(this)) {
+            SharedPreferenceUtil.setHamburgerPromoTriggerActionHappenedBefore(this);
+        }
         if (mScroller != null) {
             if (!mIsExitAnimationInProgress) {
                 mScroller.scrollOffBottom();
diff --git a/src/com/android/contacts/util/SharedPreferenceUtil.java b/src/com/android/contacts/util/SharedPreferenceUtil.java
new file mode 100644
index 0000000..e33c7aa
--- /dev/null
+++ b/src/com/android/contacts/util/SharedPreferenceUtil.java
@@ -0,0 +1,84 @@
+/*
+ * 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.content.Context;
+import android.content.SharedPreferences;
+
+public class SharedPreferenceUtil {
+
+    private static final String PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED_BEFORE =
+            "hamburgerPromoDisplayedBefore";
+
+    private static final String PREFERENCE_KEY_HAMBURGER_MENU_CLICKED_BEFORE =
+            "hamburgerMenuClickedBefore";
+
+    private static final String PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED_BEFORE =
+            "hamburgerPromoTriggerActionHappenedBefore";
+
+    public static boolean getHamburgerPromoDisplayedBefore(Context context) {
+        return getSharedPreferences(context)
+                .getBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED_BEFORE, false);
+    }
+
+    public static void setHamburgerPromoDisplayedBefore(Context context) {
+        getSharedPreferences(context).edit()
+                .putBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED_BEFORE, true)
+                .apply();
+    }
+
+    public static boolean getHamburgerMenuClickedBefore(Context context) {
+        return getSharedPreferences(context)
+                .getBoolean(PREFERENCE_KEY_HAMBURGER_MENU_CLICKED_BEFORE, false);
+    }
+
+    public static void setHamburgerMenuClickedBefore(Context context) {
+        getSharedPreferences(context).edit()
+                .putBoolean(PREFERENCE_KEY_HAMBURGER_MENU_CLICKED_BEFORE, true)
+                .apply();
+    }
+
+    public static boolean getHamburgerPromoTriggerActionHappenedBefore(Context context) {
+        return getSharedPreferences(context)
+                .getBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED_BEFORE, false);
+    }
+
+    public static void setHamburgerPromoTriggerActionHappenedBefore(Context context) {
+        getSharedPreferences(context).edit()
+                .putBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED_BEFORE, true)
+                .apply();
+    }
+
+    /**
+     * Show hamburger promo if:
+     * 1) Hamburger menu is never clicked before
+     * 2) Hamburger menu promo is never displayed before
+     * 3) There is at least one available user action
+     *      (for now, available user actions to trigger to displayed hamburger promo are:
+     *       a: QuickContact UI back to PeopleActivity
+     *       b: Search action back to PeopleActivity)
+     */
+    public static boolean getShouldShowHamburgerPromo(Context context) {
+        return !getHamburgerMenuClickedBefore(context)
+                && getHamburgerPromoTriggerActionHappenedBefore(context)
+                && !getHamburgerPromoDisplayedBefore(context);
+    }
+
+    private static SharedPreferences getSharedPreferences(Context context) {
+        return context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
+    }
+}
diff --git a/src/com/android/contacts/util/SyncUtil.java b/src/com/android/contacts/util/SyncUtil.java
index 0823ba3..cef2223 100644
--- a/src/com/android/contacts/util/SyncUtil.java
+++ b/src/com/android/contacts/util/SyncUtil.java
@@ -19,6 +19,10 @@
 import android.content.ContentResolver;
 import android.provider.ContactsContract;
 
+import com.android.contacts.common.model.account.GoogleAccountType;
+
+import java.util.List;
+
 /**
  * Utilities related to sync.
  */
@@ -29,7 +33,20 @@
     }
 
     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/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 3c3e5cc..8f43dbf 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -19,7 +19,6 @@
 
     <string-array name="allIntents">
         <!-- List modes -->
-        <item>VIEW (content uri without any id)</item>
         <item>ACTION_PICK: contact</item>
         <item>ACTION_PICK: contact (legacy)</item>
         <item>ACTION_PICK: phone</item>
@@ -44,6 +43,7 @@
         <item>ACTION_SEARCH: contact</item>
         <item>ACTION_SEARCH: email</item>
         <item>ACTION_SEARCH: phone</item>
+        <item>ACTION_SEARCH: general</item>
         <item>SEARCH_SUGGESTION_CLICKED: contact</item>
 
         <!-- Edit -->
@@ -61,6 +61,8 @@
         <item>EDIT (group)</item>
 
         <!-- View -->
+        <item>VIEW (content uri without any id)</item>
+        <item>VIEW PERSON (content uri without any id)</item>
         <item>VIEW (content uri with only id)</item>
         <item>VIEW (lookup uri without id)</item>
         <item>VIEW (lookup uri)</item>
@@ -70,6 +72,15 @@
 
         <!-- Various ways to start Contacts -->
         <item>QuickContactTestsActivity</item>
+
+        <!-- Intents accepted by PeopleActivity -->
+        <item>LIST_DEFAULT</item>
+        <item>LIST_CONTACTS</item>
+        <item>LIST_ALL_CONTACTS</item>
+        <item>LIST_CONTACTS_WITH_PHONES</item>
+        <item>LIST_STARRED</item>
+        <item>LIST_FREQUENT</item>
+        <item>LIST_STREQUENT</item>
     </string-array>
 
     <string name="pickContact">Pick contact</string>
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
index 926f75e..4a93364 100644
--- a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -45,6 +45,7 @@
 import android.widget.Toast;
 
 import com.android.contacts.GroupListLoader;
+import com.android.contacts.list.UiIntentActions;
 import com.android.contacts.tests.R;
 import com.android.contacts.tests.quickcontact.QuickContactTestsActivity;
 
@@ -67,7 +68,6 @@
             "com.android.contacts.activities.PeopleActivity";
 
     public enum ContactsIntent {
-        VIEW_CONTACT_WITHOUT_ID,
         ACTION_PICK_CONTACT,
         ACTION_PICK_CONTACT_LEGACY,
         ACTION_PICK_PHONE,
@@ -92,6 +92,7 @@
         ACTION_SEARCH_CONTACT,
         ACTION_SEARCH_EMAIL,
         ACTION_SEARCH_PHONE,
+        ACTION_SEARCH_GENERAL,
         SEARCH_SUGGESTION_CLICKED_CONTACT,
         EDIT_CONTACT,
         EDIT_CONTACT_LOOKUP,
@@ -105,13 +106,22 @@
         EDIT_NEW_RAW_CONTACT,
         EDIT_NEW_LEGACY,
         EDIT_GROUP,
+        VIEW_CONTACT_WITHOUT_ID,
+        VIEW_PERSON_WITHOUT_ID,
         VIEW_CONTACT,
         VIEW_CONTACT_LOOKUP,
         VIEW_CONTACT_LOOKUP_ID,
         VIEW_RAW_CONTACT,
         VIEW_LEGACY,
         VIEW_GROUP,
-        QUICK_CONTACT_TESTS_ACTIVITY;
+        QUICK_CONTACT_TESTS_ACTIVITY,
+        LIST_DEFAULT,
+        LIST_CONTACTS,
+        LIST_ALL_CONTACTS,
+        LIST_CONTACTS_WITH_PHONES,
+        LIST_STARRED,
+        LIST_FREQUENT,
+        LIST_STREQUENT;
 
         public static ContactsIntent get(int ordinal) {
             return values()[ordinal];
@@ -275,6 +285,12 @@
                 startSearchResultActivity(intent);
                 break;
             }
+            case ACTION_SEARCH_GENERAL: {
+                Intent intent = new Intent(Intent.ACTION_SEARCH);
+                intent.putExtra(SearchManager.QUERY, "a");
+                startSearchResultActivity(intent);
+                break;
+            }
             case SEARCH_SUGGESTION_CLICKED_CONTACT: {
                 long contactId = findArbitraryContactWithPhoneNumber();
                 if (contactId != -1) {
@@ -389,6 +405,12 @@
                 startActivity(new Intent(Intent.ACTION_VIEW, Contacts.CONTENT_URI));
                 break;
             }
+            case VIEW_PERSON_WITHOUT_ID: {
+                Intent intent = new Intent(Intent.ACTION_VIEW);
+                intent.setType("vnd.android.cursor.dir/person");
+                startActivity(intent);
+                break;
+            }
             case VIEW_CONTACT_LOOKUP: {
                 final long contactId = findArbitraryContactWithPhoneNumber();
                 if (contactId != -1) {
@@ -449,6 +471,34 @@
                 startActivity(new Intent(this, QuickContactTestsActivity.class));
                 break;
             }
+            case LIST_DEFAULT: {
+                startActivity(new Intent(UiIntentActions.LIST_DEFAULT));
+                break;
+            }
+            case LIST_CONTACTS: {
+                startActivity(new Intent(UiIntentActions.LIST_CONTACTS));
+                break;
+            }
+            case LIST_ALL_CONTACTS: {
+                startActivity(new Intent(UiIntentActions.LIST_ALL_CONTACTS_ACTION));
+                break;
+            }
+            case LIST_CONTACTS_WITH_PHONES: {
+                startActivity(new Intent(UiIntentActions.LIST_CONTACTS_WITH_PHONES_ACTION));
+                break;
+            }
+            case LIST_STARRED: {
+                startActivity(new Intent(UiIntentActions.LIST_STARRED_ACTION));
+                break;
+            }
+            case LIST_FREQUENT: {
+                startActivity(new Intent(UiIntentActions.LIST_FREQUENT_ACTION));
+                break;
+            }
+            case LIST_STREQUENT: {
+                startActivity(new Intent(UiIntentActions.LIST_STREQUENT_ACTION));
+                break;
+            }
 
             default: {
                 Toast.makeText(this, "Sorry, we forgot to write this...", Toast.LENGTH_LONG).show();
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));
+    }
+}