Merge "Add turn on/off setting for Lychee in Contacts App." into ub-contactsdialer-b-dev
diff --git a/src/com/android/contacts/common/CallUtil.java b/src/com/android/contacts/common/CallUtil.java
index 7640246..88fca92 100644
--- a/src/com/android/contacts/common/CallUtil.java
+++ b/src/com/android/contacts/common/CallUtil.java
@@ -17,6 +17,7 @@
 package com.android.contacts.common;
 
 import com.android.contacts.common.compat.CompatUtils;
+import com.android.contacts.common.compat.PhoneAccountSdkCompat;
 import com.android.contacts.common.util.PermissionsUtil;
 import com.android.contacts.common.util.PhoneNumberHelper;
 import com.android.phone.common.PhoneConstants;
@@ -151,7 +152,7 @@
 
                     int videoCapabilities = VIDEO_CALLING_ENABLED;
                     if (account.hasCapabilities(
-                            PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) {
+                            PhoneAccountSdkCompat.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) {
                         videoCapabilities |= VIDEO_CALLING_PRESENCE;
                     }
                     return videoCapabilities;
diff --git a/src/com/android/contacts/common/ContactsUtils.java b/src/com/android/contacts/common/ContactsUtils.java
index 2ef68d4..c37e8f8 100644
--- a/src/com/android/contacts/common/ContactsUtils.java
+++ b/src/com/android/contacts/common/ContactsUtils.java
@@ -22,6 +22,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.support.annotation.IntDef;
 import android.provider.ContactsContract.DisplayPhoto;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
@@ -30,8 +31,13 @@
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.model.dataitem.ImDataItem;
 import com.android.contacts.common.testing.NeededForTesting;
+import com.android.contacts.common.compat.ContactsCompat;
+import com.android.contacts.common.compat.DirectoryCompat;
+import com.android.contacts.common.compat.SdkSelectionUtils;
 import com.android.contacts.common.model.AccountTypeManager;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 public class ContactsUtils {
@@ -46,10 +52,9 @@
 
     private static int sThumbnailSize = -1;
 
-    public static final boolean FLAG_N_FEATURE =
-            false // Enforce Pre-N behavior in release build
-            && (Build.VERSION.SDK_INT > Build.VERSION_CODES.M
-                    || Build.VERSION.CODENAME.startsWith("N"));
+    public static final boolean FLAG_N_FEATURE = SdkSelectionUtils.TARGET_N_SDK // build-time flag
+            && (Build.VERSION.SDK_INT > Build.VERSION_CODES.M // runtime flag
+                    || Build.VERSION.CODENAME.startsWith("N")); // TODO: remove startsWith("N")
 
     // TODO find a proper place for the canonical version of these
     public interface ProviderNames {
@@ -96,6 +101,20 @@
         return null;
     }
 
+
+    public static final long USER_TYPE_CURRENT = 0;
+    public static final long USER_TYPE_WORK = 1;
+
+    /**
+     * UserType indicates the user type of the contact. If the contact is from Work User (Work
+     * Profile in Android Multi-User System), it's {@link #USER_TYPE_WORK}, otherwise,
+     * {@link #USER_TYPE_CURRENT}. Please note that current user can be in work profile, where the
+     * dialer is running inside Work Profile.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({USER_TYPE_CURRENT, USER_TYPE_WORK})
+    public @interface UserType {}
+
     /**
      * Test if the given {@link CharSequence} contains any graphic characters,
      * first checking {@link TextUtils#isEmpty(CharSequence)} to handle null.
@@ -223,4 +242,49 @@
         }
         return new Pair<>(intent, secondaryIntent);
     }
+
+    /**
+     * Determine UserType from directory id and contact id.
+     *
+     * 3 types of query
+     *
+     * 1. 2 profile query: content://com.android.contacts/phone_lookup_enterprise/1234567890
+     * personal and work contact are mixed into one cursor. no directory id. contact_id indicates if
+     * it's work contact
+     *
+     * 2. work local query:
+     * content://com.android.contacts/phone_lookup_enterprise/1234567890?directory=1000000000
+     * either directory_id or contact_id is enough to identify work contact
+     *
+     * 3. work remote query:
+     * content://com.android.contacts/phone_lookup_enterprise/1234567890?directory=1000000003
+     * contact_id is random. only directory_id is available
+     *
+     * Summary: If directory_id is not null, always use directory_id to identify work contact.
+     * (which is the case here) Otherwise, use contact_id.
+     *
+     * @param directoryId directory id of ContactsProvider query
+     * @param contactId contact id
+     * @return UserType indicates the user type of the contact. A directory id or contact id larger
+     *         than a thredshold indicates that the contact is stored in Work Profile, but not in
+     *         current user. It's a contract by ContactsProvider and check by
+     *         Contacts.isEnterpriseDirectoryId and Contacts.isEnterpriseContactId. Currently, only
+     *         2 kinds of users can be detected from the directoryId and contactId as
+     *         ContactsProvider can only access current and work user's contacts
+     */
+    public static @UserType long determineUserType(Long directoryId, Long contactId) {
+        // First check directory id
+        if (directoryId != null) {
+            return DirectoryCompat.isEnterpriseDirectoryId(directoryId) ? USER_TYPE_WORK
+                    : USER_TYPE_CURRENT;
+        }
+        // Only check contact id if directory id is null
+        if (contactId != null && contactId != 0L
+                && ContactsCompat.isEnterpriseContactId(contactId)) {
+            return USER_TYPE_WORK;
+        } else {
+            return USER_TYPE_CURRENT;
+        }
+
+    }
 }
diff --git a/src/com/android/contacts/common/activity/AppCompatTransactionSafeActivity.java b/src/com/android/contacts/common/activity/AppCompatTransactionSafeActivity.java
new file mode 100644
index 0000000..e70a9fd
--- /dev/null
+++ b/src/com/android/contacts/common/activity/AppCompatTransactionSafeActivity.java
@@ -0,0 +1,66 @@
+/*
+ * 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.common.activity;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+/**
+ * A common superclass that keeps track of whether an {@link AppCompatActivity} has saved its state
+ * yet or not, copied from {@link com.android.contacts.common.activity.TransactionSafeActivity},
+ * which will be deprecated after Kitkat backporting is done.
+ */
+public abstract class AppCompatTransactionSafeActivity extends AppCompatActivity {
+
+    private boolean mIsSafeToCommitTransactions;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mIsSafeToCommitTransactions = true;
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mIsSafeToCommitTransactions = true;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mIsSafeToCommitTransactions = true;
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        mIsSafeToCommitTransactions = false;
+    }
+
+    /**
+     * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
+     * whether {@link FragmentActivity#onSaveInstanceState} has been called or not.
+     *
+     * Make sure that the current activity calls into
+     * {@link super.onSaveInstanceState(Bundle outState)} (if that method is overridden),
+     * so the flag is properly set.
+     */
+    public boolean isSafeToCommitTransactions() {
+        return mIsSafeToCommitTransactions;
+    }
+}
diff --git a/src/com/android/contacts/common/compat/CallableCompat.java b/src/com/android/contacts/common/compat/CallableCompat.java
index 2979f63..d25d4be 100644
--- a/src/com/android/contacts/common/compat/CallableCompat.java
+++ b/src/com/android/contacts/common/compat/CallableCompat.java
@@ -28,8 +28,7 @@
             Uri.withAppendedPath(Callable.CONTENT_URI, "filter_enterprise");
 
     public static Uri getContentFilterUri() {
-        // TODO: Use N APIs
-        if (ContactsUtils.FLAG_N_FEATURE && android.os.Build.VERSION.CODENAME.startsWith("N")) {
+        if (ContactsUtils.FLAG_N_FEATURE) {
             return ENTERPRISE_CONTENT_FILTER_URI;
         }
         return Callable.CONTENT_FILTER_URI;
diff --git a/src/com/android/contacts/common/compat/ContactsCompat.java b/src/com/android/contacts/common/compat/ContactsCompat.java
index 5b50385..5a5e46a 100644
--- a/src/com/android/contacts/common/compat/ContactsCompat.java
+++ b/src/com/android/contacts/common/compat/ContactsCompat.java
@@ -40,8 +40,7 @@
     private static final long ENTERPRISE_CONTACT_ID_BASE = 1000000000;
 
     public static Uri getContentUri() {
-        // TODO: Use N APIs
-        if (ContactsUtils.FLAG_N_FEATURE && android.os.Build.VERSION.CODENAME.startsWith("N")) {
+        if (ContactsUtils.FLAG_N_FEATURE) {
             return ENTERPRISE_CONTENT_FILTER_URI;
         }
         return Contacts.CONTENT_FILTER_URI;
diff --git a/src/com/android/contacts/common/compat/DirectoryCompat.java b/src/com/android/contacts/common/compat/DirectoryCompat.java
index b032fe9..f100938 100644
--- a/src/com/android/contacts/common/compat/DirectoryCompat.java
+++ b/src/com/android/contacts/common/compat/DirectoryCompat.java
@@ -17,7 +17,6 @@
 package com.android.contacts.common.compat;
 
 import android.net.Uri;
-import android.os.Build;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Directory;
 
@@ -25,30 +24,31 @@
 
 public class DirectoryCompat {
 
-    // TODO: Use N APIs
-    private static final Uri ENTERPRISE_CONTENT_URI =
-            Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories_enterprise");
-    // TODO: Use N APIs
-    private static final long ENTERPRISE_LOCAL_INVISIBLE = 1000000000L + Directory.LOCAL_INVISIBLE;
-
     public static Uri getContentUri() {
-        // TODO: Use N APIs
-        if (ContactsUtils.FLAG_N_FEATURE && android.os.Build.VERSION.CODENAME.startsWith("N")) {
-            return ENTERPRISE_CONTENT_URI;
+        if (ContactsUtils.FLAG_N_FEATURE) {
+            return DirectorySdkCompat.ENTERPRISE_CONTENT_URI;
         }
         return Directory.CONTENT_URI;
     }
 
     public static boolean isInvisibleDirectory(long directoryId) {
-        return (directoryId == Directory.LOCAL_INVISIBLE
-                || directoryId == ENTERPRISE_LOCAL_INVISIBLE);
+        if (ContactsUtils.FLAG_N_FEATURE) {
+            return (directoryId == Directory.LOCAL_INVISIBLE
+                    || directoryId == DirectorySdkCompat.ENTERPRISE_LOCAL_INVISIBLE);
+        }
+        return directoryId == Directory.LOCAL_INVISIBLE;
     }
 
     public static boolean isRemoteDirectory(long directoryId) {
-        // TODO: Use N APIs
-        if (ContactsUtils.FLAG_N_FEATURE && android.os.Build.VERSION.CODENAME.startsWith("N")) {
-            return Directory.isRemoteDirectory(directoryId);
+        if (ContactsUtils.FLAG_N_FEATURE) {
+            return DirectorySdkCompat.isRemoteDirectory(directoryId);
         }
         return !(directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE);
     }
+
+    public static boolean isEnterpriseDirectoryId(long directoryId) {
+        return ContactsUtils.FLAG_N_FEATURE
+                ? DirectorySdkCompat.isEnterpriseDirectoryId(directoryId)
+                : false;
+    }
 }
diff --git a/src/com/android/contacts/common/compat/PhoneCompat.java b/src/com/android/contacts/common/compat/PhoneCompat.java
index 24600f5..5277761 100644
--- a/src/com/android/contacts/common/compat/PhoneCompat.java
+++ b/src/com/android/contacts/common/compat/PhoneCompat.java
@@ -28,8 +28,7 @@
             Uri.withAppendedPath(Phone.CONTENT_URI, "filter_enterprise");
 
     public static Uri getContentFilterUri() {
-        // TODO: Use N APIs
-        if (ContactsUtils.FLAG_N_FEATURE && android.os.Build.VERSION.CODENAME.startsWith("N")) {
+        if (ContactsUtils.FLAG_N_FEATURE) {
             return ENTERPRISE_CONTENT_FILTER_URI;
         }
         return Phone.CONTENT_FILTER_URI;
diff --git a/src/com/android/contacts/common/dialog/CallSubjectDialog.java b/src/com/android/contacts/common/dialog/CallSubjectDialog.java
index 3c08b37..a17c4fc 100644
--- a/src/com/android/contacts/common/dialog/CallSubjectDialog.java
+++ b/src/com/android/contacts/common/dialog/CallSubjectDialog.java
@@ -48,6 +48,7 @@
 import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.R;
 import com.android.contacts.common.compat.CompatUtils;
+import com.android.contacts.common.compat.PhoneAccountSdkCompat;
 import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
 import com.android.contacts.common.util.UriUtils;
 import com.android.phone.common.animation.AnimUtils;
@@ -593,17 +594,18 @@
                 (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
         final PhoneAccount account = telecomManager.getPhoneAccount(mPhoneAccountHandle);
 
-        Bundle phoneAccountExtras = account.getExtras();
+        Bundle phoneAccountExtras = PhoneAccountSdkCompat.getExtras(account);
         if (phoneAccountExtras == null) {
             return;
         }
 
         // Get limit, if provided; otherwise default to existing value.
-        mLimit = phoneAccountExtras.getInt(PhoneAccount.EXTRA_CALL_SUBJECT_MAX_LENGTH, mLimit);
+        mLimit = phoneAccountExtras
+                .getInt(PhoneAccountSdkCompat.EXTRA_CALL_SUBJECT_MAX_LENGTH, mLimit);
 
         // Get charset; default to none (e.g. count characters 1:1).
         String charsetName = phoneAccountExtras.getString(
-                PhoneAccount.EXTRA_CALL_SUBJECT_CHARACTER_ENCODING);
+                PhoneAccountSdkCompat.EXTRA_CALL_SUBJECT_CHARACTER_ENCODING);
 
         if (!TextUtils.isEmpty(charsetName)) {
             try {
diff --git a/src/com/android/contacts/common/list/AccountFilterActivity.java b/src/com/android/contacts/common/list/AccountFilterActivity.java
index 20be63b..966637b 100644
--- a/src/com/android/contacts/common/list/AccountFilterActivity.java
+++ b/src/com/android/contacts/common/list/AccountFilterActivity.java
@@ -63,6 +63,10 @@
 
     private ContactListFilter mCurrentFilter;
 
+    private ContactListFilterView mCustomFilterView; // the "Customize" filter
+
+    private boolean mIsCustomFilterViewSelected;
+
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -170,6 +174,8 @@
         final ContactListFilter filter = (ContactListFilter) view.getTag();
         if (filter == null) return; // Just in case
         if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+            mCustomFilterView = listFilterView;
+            mIsCustomFilterViewSelected = listFilterView.isChecked();
             final Intent intent = new Intent(this,
                     CustomContactListFilterActivity.class);
             listFilterView.setActivated(true);
@@ -190,6 +196,12 @@
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode == AppCompatActivity.RESULT_CANCELED && mCustomFilterView != null &&
+                !mIsCustomFilterViewSelected) {
+            mCustomFilterView.setActivated(false);
+            return;
+        }
+
         if (resultCode != AppCompatActivity.RESULT_OK) {
             return;
         }
diff --git a/src/com/android/contacts/common/list/ContactEntryListAdapter.java b/src/com/android/contacts/common/list/ContactEntryListAdapter.java
index 089288b..a7c3504 100644
--- a/src/com/android/contacts/common/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/common/list/ContactEntryListAdapter.java
@@ -418,9 +418,17 @@
                 DirectoryPartition partition = new DirectoryPartition(false, true);
                 partition.setDirectoryId(id);
                 if (DirectoryCompat.isRemoteDirectory(id)) {
-                    partition.setLabel(mContext.getString(R.string.directory_search_label));
+                    if (DirectoryCompat.isEnterpriseDirectoryId(id)) {
+                        partition.setLabel(mContext.getString(R.string.directory_search_label_work));
+                    } else {
+                        partition.setLabel(mContext.getString(R.string.directory_search_label));
+                    }
                 } else {
-                    partition.setLabel(mDefaultFilterHeaderText.toString());
+                    if (DirectoryCompat.isEnterpriseDirectoryId(id)) {
+                        partition.setLabel(mContext.getString(R.string.list_filter_phones_work));
+                    } else {
+                        partition.setLabel(mDefaultFilterHeaderText.toString());
+                    }
                 }
                 partition.setDirectoryType(cursor.getString(directoryTypeColumnIndex));
                 partition.setDisplayName(cursor.getString(displayNameColumnIndex));
diff --git a/src/com/android/contacts/common/list/ContactListFilterView.java b/src/com/android/contacts/common/list/ContactListFilterView.java
index c6cc597..6df8e21 100644
--- a/src/com/android/contacts/common/list/ContactListFilterView.java
+++ b/src/com/android/contacts/common/list/ContactListFilterView.java
@@ -78,6 +78,10 @@
         setContentDescription(generateContentDescription());
     }
 
+    public boolean isChecked() {
+        return mRadioButton.isChecked();
+    }
+
     public void bindView(AccountTypeManager accountTypes) {
         if (mAccountType == null) {
             mIcon = (ImageView) findViewById(R.id.icon);
diff --git a/src/com/android/contacts/common/list/ContactListItemView.java b/src/com/android/contacts/common/list/ContactListItemView.java
index 18e82ad..90b3d60 100644
--- a/src/com/android/contacts/common/list/ContactListItemView.java
+++ b/src/com/android/contacts/common/list/ContactListItemView.java
@@ -31,6 +31,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v7.widget.AppCompatCheckBox;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -43,7 +44,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView.SelectionBoundsAdjuster;
-import android.widget.CheckBox;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 import android.widget.QuickContactBadge;
@@ -185,8 +185,9 @@
     private TextView mSnippetView;
     private TextView mStatusView;
     private ImageView mPresenceIcon;
-    private CheckBox mCheckBox;
+    private AppCompatCheckBox mCheckBox;
     private ImageView mVideoCallIcon;
+    private ImageView mWorkProfileIcon;
 
     private ColorStateList mSecondaryTextColor;
 
@@ -537,6 +538,14 @@
                     MeasureSpec.makeMeasureSpec(mVideoCallIconSize, MeasureSpec.EXACTLY));
         }
 
+        if (isVisible(mWorkProfileIcon)) {
+            mWorkProfileIcon.measure(
+                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+            mNameTextViewHeight =
+                    Math.max(mNameTextViewHeight, mWorkProfileIcon.getMeasuredHeight());
+        }
+
         if (isVisible(mStatusView)) {
             // Presence and status are in a same row, so status will be affected by icon size.
             final int statusWidth;
@@ -706,11 +715,32 @@
                 mLabelAndDataViewMaxHeight + mSnippetTextViewHeight + mStatusTextViewHeight;
         int textTopBound = (bottomBound + topBound - totalTextHeight) / 2 + mTextOffsetTop;
 
+        // Work Profile icon align top
+        int workProfileIconWidth = 0;
+        if (isVisible(mWorkProfileIcon)) {
+            workProfileIconWidth = mWorkProfileIcon.getMeasuredWidth();
+            final int distanceFromEnd = mCheckBoxWidth > 0
+                    ? mCheckBoxWidth + mGapBetweenImageAndText : 0;
+            if (mPhotoPosition == PhotoPosition.LEFT) {
+                // When photo is on left, label is placed on the right edge of the list item.
+                mWorkProfileIcon.layout(rightBound - workProfileIconWidth - distanceFromEnd,
+                        textTopBound,
+                        rightBound - distanceFromEnd,
+                        textTopBound + mNameTextViewHeight);
+            } else {
+                // When photo is on right, label is placed on the left of data view.
+                mWorkProfileIcon.layout(leftBound + distanceFromEnd,
+                        textTopBound,
+                        leftBound + workProfileIconWidth + distanceFromEnd,
+                        textTopBound + mNameTextViewHeight);
+            }
+        }
+
         // Layout all text view and presence icon
         // Put name TextView first
         if (isVisible(mNameTextView)) {
-            final int distanceFromEnd = mCheckBoxWidth > 0
-                    ? mCheckBoxWidth + mGapBetweenImageAndText : 0;
+            final int distanceFromEnd = workProfileIconWidth
+                    + (mCheckBoxWidth > 0 ? mCheckBoxWidth + mGapBetweenImageAndText : 0);
             if (mPhotoPosition == PhotoPosition.LEFT) {
                 mNameTextView.layout(leftBound,
                         textTopBound,
@@ -722,6 +752,9 @@
                         rightBound,
                         textTopBound + mNameTextViewHeight);
             }
+        }
+
+        if (isVisible(mNameTextView) || isVisible(mWorkProfileIcon)) {
             textTopBound += mNameTextViewHeight;
         }
 
@@ -1198,11 +1231,11 @@
     }
 
     /**
-     * Returns the {@link CheckBox} view, creating it if necessary.
+     * Returns the {@link AppCompatCheckBox} view, creating it if necessary.
      */
-    public CheckBox getCheckBox() {
+    public AppCompatCheckBox getCheckBox() {
         if (mCheckBox == null) {
-            mCheckBox = new CheckBox(getContext());
+            mCheckBox = new AppCompatCheckBox(getContext());
             // Make non-focusable, so the rest of the ContactListItemView can be clicked.
             mCheckBox.setFocusable(false);
             addView(mCheckBox);
@@ -1318,6 +1351,23 @@
         }
     }
 
+    /**
+     * Set to display work profile icon or not
+     *
+     * @param enabled set to display work profile icon or not
+     */
+    public void setWorkProfileIconEnabled(boolean enabled) {
+        if (mWorkProfileIcon != null) {
+            mWorkProfileIcon.setVisibility(enabled ? View.VISIBLE : View.GONE);
+        } else if (enabled) {
+            mWorkProfileIcon = new ImageView(getContext());
+            addView(mWorkProfileIcon);
+            mWorkProfileIcon.setImageResource(R.drawable.ic_work_profile);
+            mWorkProfileIcon.setScaleType(ScaleType.CENTER_INSIDE);
+            mWorkProfileIcon.setVisibility(View.VISIBLE);
+        }
+    }
+
     private TruncateAt getTextEllipsis() {
         return TruncateAt.MARQUEE;
     }
diff --git a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
index fc90fbf..9e886d9 100644
--- a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
@@ -35,6 +35,7 @@
 
 import com.android.contacts.common.CallUtil;
 import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.ContactsUtils;
 import com.android.contacts.common.GeoUtil;
 import com.android.contacts.common.R;
 import com.android.contacts.common.compat.CallableCompat;
@@ -321,11 +322,20 @@
     public Uri getDataUri(int partitionIndex, Cursor cursor) {
         final long directoryId =
                 ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
-        if (!DirectoryCompat.isRemoteDirectory(directoryId)) {
+        if (DirectoryCompat.isRemoteDirectory(directoryId)) {
+            return null;
+        } else if (DirectoryCompat.isEnterpriseDirectoryId(directoryId)) {
+            /*
+             * ContentUris.withAppendedId(Data.CONTENT_URI, phoneId), is invalid if
+             * isEnterpriseDirectoryId returns true, because the uri itself will fail since the
+             * ContactsProvider in Android Framework currently doesn't support it. return null until
+             * Android framework has enterprise version of Data.CONTENT_URI
+             */
+            return null;
+        } else {
             final long phoneId = cursor.getLong(PhoneQuery.PHONE_ID);
             return ContentUris.withAppendedId(Data.CONTENT_URI, phoneId);
         }
-        return null;
     }
 
     /**
@@ -427,6 +437,7 @@
 
             view.removePhotoView(true, false);
         }
+        bindWorkProfileIcon(view, partition);
 
         final DirectoryPartition directory = (DirectoryPartition) getPartition(partition);
         bindPhoneNumber(view, cursor, directory.isDisplayNumber(), position);
@@ -485,6 +496,13 @@
         view.hideDisplayName();
     }
 
+    private void bindWorkProfileIcon(final ContactListItemView view, int partition) {
+        final DirectoryPartition directory = (DirectoryPartition) getPartition(partition);
+        final long directoryId = directory.getDirectoryId();
+        final long userType = ContactsUtils.determineUserType(directoryId, null);
+        view.setWorkProfileIconEnabled(userType == ContactsUtils.USER_TYPE_WORK);
+    }
+
     protected void bindPhoto(final ContactListItemView view, int partitionIndex, Cursor cursor) {
         if (!isPhotoSupported(partitionIndex)) {
             view.removePhotoView();
diff --git a/src/com/android/contacts/common/model/ContactLoader.java b/src/com/android/contacts/common/model/ContactLoader.java
index 923e2f9..f925d1d 100644
--- a/src/com/android/contacts/common/model/ContactLoader.java
+++ b/src/com/android/contacts/common/model/ContactLoader.java
@@ -40,6 +40,7 @@
 
 import com.android.contacts.common.GeoUtil;
 import com.android.contacts.common.GroupMetaData;
+import com.android.contacts.common.compat.CompatUtils;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountTypeWithDataSet;
 import com.android.contacts.common.util.Constants;
@@ -51,6 +52,7 @@
 import com.android.contacts.common.model.dataitem.PhotoDataItem;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
@@ -113,7 +115,7 @@
      * social stream items).
      */
     private static class ContactQuery {
-        static final String[] COLUMNS = new String[] {
+        static final String[] COLUMNS_INTERNAL = new String[] {
                 Contacts.NAME_RAW_CONTACT_ID,
                 Contacts.DISPLAY_NAME_SOURCE,
                 Contacts.LOOKUP_KEY,
@@ -183,10 +185,19 @@
                 Contacts.IS_USER_PROFILE,
 
                 Data.TIMES_USED,
-                Data.LAST_TIME_USED,
-                Data.CARRIER_PRESENCE
+                Data.LAST_TIME_USED
         };
 
+        static final String[] COLUMNS;
+
+        static {
+            List<String> projectionList = Lists.newArrayList(COLUMNS_INTERNAL);
+            if (CompatUtils.isMarshmallowCompatible()) {
+                projectionList.add(Data.CARRIER_PRESENCE);
+            }
+            COLUMNS = projectionList.toArray(new String[projectionList.size()]);
+        }
+
         public static final int NAME_RAW_CONTACT_ID = 0;
         public static final int DISPLAY_NAME_SOURCE = 1;
         public static final int LOOKUP_KEY = 2;
@@ -716,7 +727,9 @@
         cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
         cursorColumnToContentValues(cursor, cv, ContactQuery.TIMES_USED);
         cursorColumnToContentValues(cursor, cv, ContactQuery.LAST_TIME_USED);
-        cursorColumnToContentValues(cursor, cv, ContactQuery.CARRIER_PRESENCE);
+        if (CompatUtils.isMarshmallowCompatible()) {
+            cursorColumnToContentValues(cursor, cv, ContactQuery.CARRIER_PRESENCE);
+        }
 
         return cv;
     }
diff --git a/src/com/android/contacts/common/model/account/BaseAccountType.java b/src/com/android/contacts/common/model/account/BaseAccountType.java
index 7526f0c..214ba77 100644
--- a/src/com/android/contacts/common/model/account/BaseAccountType.java
+++ b/src/com/android/contacts/common/model/account/BaseAccountType.java
@@ -119,7 +119,7 @@
         this.accountType = null;
         this.dataSet = null;
         this.titleRes = R.string.account_phone;
-        this.iconRes = R.mipmap.ic_contacts_clr_48cv_44dp;
+        this.iconRes = R.mipmap.ic_contacts_launcher;
     }
 
     protected static EditType buildPhoneType(int type) {
diff --git a/src/com/android/contacts/common/model/account/FallbackAccountType.java b/src/com/android/contacts/common/model/account/FallbackAccountType.java
index fd81e46..ce1d059 100644
--- a/src/com/android/contacts/common/model/account/FallbackAccountType.java
+++ b/src/com/android/contacts/common/model/account/FallbackAccountType.java
@@ -30,7 +30,7 @@
         this.accountType = null;
         this.dataSet = null;
         this.titleRes = R.string.account_phone;
-        this.iconRes = R.mipmap.ic_contacts_clr_48cv_44dp;
+        this.iconRes = R.mipmap.ic_contacts_launcher;
 
         // Note those are only set for unit tests.
         this.resourcePackageName = resPackageName;
diff --git a/src/com/android/contacts/common/widget/FloatingActionButtonController.java b/src/com/android/contacts/common/widget/FloatingActionButtonController.java
index eb69d3c..c943669 100644
--- a/src/com/android/contacts/common/widget/FloatingActionButtonController.java
+++ b/src/com/android/contacts/common/widget/FloatingActionButtonController.java
@@ -24,8 +24,9 @@
 import android.view.View;
 import android.widget.ImageButton;
 
-import com.android.contacts.common.util.ViewUtil;
 import com.android.contacts.common.R;
+import com.android.contacts.common.compat.CompatUtils;
+import com.android.contacts.common.util.ViewUtil;
 import com.android.phone.common.animation.AnimUtils;
 
 /**
@@ -50,8 +51,14 @@
 
     public FloatingActionButtonController(Activity activity, View container, ImageButton button) {
         Resources resources = activity.getResources();
-        mFabInterpolator = AnimationUtils.loadInterpolator(activity,
-                android.R.interpolator.fast_out_slow_in);
+        if (CompatUtils.isLollipopCompatible()) {
+            mFabInterpolator = AnimationUtils.loadInterpolator(activity,
+                    android.R.interpolator.fast_out_slow_in);
+        } else {
+            // Use linear to avoid crash on Kitkat since fast_out_slow_in was added in API level 21
+            mFabInterpolator = AnimationUtils.loadInterpolator(activity,
+                    android.R.interpolator.linear);
+        }
         mFloatingActionButtonWidth = resources.getDimensionPixelSize(
                 R.dimen.floating_action_button_width);
         mFloatingActionButtonMarginRight = resources.getDimensionPixelOffset(
@@ -104,7 +111,6 @@
         // moves along with it.
         mFloatingActionButtonContainer.setTranslationX(
                 (int) (positionOffset * getTranslationXForAlignment(ALIGN_END)));
-        mFloatingActionButtonContainer.setTranslationY(0);
     }
 
     /**