Add suggestions to settings

Use an XML to define the categories that Settings will look for,
and surface enabled activities under those categories as suggestions.

When clicked on the activity will be started for result.  If the result
is not cancelled, then the operation is assumed successful and the
suggestion is disabled.  Users can also use an overflow -> remove
flow to get rid of unwanted suggestions.

Change-Id: I767abf8efe103af0509bc6b6b55888ae82643512
diff --git a/res/layout/dashboard_category.xml b/res/layout/dashboard_category.xml
index 9109440..e79f8d5 100644
--- a/res/layout/dashboard_category.xml
+++ b/res/layout/dashboard_category.xml
@@ -15,15 +15,14 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/category"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="@dimen/dashboard_category_padding_start"
-        android:paddingEnd="@dimen/dashboard_category_padding_end"
-        android:orientation="vertical"
-        android:paddingBottom="8dip"
-        android:background="@color/card_background"
-        android:elevation="@dimen/dashboard_category_elevation" >
+    android:id="@+id/category"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/dashboard_category_height"
+    android:paddingStart="@dimen/dashboard_category_padding_start"
+    android:paddingEnd="@dimen/dashboard_category_padding_end"
+    android:orientation="vertical"
+    android:paddingBottom="8dip"
+    android:background="@color/card_background" >
 
     <View
         android:layout_width="match_parent"
@@ -31,14 +30,14 @@
         android:background="?android:attr/listDivider" />
 
     <TextView android:id="@android:id/title"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/dashboard_category_title_height"
-            android:paddingStart="@dimen/dashboard_category_title_margin_start"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:gravity="center_vertical"
-            android:textAppearance="@style/TextAppearance.CategoryTitle"
-            android:textAlignment="viewStart"
-            />
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingStart="@dimen/dashboard_category_title_margin_start"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:gravity="center_vertical"
+        android:textAppearance="@style/TextAppearance.CategoryTitle"
+        android:textAlignment="viewStart"
+        />
 
 </LinearLayout>
diff --git a/res/layout/dashboard_tile.xml b/res/layout/dashboard_tile.xml
index cc237ee..cd7c8ee 100644
--- a/res/layout/dashboard_tile.xml
+++ b/res/layout/dashboard_tile.xml
@@ -21,8 +21,7 @@
         android:gravity="center_vertical"
         android:minHeight="@dimen/dashboard_tile_minimum_height"
         android:clickable="true"
-        android:background="@drawable/selectable_card"
-        android:elevation="@dimen/dashboard_category_elevation" >
+        android:background="@drawable/selectable_card" >
 
     <ImageView
             android:id="@android:id/icon"
diff --git a/res/layout/search_panel_results_header.xml b/res/layout/search_panel_results_header.xml
index 7b3f198..2deb273 100644
--- a/res/layout/search_panel_results_header.xml
+++ b/res/layout/search_panel_results_header.xml
@@ -16,7 +16,7 @@
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
           android:layout_width="match_parent"
-          android:layout_height="@dimen/dashboard_category_title_height"
+          android:layout_height="@dimen/dashboard_category_height"
           android:paddingStart="@dimen/search_title_padding_start"
           android:singleLine="true"
           android:ellipsize="marquee"
diff --git a/res/layout/search_panel_suggestions_header.xml b/res/layout/search_panel_suggestions_header.xml
index d97c3cc..98957e4 100644
--- a/res/layout/search_panel_suggestions_header.xml
+++ b/res/layout/search_panel_suggestions_header.xml
@@ -16,7 +16,7 @@
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
           android:layout_width="match_parent"
-          android:layout_height="@dimen/dashboard_category_title_height"
+          android:layout_height="@dimen/dashboard_category_height"
           android:paddingStart="@dimen/search_title_padding_start"
           android:singleLine="true"
           android:ellipsize="marquee"
diff --git a/res/layout/see_all.xml b/res/layout/see_all.xml
index fff4d07..af68036 100644
--- a/res/layout/see_all.xml
+++ b/res/layout/see_all.xml
@@ -21,8 +21,7 @@
         android:gravity="center_vertical"
         android:minHeight="@dimen/dashboard_tile_minimum_height"
         android:clickable="true"
