Add basic breadcrumb on search result.

- Some line formatting
- Some UI tweak to make it more polished
- Updated the way we generate docid
- Use screen title as leaf level breadcrumb and display on UI (TBD how
  to get parent level titles)
- Hardcode breadcrumb for installed app results

Bug: 32936784
Test: make RunSettingsRoboTests
Change-Id: I18e0780f91b1123bbf25cd99adf2e2a5f684a83c
diff --git a/res/layout/search_breadcrumb_view.xml b/res/layout/search_breadcrumb_view.xml
new file mode 100644
index 0000000..a78b745
--- /dev/null
+++ b/res/layout/search_breadcrumb_view.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 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.
+  -->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/breadcrumb"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:paddingTop="8dp"
+    android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+    android:textColor="?android:attr/textColorSecondary"
+    android:ellipsize="marquee"/>
\ No newline at end of file
diff --git a/res/layout/search_icon_view.xml b/res/layout/search_icon_view.xml
new file mode 100644
index 0000000..d7e0205
--- /dev/null
+++ b/res/layout/search_icon_view.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/icon_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:minWidth="60dp"
+    android:gravity="center_horizontal|top"
+    android:orientation="horizontal"
+    android:paddingEnd="12dp"
+    android:paddingTop="4dp"
+    android:paddingBottom="4dp">
+    <com.android.internal.widget.PreferenceImageView
+        android:id="@android:id/icon"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:scaleType="fitCenter"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/search_inline_switch_item.xml b/res/layout/search_inline_switch_item.xml
index 998c09c..40639b0 100644
--- a/res/layout/search_inline_switch_item.xml
+++ b/res/layout/search_inline_switch_item.xml
@@ -15,67 +15,49 @@
 -->
 
 <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:minHeight="?android:attr/listPreferredItemHeight"
-        android:gravity="center_vertical"
-        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-        android:background="?android:attr/selectableItemBackground"
-        android:clipToPadding="false">
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingTop="16dp"
+    android:paddingBottom="16dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <include layout="@layout/search_icon_view"/>
 
     <LinearLayout
-            android:id="@+id/icon_container"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:minWidth="60dp"
-            android:gravity="start|center_vertical"
-            android:orientation="horizontal"
-            android:paddingEnd="12dp"
-            android:paddingTop="4dp"
-            android:paddingBottom="4dp">
-        <com.android.internal.widget.PreferenceImageView
-                android:id="@android:id/icon"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:maxWidth="48dp"
-                android:maxHeight="48dp"/>
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLength="60"
+            android:maxLines="10"/>
+
+        <include layout="@layout/search_breadcrumb_view"/>
     </LinearLayout>
 
-    <RelativeLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:paddingTop="16dp"
-            android:paddingBottom="16dp">
-
-        <TextView
-                android:id="@android:id/title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:textAppearance="?android:attr/textAppearanceListItem"
-                android:ellipsize="marquee"/>
-
-        <TextView
-                android:id="@android:id/summary"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_below="@android:id/title"
-                android:layout_alignStart="@android:id/title"
-                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
-                android:textColor="?android:attr/textColorSecondary"
-                android:maxLength="60"
-                android:maxLines="10"/>
-
-    </RelativeLayout>
-
     <Switch
-            android:id="@+id/switchView"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:gravity="end|center_vertical"
-            android:paddingStart="16dp"
-            android:orientation="vertical"/>
+        android:id="@+id/switchView"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="top"
+        android:paddingStart="16dp"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/search_intent_item.xml b/res/layout/search_intent_item.xml
index fcb5532..31f7f26 100644
--- a/res/layout/search_intent_item.xml
+++ b/res/layout/search_intent_item.xml
@@ -15,58 +15,42 @@
 -->
 
 <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:minHeight="?android:attr/listPreferredItemHeight"
-        android:gravity="center_vertical"
-        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-        android:background="?android:attr/selectableItemBackground"
-        android:clipToPadding="false">
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingTop="16dp"
+    android:paddingBottom="16dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <include layout="@layout/search_icon_view"/>
 
     <LinearLayout