-        android:background="@drawable/selectable_card"
-        android:elevation="@dimen/dashboard_category_elevation">
+        android:background="@drawable/selectable_card">
 
     <View
             android:layout_width="@dimen/dashboard_tile_image_size"
diff --git a/res/layout/suggestion_header.xml b/res/layout/suggestion_header.xml
new file mode 100644
index 0000000..512c53a
--- /dev/null
+++ b/res/layout/suggestion_header.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/dashboard_category_height"
+    android:clickable="true"
+    android:background="@drawable/selectable_card"
+    android:elevation="@dimen/dashboard_category_elevation"
+    android:gravity="center_vertical" >
+
+    <TextView android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:layout_marginStart="@dimen/suggestion_arrow_margin"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.TileTitle"
+        android:textColor="?android:attr/colorAccent"
+        android:alpha=".87"
+        android:ellipsize="marquee"
+        android:fadingEdge="horizontal" />
+
+    <ImageView android:id="@android:id/icon"
+        android:layout_width="@dimen/dashboard_tile_image_size"
+        android:layout_height="@dimen/dashboard_tile_image_size"
+        android:layout_marginStart="@dimen/suggestion_arrow_margin"
+        android:layout_marginEnd="@dimen/suggestion_arrow_margin"
+        android:src="@drawable/ic_expand_more" />
+
+</LinearLayout>
diff --git a/res/layout/suggestion_tile.xml b/res/layout/suggestion_tile.xml
new file mode 100644
index 0000000..0b5e652
--- /dev/null
+++ b/res/layout/suggestion_tile.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clickable="true"
+    android:background="@drawable/selectable_card"
+    android:elevation="@dimen/dashboard_category_elevation"
+    android:orientation="vertical" >
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?android:attr/listDivider" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:minHeight="@dimen/dashboard_tile_minimum_height">
+
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="@dimen/dashboard_tile_image_size"
+            android:layout_height="@dimen/dashboard_tile_image_size"
+            android:scaleType="centerInside"
+            android:layout_marginStart="@dimen/dashboard_tile_image_margin_start"
+            android:layout_marginEnd="@dimen/dashboard_tile_image_margin_end" />
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+
+            <TextView android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:textAppearance="@style/TextAppearance.TileTitle"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal" />
+
+            <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="@style/TextAppearance.Small"
+                android:textColor="?android:attr/textColorSecondary" />
+
+        </RelativeLayout>
+
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent">
+
+            <ImageView android:id="@+id/overflow"
+                android:layout_width="44dp"
+                android:layout_height="44dp"
+                android:paddingStart="16dp"
+                android:paddingBottom="16dp"
+                android:paddingTop="12dp"
+                android:paddingEnd="12dp"
+                android:gravity="top"
+                style="?android:attr/actionOverflowButtonStyle" />
+
+        </FrameLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 539dc05..79655e4 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -103,8 +103,8 @@
     <!-- Dashboard category panel elevation -->
     <dimen name="dashboard_category_elevation">2dp</dimen>
 
-    <!-- Dashboard category title layout height -->
-    <dimen name="dashboard_category_title_height">48dp</dimen>
+    <!-- Dashboard category layout height -->
+    <dimen name="dashboard_category_height">48dp</dimen>
 
     <!-- Dashboard category title margin start -->
     <dimen name="dashboard_category_title_margin_start">16dp</dimen>
@@ -119,6 +119,8 @@
     <dimen name="dashboard_tile_image_margin_start">16dp</dimen>
     <dimen name="dashboard_tile_image_margin_end">32dp</dimen>
 
+    <dimen name="suggestion_arrow_margin">16dp</dimen>
+
     <!-- SwitchBar margin start / end -->
     <dimen name="switchbar_margin_start">16dp</dimen>
     <dimen name="switchbar_margin_end">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e7442ed..cf8acea 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6767,4 +6767,10 @@
     <!-- Summary of condition that background data is off [CHAR LIMIT=NONE] -->
     <string name="condition_bg_data_summary">Background data is only available via Wi-Fi. This may affect some apps or services when Wi-Fi is not available.</string>
 
+    <!-- Title for the suggestions section on the dashboard [CHAR LIMIT=30] -->
+    <string name="suggestions_title">Suggestions (<xliff:g name="count" example="3">%1$d</xliff:g>)</string>
+
+    <!-- Name of option to remove a suggestion from the list [CHAR LIMIT=30] -->
+    <string name="suggestion_remove">Remove</string>
+
 </resources>
diff --git a/res/xml/suggestion_ordering.xml b/res/xml/suggestion_ordering.xml
new file mode 100644
index 0000000..339b195
--- /dev/null
+++ b/res/xml/suggestion_ordering.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<optional-steps>
+    <step category="com.android.settings.suggested.category.LOCK_SCREEN" />
+    <step category="com.android.settings.suggested.category.EMAIL" />
+    <step category="com.android.settings.suggested.category.PAYMENT" />
+    <step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
+        multiple="true" />
+    <step category="com.android.settings.suggested.category.DEFAULT"
+        multiple="true" />
+</optional-steps>
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 4d9e2f0..ba975a2 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -50,7 +50,6 @@
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.SearchView;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.ArrayUtils;
 import com.android.settings.Settings.WifiSettingsActivity;
 import com.android.settings.accessibility.AccessibilitySettings;
@@ -111,7 +110,7 @@
 import com.android.settings.wifi.WifiSettings;
 import com.android.settings.wifi.p2p.WifiP2pSettings;
 import com.android.settingslib.drawer.DashboardCategory;
-import com.android.settingslib.drawer.DashboardTile;
+import com.android.settingslib.drawer.Tile;
 import com.android.settingslib.drawer.SettingsDrawerActivity;
 
 import java.util.ArrayList;
@@ -199,6 +198,8 @@
 
     private static final String EMPTY_QUERY = "";
 
+    private static final int REQUEST_SUGGESTION = 42;
+
     private String mFragmentClass;
 
     private CharSequence mInitialTitle;
@@ -365,6 +366,7 @@
     private int mHomeActivitiesCount = 1;
 
     private Intent mResultIntentData;