-            android:id="@+id/icon_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:minWidth="60dp"
-            android:gravity="start|center_vertical"
-            android:orientation="horizontal"
-            android:paddingEnd="12dp"
-            android:paddingTop="4dp"
-            android:paddingBottom="4dp">
-        <com.android.internal.widget.PreferenceImageView
-                android:id="@android:id/icon"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:maxWidth="48dp"
-                android:maxHeight="48dp"/>
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="3"
+            android:ellipsize="marquee"/>
+
+        <include layout="@layout/search_breadcrumb_view"/>
     </LinearLayout>
-
-    <RelativeLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:paddingTop="16dp"
-            android:paddingBottom="16dp">
-
-        <TextView
-                android:id="@android:id/title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:textAppearance="?android:attr/textAppearanceListItem"
-                android:ellipsize="marquee"/>
-
-        <TextView
-                android:id="@android:id/summary"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_below="@android:id/title"
-                android:layout_alignStart="@android:id/title"
-                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
-                android:textColor="?android:attr/textColorSecondary"
-                android:maxLines="10"/>
-
-    </RelativeLayout>
 </LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b4e6339..aea2408 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5947,6 +5947,10 @@
     <string name="search_menu">Search settings</string>
     <!-- Text used as a search hint into the search box -->
     <string name="query_hint_text">Search settings</string>
+    <!-- Search breadcrumb connector symbol -->
+    <string name="search_breadcrumb_connector" translatable="false">
+        <xliff:g name="first_item">%1$s</xliff:g> > <xliff:g name="second_item">%2$s</xliff:g>
+    </string>
 
     <!-- Text used to identify the search query suggestions / recent searches -->
     <string name="search_recents_queries_label">Recent searches</string>
diff --git a/src/com/android/settings/search2/CursorToSearchResultConverter.java b/src/com/android/settings/search2/CursorToSearchResultConverter.java
index 540932c..948397f 100644
--- a/src/com/android/settings/search2/CursorToSearchResultConverter.java
+++ b/src/com/android/settings/search2/CursorToSearchResultConverter.java
@@ -211,7 +211,12 @@
     }
 
     private List<String> getBreadcrumbs(Cursor cursor) {
-        return new ArrayList<>();
+        final List<String> breadcrumbs = new ArrayList<>();
+        final String screenTitle = cursor.getString(COLUMN_INDEX_SCREEN_TITLE);
+        if (!TextUtils.isEmpty(screenTitle)) {
+            breadcrumbs.add(screenTitle);
+        }
+        return breadcrumbs;
     }
 
     /** Uses the breadcrumbs to determine the offset to the base rank.
diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java
index 2cd0cea..073e202 100644
--- a/src/com/android/settings/search2/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search2/DatabaseIndexingManager.java
@@ -56,6 +56,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
@@ -279,7 +280,7 @@
      * @param className the class name (typically a fragment name).
      * @param rebuild true means that you want to delete the data from the Index first.
      * @param includeInSearchResults true means that you want the bit "enabled" set so that the
-     *                               data will be seen included into the search results
+     * data will be seen included into the search results
      */
     public void updateFromClassNameResource(String className, final boolean rebuild,
             boolean includeInSearchResults) {
@@ -287,7 +288,7 @@
             throw new IllegalArgumentException("class name cannot be null!");
         }
         final SearchIndexableResource res = SearchIndexableResources.getResourceByName(className);
-        if (res == null ) {
+        if (res == null) {
             Log.e(LOG_TAG, "Cannot find SearchIndexableResources for class name: " + className);
             return;
         }
@@ -753,7 +754,7 @@
     }
 
     private void updateOneRowWithFilteredData(SQLiteDatabase database, DatabaseRow.Builder builder,
-            String title, String summaryOn, String summaryOff,String keywords) {
+            String title, String summaryOn, String summaryOff, String keywords) {
 
         final String updatedTitle = DatabaseIndexingUtils.normalizeHyphen(title);
         final String updatedSummaryOn = DatabaseIndexingUtils.normalizeHyphen(summaryOn);
@@ -783,14 +784,8 @@
             return;
         }
 