+    private ComponentName mCurrentSuggestion;
 
     public SwitchBar getSwitchBar() {
         return mSwitchBar;
@@ -1031,7 +1033,7 @@
             // When on restricted users, disable all extra categories (but only the settings ones).
             List<DashboardCategory> categories = getDashboardCategories();
             for (DashboardCategory category : categories) {
-                for (DashboardTile tile : category.tiles) {
+                for (Tile tile : category.tiles) {
                     ComponentName component = tile.intent.getComponent();
                     if (packageName.equals(component)&& !ArrayUtils.contains(
                             SETTINGS_FOR_RESTRICTED, component.getClassName())) {
@@ -1146,7 +1148,7 @@
     }
 
     @Override
-    protected void onTileClicked(DashboardTile tile) {
+    protected void onTileClicked(Tile tile) {
         if (mIsShowingDashboard) {
             // If on dashboard, don't finish so the back comes back to here.
             openTile(tile);
@@ -1200,4 +1202,20 @@
     public void setResultIntentData(Intent resultIntentData) {
         mResultIntentData = resultIntentData;
     }
+
+    public void startSuggestion(Intent intent) {
+        mCurrentSuggestion = intent.getComponent();
+        startActivityForResult(intent, REQUEST_SUGGESTION);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_SUGGESTION && mCurrentSuggestion != null
+                && resultCode != RESULT_CANCELED) {
+            getPackageManager().setComponentEnabledSetting(mCurrentSuggestion,
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
+        }
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
 }
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
index a53d8b6..b564a27 100644
--- a/src/com/android/settings/dashboard/DashboardAdapter.java
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -16,10 +16,14 @@
 package com.android.settings.dashboard;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.support.v7.widget.PopupMenu;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
@@ -30,14 +34,21 @@
 import com.android.settings.dashboard.conditional.Condition;
 import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
 import com.android.settingslib.drawer.DashboardCategory;
-import com.android.settingslib.drawer.DashboardTile;
+import com.android.settingslib.drawer.Tile;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder> implements View.OnClickListener {
+public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
+        implements View.OnClickListener {
     public static final String TAG = "DashboardAdapter";
 
+    private static int SUGGESTION_MODE_DEFAULT = 0;
+    private static int SUGGESTION_MODE_COLLAPSED = 1;
+    private static int SUGGESTION_MODE_EXPANDED = 2;
+
+    private static final int DEFAULT_SUGGESTION_COUNT = 2;
+
     private final List<Object> mItems = new ArrayList<>();
     private final List<Integer> mTypes = new ArrayList<>();
     private final List<Integer> mIds = new ArrayList<>();
@@ -46,11 +57,14 @@
 
     private List<DashboardCategory> mCategories;
     private List<Condition> mConditions;
+    private List<Tile> mSuggestions;
 
     private boolean mIsShowingAll;
     // Used for counting items;
     private int mId;
 
+    private int mSuggestionMode = SUGGESTION_MODE_DEFAULT;
+
     private Condition mExpandedCondition = null;
 
     public DashboardAdapter(Context context) {
@@ -59,6 +73,11 @@
         setHasStableIds(true);
     }
 
+    public void setSuggestions(List<Tile> suggestions) {
+        mSuggestions = suggestions;
+        recountItems();
+    }
+
     public void setCategories(List<DashboardCategory> categories) {
         mCategories = categories;
 
@@ -68,7 +87,7 @@
                 tintColor, true);
         for (int i = 0; i < categories.size(); i++) {
             for (int j = 0; j < categories.get(i).tiles.size(); j++) {
-                DashboardTile tile = categories.get(i).tiles.get(j);
+                Tile tile = categories.get(i).tiles.get(j);
 
                 if (!mContext.getPackageName().equals(
                         tile.intent.getComponent().getPackageName())) {
@@ -78,34 +97,53 @@
                 }
             }
         }
-        setShowingAll(mIsShowingAll);
+        recountItems();
     }
 
     public void setConditions(List<Condition> conditions) {
         mConditions = conditions;
-        setShowingAll(mIsShowingAll);
+        recountItems();
     }
 
     public boolean isShowingAll() {
         return mIsShowingAll;
     }
 
-    public void notifyChanged(DashboardTile tile) {
+    public void notifyChanged(Tile tile) {
         notifyDataSetChanged();
     }
 
     public void setShowingAll(boolean showingAll) {
         mIsShowingAll = showingAll;
+        recountItems();
+    }
+
+    private void recountItems() {
         reset();
+        boolean hasConditions = false;
         for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
-            countItem(mConditions.get(i), R.layout.condition_card, mConditions.get(i).shouldShow());
+            boolean shouldShow = mConditions.get(i).shouldShow();
+            hasConditions |= shouldShow;
+            countItem(mConditions.get(i), R.layout.condition_card, shouldShow);
+        }
+        boolean hasSuggestions = mSuggestions != null && mSuggestions.size() != 0;
+        countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions);
+        countItem(null, R.layout.suggestion_header, hasSuggestions);
+        if (mSuggestions != null) {
+            int maxSuggestions = mSuggestionMode == SUGGESTION_MODE_DEFAULT
+                    ? Math.min(DEFAULT_SUGGESTION_COUNT, mSuggestions.size())
+                    : mSuggestionMode == SUGGESTION_MODE_EXPANDED ? mSuggestions.size()
+                    : 0;
+            for (int i = 0; i < mSuggestions.size(); i++) {
+                countItem(mSuggestions.get(i), R.layout.suggestion_tile, i < maxSuggestions);
+            }
         }
         countItem(null, R.layout.dashboard_spacer, true);
         for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
             DashboardCategory category = mCategories.get(i);
             countItem(category, R.layout.dashboard_category, mIsShowingAll);
             for (int j = 0; j < category.tiles.size(); j++) {
-                DashboardTile tile = category.tiles.get(j);
+                Tile tile = category.tiles.get(j);
                 countItem(tile, R.layout.dashboard_tile, mIsShowingAll
                         || ArrayUtils.contains(DashboardSummary.INITIAL_ITEMS,
                         tile.intent.getComponent().getClassName()));
@@ -126,6 +164,7 @@
         if (add) {
             mItems.add(object);
             mTypes.add(type);
+            // TODO: Counting namespaces for handling of suggestions/conds appearing/disappearing.
             mIds.add(mId);
         }
         mId++;
@@ -144,7 +183,7 @@
                 onBindCategory(holder, (DashboardCategory) mItems.get(position));
                 break;
             case R.layout.dashboard_tile:
-                final DashboardTile tile = (DashboardTile) mItems.get(position);
+                final Tile tile = (Tile) mItems.get(position);
                 onBindTile(holder, tile);
                 holder.itemView.setOnClickListener(new View.OnClickListener() {
                     @Override
@@ -153,14 +192,28 @@
                     }
                 });
                 break;
-            case R.layout.see_all:
-                onBindSeeAll(holder);
+            case R.layout.suggestion_header:
+                onBindSuggestionHeader(holder);
+                break;
+            case R.layout.suggestion_tile:
+                final Tile suggestion = (Tile) mItems.get(position);
+                onBindTile(holder, suggestion);
                 holder.itemView.setOnClickListener(new View.OnClickListener() {
                     @Override
                     public void onClick(View v) {
-                        setShowingAll(!mIsShowingAll);
+                        ((SettingsActivity) mContext).startSuggestion(suggestion.intent);
                     }
                 });
+                holder.itemView.findViewById(R.id.overflow).setOnClickListener(
+                        new View.OnClickListener() {
+                            @Override
+                            public void onClick(View v) {
+                                showRemoveOption(v, suggestion);
+                            }
+                        });
+                break;
+            case R.layout.see_all:
+                onBindSeeAll(holder);
                 break;
             case R.layout.condition_card:
                 ConditionAdapterUtils.bindViews((Condition) mItems.get(position), holder,
@@ -175,11 +228,53 @@
         }
     }
 
-    private void onBindTile(DashboardItemHolder holder, DashboardTile dashboardTile) {
-        holder.icon.setImageIcon(dashboardTile.icon);
-        holder.title.setText(dashboardTile.title);
-        if (!TextUtils.isEmpty(dashboardTile.summary)) {
-            holder.summary.setText(dashboardTile.summary);
+    private void showRemoveOption(View v, final Tile suggestion) {
+        PopupMenu popup = new PopupMenu(
+                new ContextThemeWrapper(mContext, R.style.Theme_AppCompat_DayNight), v);
+        popup.getMenu().add(R.string.suggestion_remove).setOnMenuItemClickListener(
+                new MenuItem.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                mContext.getPackageManager().setComponentEnabledSetting(
+                        suggestion.intent.getComponent(),
+                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                        PackageManager.DONT_KILL_APP);
+                mSuggestions.remove(suggestion);
+                recountItems();
+                return true;
+            }
+        });
+        popup.show();
+    }
+
+    private void onBindSuggestionHeader(final DashboardItemHolder holder) {
+        holder.icon.setImageResource(hasMoreSuggestions() ? R.drawable.ic_expand_more
+                : R.drawable.ic_expand_less);
+        holder.title.setText(mContext.getString(R.string.suggestions_title, mSuggestions.size()));
+        holder.itemView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (hasMoreSuggestions()) {
+                    mSuggestionMode = SUGGESTION_MODE_EXPANDED;
+                } else {
+                    mSuggestionMode = SUGGESTION_MODE_COLLAPSED;
+                }
+                recountItems();
+            }
+        });
+    }
+
+    private boolean hasMoreSuggestions() {
+        return mSuggestionMode == SUGGESTION_MODE_COLLAPSED
+                || (mSuggestionMode == SUGGESTION_MODE_DEFAULT
+                && mSuggestions.size() > DEFAULT_SUGGESTION_COUNT);
+    }
+
+    private void onBindTile(DashboardItemHolder holder, Tile tile) {
+        holder.icon.setImageIcon(tile.icon);
+        holder.title.setText(tile.title);
+        if (!TextUtils.isEmpty(tile.summary)) {
+            holder.summary.setText(tile.summary);
             holder.summary.setVisibility(View.VISIBLE);
         } else {
             holder.summary.setVisibility(View.GONE);
@@ -193,6 +288,12 @@
     private void onBindSeeAll(DashboardItemHolder holder) {
         holder.title.setText(mIsShowingAll ? R.string.see_less
                 : R.string.see_all);
+        holder.itemView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setShowingAll(!mIsShowingAll);
+            }
+        });
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index 95170a4..cbb1549 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -33,6 +33,7 @@
 import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
 import com.android.settings.dashboard.conditional.ConditionManager;
 import com.android.settings.dashboard.conditional.FocusRecyclerView;
+import com.android.settingslib.SuggestionParser;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.SettingsDrawerActivity;
 
@@ -58,6 +59,7 @@
     private DashboardAdapter mAdapter;
     private SummaryLoader mSummaryLoader;
     private ConditionManager mConditionManager;
+    private SuggestionParser mSuggestionParser;
 
     @Override
     protected int getMetricsCategory() {
@@ -76,6 +78,7 @@
         if (DEBUG_TIMING) Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
                 + " ms");
         mConditionManager = ConditionManager.get(getContext());
+        mSuggestionParser = new SuggestionParser(getContext(), R.xml.suggestion_ordering);
     }
 
     @Override
@@ -135,6 +138,7 @@
         mDashboard.setListener(this);
         mAdapter = new DashboardAdapter(getContext());
         mAdapter.setConditions(mConditionManager.getConditions());
+        mAdapter.setSuggestions(mSuggestionParser.getSuggestions());
         mSummaryLoader.setAdapter(mAdapter);
         ConditionAdapterUtils.addDismiss(mDashboard);
 
diff --git a/src/com/android/settings/dashboard/SummaryLoader.java b/src/com/android/settings/dashboard/SummaryLoader.java
index a9dd43a..a94ae83 100644
--- a/src/com/android/settings/dashboard/SummaryLoader.java
+++ b/src/com/android/settings/dashboard/SummaryLoader.java
@@ -25,7 +25,7 @@
 import android.util.Log;
 import com.android.settings.SettingsActivity;
 import com.android.settingslib.drawer.DashboardCategory;
-import com.android.settingslib.drawer.DashboardTile;
+import com.android.settingslib.drawer.Tile;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -38,8 +38,8 @@
     public static final String SUMMARY_PROVIDER_FACTORY = "SUMMARY_PROVIDER_FACTORY";
 
     private final Activity mActivity;
-    private final ArrayMap<SummaryProvider, DashboardTile> mSummaryMap = new ArrayMap<>();
-    private final List<DashboardTile> mTiles = new ArrayList<>();
+    private final ArrayMap<SummaryProvider, Tile> mSummaryMap = new ArrayMap<>();
+    private final List<Tile> mTiles = new ArrayList<>();
 
     private final Worker mWorker;
     private final Handler mHandler;
@@ -54,9 +54,9 @@
         mWorker = new Worker(mWorkerThread.getLooper());
         mActivity = activity;
         for (int i = 0; i < categories.size(); i++) {
-            List<DashboardTile> tiles = categories.get(i).tiles;
+            List<Tile> tiles = categories.get(i).tiles;
             for (int j = 0; j < tiles.size(); j++) {
-                DashboardTile tile = tiles.get(j);
+                Tile tile = tiles.get(j);
                 mWorker.obtainMessage(Worker.MSG_GET_PROVIDER, tile).sendToTarget();
             }
         }
@@ -71,7 +71,7 @@
     }
 
     public void setSummary(SummaryProvider provider, final CharSequence summary) {
-        final DashboardTile tile = mSummaryMap.get(provider);
+        final Tile tile = mSummaryMap.get(provider);
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -85,7 +85,7 @@
         mWorker.obtainMessage(Worker.MSG_SET_LISTENING, listening ? 1 : 0, 0).sendToTarget();
     }
 
-    private SummaryProvider getSummaryProvider(DashboardTile tile) {
+    private SummaryProvider getSummaryProvider(Tile tile) {
         if (!mActivity.getPackageName().equals(tile.intent.getComponent().getPackageName())) {
             // Not within Settings, can't load Summary directly.
             // TODO: Load summary indirectly.
@@ -118,7 +118,7 @@
         return null;
     }
 
-    private Bundle getMetaData(DashboardTile tile) {
+    private Bundle getMetaData(Tile tile) {
         return tile.metaData;
     }
 
@@ -142,7 +142,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_GET_PROVIDER:
-                    DashboardTile tile = (DashboardTile) msg.obj;
+                    Tile tile = (Tile) msg.obj;
                     SummaryProvider provider = getSummaryProvider(tile);
                     if (provider != null) {
                         if (DEBUG) Log.d(TAG, "Creating " + tile);