-        // The DocID should contains more than the title string itself (you may have two settings
-        // with the same title). So we need to use a combination of the title and the screenTitle.
-        StringBuilder sb = new StringBuilder(row.updatedTitle);
-        sb.append(row.screenTitle);
-        int docId = sb.toString().hashCode();
-
         ContentValues values = new ContentValues();
-        values.put(IndexDatabaseHelper.IndexColumns.DOCID, docId);
+        values.put(IndexDatabaseHelper.IndexColumns.DOCID, row.getDocId());
         values.put(IndexDatabaseHelper.IndexColumns.LOCALE, row.locale);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, row.rank);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, row.updatedTitle);
@@ -919,7 +914,7 @@
                 }
                 if (!TextUtils.isEmpty(data.className)) {
                     delete(database, IndexDatabaseHelper.IndexColumns.CLASS_NAME, data.className);
-                } else  {
+                } else {
                     if (data instanceof SearchIndexableRaw) {
                         final SearchIndexableRaw raw = (SearchIndexableRaw) data;
                         if (!TextUtils.isEmpty(raw.title)) {
@@ -938,7 +933,7 @@
 
         private int delete(SQLiteDatabase database, String columName, String value) {
             final String whereClause = columName + "=?";
-            final String[] whereArgs = new String[] { value };
+            final String[] whereArgs = new String[]{value};
 
             return database.delete(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, whereClause,
                     whereArgs);
@@ -993,6 +988,16 @@
                     : null;
         }
 
+        /**
+         * Returns the doc id for this row.
+         */
+        public int getDocId() {
+            // The DocID should contains more than the title string itself (you may have two
+            // settings with the same title). So we need to use a combination of multiple
+            // attributes from this row.
+            return Objects.hash(updatedTitle, screenTitle, key, payloadType);
+        }
+
         public static class Builder {
             private String mLocale;
             private String mUpdatedTitle;
@@ -1013,7 +1018,8 @@
             private boolean mEnabled;
             private String mKey;
             private int mUserId;
-            @ResultPayload.PayloadType private int mPayloadType;
+            @ResultPayload.PayloadType
+            private int mPayloadType;
             private ResultPayload mPayload;
 
             public Builder setLocale(String locale) {
@@ -1114,7 +1120,7 @@
             public Builder setPayload(ResultPayload payload) {
                 mPayload = payload;
 
-                if(mPayload != null) {
+                if (mPayload != null) {
                     setPayloadType(mPayload.getType());
                 }
                 return this;
@@ -1122,6 +1128,7 @@
 
             /**
              * Payload type is added when a Payload is added to the Builder in {setPayload}
+             *
              * @param payloadType PayloadType
              * @return The Builder
              */
diff --git a/src/com/android/settings/search2/InlineSwitchViewHolder.java b/src/com/android/settings/search2/InlineSwitchViewHolder.java
index ad99af8..ac49b1c 100644
--- a/src/com/android/settings/search2/InlineSwitchViewHolder.java
+++ b/src/com/android/settings/search2/InlineSwitchViewHolder.java
@@ -21,41 +21,32 @@
 import android.view.View;
 import android.widget.CompoundButton;
 import android.widget.Switch;
-import android.widget.TextView;
 
-import com.android.internal.widget.PreferenceImageView;
 import com.android.settings.R;
 
 /**
  * ViewHolder for Settings represented as SwitchPreferences.
  */
 public class InlineSwitchViewHolder extends SearchViewHolder {
-    public final TextView titleView;
-    public final TextView summaryView;
-    public final PreferenceImageView iconView;
+
     public final Switch switchView;
 
     private final Context mContext;
 
-    private final String TAG = "SwitchViewHolder";
-
     public InlineSwitchViewHolder(View view, Context context) {
         super(view);
         mContext = context;
-        titleView = (TextView) view.findViewById(android.R.id.title);
-        summaryView = (TextView) view.findViewById(android.R.id.summary);
-        iconView = (PreferenceImageView) view.findViewById(android.R.id.icon);
         switchView = (Switch) view.findViewById(R.id.switchView);
     }
 
     @Override
     public void onBind(SearchFragment fragment, SearchResult result) {
+        super.onBind(fragment, result);
         if (mContext == null) {
             return;
         }
         final InlineSwitchPayload payload = (InlineSwitchPayload) result.payload;
         switchView.setChecked(payload.getSwitchValue(mContext));
-
         switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@@ -63,9 +54,5 @@
                 payload.setSwitchValue(mContext, isChecked);
             }
         });
-
-        titleView.setText(result.title);
-        summaryView.setText(result.summary);
-        iconView.setImageDrawable(result.icon);
     }
 }
diff --git a/src/com/android/settings/search2/InstalledAppResultLoader.java b/src/com/android/settings/search2/InstalledAppResultLoader.java
index 14735bd..17bb157 100644
--- a/src/com/android/settings/search2/InstalledAppResultLoader.java
+++ b/src/com/android/settings/search2/InstalledAppResultLoader.java
@@ -28,6 +28,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 
+import com.android.settings.R;
 import com.android.settings.applications.PackageManagerWrapper;
 import com.android.settings.utils.AsyncLoader;
 
@@ -45,6 +46,7 @@
     private static final Intent LAUNCHER_PROBE = new Intent(Intent.ACTION_MAIN)
             .addCategory(Intent.CATEGORY_LAUNCHER);
 
+    private final List<String> mBreadcrumb;
     private final String mQuery;
     private final UserManager mUserManager;
     private final PackageManagerWrapper mPackageManager;
@@ -53,6 +55,9 @@
     public InstalledAppResultLoader(Context context, PackageManagerWrapper pmWrapper,
             String query) {
         super(context);
+        mBreadcrumb = new ArrayList<>();
+        mBreadcrumb.add(context.getString(R.string.app_and_notification_dashboard_title));
+        mBreadcrumb.add(context.getString(R.string.applications_settings));
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mPackageManager = pmWrapper;
         mQuery = query;
@@ -87,6 +92,7 @@
                 builder.addIcon(info.loadIcon(pm))
                         .addTitle(info.loadLabel(pm))
                         .addRank(wordDiff)
+                        .addBreadcrumbs(mBreadcrumb)
                         .addPayload(new IntentPayload(intent));
                 results.add(builder.build());
             }
@@ -127,7 +133,7 @@
      * perfectly, and larger values means they are less similar.
      * <p/>
      * Example:
-     * appName: Abcde, query: Abcde, Returns NAME_EXACT_MATCH
+     * appName: Abcde, query: Abcde, Returns {@link #NAME_EXACT_MATCH}
      * appName: Abcde, query: ade, Returns 2
      * appName: Abcde, query: ae, Returns 3
      * appName: Abcde, query: ea, Returns NAME_NO_MATCH
diff --git a/src/com/android/settings/search2/IntentSearchViewHolder.java b/src/com/android/settings/search2/IntentSearchViewHolder.java
index c9952fa..d02ef1c 100644
--- a/src/com/android/settings/search2/IntentSearchViewHolder.java
+++ b/src/com/android/settings/search2/IntentSearchViewHolder.java
@@ -16,10 +16,6 @@
 package com.android.settings.search2;
 
 import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.settings.R;
 
 /**
  * ViewHolder for intent based search results.
@@ -27,25 +23,14 @@
  */
 public class IntentSearchViewHolder extends SearchViewHolder {
 
-    public final TextView titleView;
-    public final TextView summaryView;
-    public final ImageView iconView;
-
     public IntentSearchViewHolder(View view) {
         super(view);
-        titleView = (TextView) view.findViewById(android.R.id.title);
-        summaryView = (TextView) view.findViewById(android.R.id.summary);
-        iconView = (ImageView) view.findViewById(android.R.id.icon);
     }
 
     @Override
     public void onBind(final SearchFragment fragment, final SearchResult result) {
-        titleView.setText(result.title);
-        summaryView.setText(result.summary);
-        iconView.setImageDrawable(result.icon);
-        if (result.icon == null) {
-            iconView.setBackgroundResource(R.drawable.empty_icon);
-        }
+        super.onBind(fragment, result);
+
         itemView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
diff --git a/src/com/android/settings/search2/SearchViewHolder.java b/src/com/android/settings/search2/SearchViewHolder.java
index 315ec65..0b10b06 100644
--- a/src/com/android/settings/search2/SearchViewHolder.java
+++ b/src/com/android/settings/search2/SearchViewHolder.java
@@ -15,8 +15,14 @@
  */
 package com.android.settings.search2;
 
+import android.content.Context;
 import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
 import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.settings.R;
 
 /**
  * The ViewHolder for the Search RecyclerView.
@@ -25,9 +31,47 @@
  */
 public abstract class SearchViewHolder extends RecyclerView.ViewHolder {
 
+    public final TextView titleView;
+    public final TextView summaryView;
+    public final TextView breadcrumbView;
+    public final ImageView iconView;
+
     public SearchViewHolder(View view) {
         super(view);
+        titleView = (TextView) view.findViewById(android.R.id.title);
+        summaryView = (TextView) view.findViewById(android.R.id.summary);
+        iconView = (ImageView) view.findViewById(android.R.id.icon);
+        breadcrumbView = (TextView) view.findViewById(R.id.breadcrumb);
     }
 
-    public abstract void onBind(SearchFragment fragment, SearchResult result);
+    public void onBind(SearchFragment fragment, SearchResult result) {
+        titleView.setText(result.title);
+        if (TextUtils.isEmpty(result.summary)) {
+            summaryView.setVisibility(View.GONE);
+        } else {
+            summaryView.setText(result.summary);
+            summaryView.setVisibility(View.VISIBLE);
+        }
+        iconView.setImageDrawable(result.icon);
+        if (result.icon == null) {
+            iconView.setBackgroundResource(R.drawable.empty_icon);
+        }
+        bindBreadcrumbView(result);
+    }
+
+    private void bindBreadcrumbView(SearchResult result) {
+        if (result.breadcrumbs == null || result.breadcrumbs.isEmpty()) {
+            breadcrumbView.setVisibility(View.GONE);
+            return;
+        }
+        final Context context = breadcrumbView.getContext();
+        String breadcrumb = result.breadcrumbs.get(0);
+        final int count = result.breadcrumbs.size();
+        for (int i = 1; i < count; i++) {
+            breadcrumb = context.getString(R.string.search_breadcrumb_connector,
+                    breadcrumb, result.breadcrumbs.get(i));
+        }
+        breadcrumbView.setText(breadcrumb);
+        breadcrumbView.setVisibility(View.VISIBLE);
+    }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
index 3d469dd..e55dc10 100644
--- a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
+++ b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
@@ -196,7 +196,7 @@
     @Test
     public void testNullResource_NothingInserted() {
         mManager.indexOneSearchIndexableData(mDb, localeStr, null /* searchIndexableResource */,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         assertThat(cursor.getCount()).isEqualTo(0);
     }
@@ -205,7 +205,7 @@
     public void testAddResource_RowsInserted() {
         SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         assertThat(cursor.getCount()).isEqualTo(6);
     }
@@ -214,7 +214,7 @@
     public void testAddResourceHeader_RowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
 
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
         cursor.moveToPosition(1);
@@ -267,25 +267,27 @@
     public void testAddResourceCustomSetting_RowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
-
-        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
-        cursor.moveToPosition(0);
+                new HashMap<>());
+        final String prefTitle =
+                mContext.getString(R.string.fingerprint_swipe_for_notifications_title);
+        final String prefSummary =
+                mContext.getString(R.string.fingerprint_swipe_for_notifications_summary);
+        Cursor cursor = mDb.rawQuery(
+                "SELECT * FROM prefs_index where data_title='" + prefTitle + "'", null);
+        cursor.moveToFirst();
 
         // Locale
         assertThat(cursor.getString(0)).isEqualTo(localeStr);
         // Data Rank
         assertThat(cursor.getInt(1)).isEqualTo(rank);
         // Data Title
-        assertThat(cursor.getString(2)).isEqualTo("Swipe for notifications");
+        assertThat(cursor.getString(2)).isEqualTo(prefTitle);
         // Normalized Title
-        assertThat(cursor.getString(3)).isEqualTo("swipe for notifications");
+        assertThat(cursor.getString(3)).isEqualTo(prefTitle.toLowerCase());
         // Summary On
-        assertThat(cursor.getString(4)).isEqualTo("To check your notifications, " +
-                "swipe down on the fingerprint sensor on the back of your phone.");
+        assertThat(cursor.getString(4)).isEqualTo(prefSummary);
         // Summary On Normalized
-        assertThat(cursor.getString(5)).isEqualTo("to check your notifications, " +
-                "swipe down on the fingerprint sensor on the back of your phone.");
+        assertThat(cursor.getString(5)).isEqualTo(prefSummary.toLowerCase());
         // Summary Off - only on for checkbox preferences
         assertThat(cursor.getString(6)).isEmpty();
         // Summary off normalized - only on for checkbox preferences
@@ -295,7 +297,8 @@
         // Keywords
         assertThat(cursor.getString(9)).isEmpty();
         // Screen Title
-        assertThat(cursor.getString(10)).isEqualTo("Gestures");
+        assertThat(cursor.getString(10)).isEqualTo(
+                mContext.getString(R.string.gesture_preference_title));
         // Class Name
         assertThat(cursor.getString(11)).isEqualTo(className);
         // Icon
@@ -322,7 +325,7 @@
     public void testAddResourceCheckboxPreference_RowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
 
         /* Should return 6 results, with the following titles:
          * Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services
@@ -377,7 +380,7 @@
     public void testAddResourceListPreference_RowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
 
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
         cursor.moveToPosition(3);
@@ -436,7 +439,7 @@
         resource.className = "com.android.settings.display.ScreenZoomSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         assertThat(cursor.getCount()).isEqualTo(1);
     }
@@ -448,7 +451,7 @@
         resource.className = "com.android.settings.display.ScreenZoomSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         cursor.moveToPosition(0);
 
@@ -504,7 +507,7 @@
         resource.className = "com.android.settings.LegalSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
         assertThat(cursor.getCount()).isEqualTo(2);
     }
@@ -516,7 +519,7 @@
         resource.className = "com.android.settings.LegalSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
-                new HashMap<String, List<String>>());
+                new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
         cursor.moveToPosition(0);
 
diff --git a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
index 3874479..7a2499b 100644
--- a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
+++ b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
@@ -41,6 +41,7 @@
 import org.robolectric.shadows.ShadowApplication;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.any;
@@ -73,6 +74,7 @@
         assertThat(mHolder.titleView).isNotNull();
         assertThat(mHolder.summaryView).isNotNull();
         assertThat(mHolder.iconView).isNotNull();
+        assertThat(mHolder.breadcrumbView).isNotNull();
     }
 
     @Test
@@ -84,11 +86,43 @@
         assertThat(mHolder.titleView.getText()).isEqualTo(TITLE);
         assertThat(mHolder.summaryView.getText()).isEqualTo(SUMMARY);
         assertThat(mHolder.iconView.getDrawable()).isEqualTo(mIcon);
+        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.GONE);
 
         verify(mFragment).onSearchResultClicked();
         verify(mFragment).startActivity(any(Intent.class));
     }
 
+    @Test
+    public void testBindViewElements_emptySummary_hideSummaryView() {
+        final SearchResult result = new Builder().addTitle(TITLE)
+                .addRank(1)
+                .addPayload(new IntentPayload(null))
+                .addIcon(mIcon)
+                .build();
+
+        mHolder.onBind(mFragment, result);
+        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void testBindViewElements_withBreadcrumb_shouldFormatBreadcrumb() {
+        final List<String> breadcrumbs = new ArrayList<>();
+        breadcrumbs.add("a");
+        breadcrumbs.add("b");
+        breadcrumbs.add("c");
+        final SearchResult result = new Builder().addTitle(TITLE)
+                .addRank(1)
+                .addPayload(new IntentPayload(null))
+                .addBreadcrumbs(breadcrumbs)
+                .addIcon(mIcon)
+                .build();
+
+        mHolder.onBind(mFragment, result);
+        assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mHolder.breadcrumbView.getText()).isEqualTo("a > b > c");
+    }
+
     private SearchResult getSearchResult() {
         Builder builder = new Builder();
         builder.addTitle(TITLE)