Add external Preferences indexing

- define SettingsSearchIndexablesProvider as an internal
SearchIndexablesProvider
- protect access thru using android.permission.READ_SEARCH_INDEXABLES
- update WallpaperTypeSettings and WifiSettings for taking care of
the new model
- update the Dashboard for taking care about external Icons for the
search result
- update sqlite model/version for taking care about Intents
(enable launching external applications for showing the settings)

Change-Id: I2e38599327e6480f1754f52666becce0884cee9d
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index e49d827..0e6128c 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -94,14 +94,12 @@
 import com.android.settings.deviceinfo.Memory;
 import com.android.settings.deviceinfo.UsbSettings;
 import com.android.settings.fuelgauge.PowerUsageSummary;
-import com.android.settings.indexer.Index;
-import com.android.settings.indexer.IndexableRef;
+import com.android.settings.search.Index;
 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
 import com.android.settings.inputmethod.SpellCheckersSettings;
 import com.android.settings.inputmethod.UserDictionaryList;
 import com.android.settings.location.LocationSettings;
-import com.android.settings.net.DataUsageMeteredSettings;
 import com.android.settings.nfc.AndroidBeam;
 import com.android.settings.nfc.PaymentSettings;
 import com.android.settings.print.PrintJobSettingsFragment;
@@ -342,76 +340,6 @@
         }
     };
 
-    private static int NO_DATA_RES_ID = 0;
-
-    /**
-     * Indexable data description.
-     *
-     * Known restriction: we are only searching (for now) the first level of Settings.
-     */
-    private static IndexableRef[] INDEXABLE_REFS = new IndexableRef[] {
-            new IndexableRef(1, NO_DATA_RES_ID,
-                    WifiSettings.class.getName(),
-                    R.drawable.ic_settings_wireless),
-            new IndexableRef(2, R.xml.bluetooth_settings,
-                    BluetoothSettings.class.getName(),
-                    R.drawable.ic_settings_bluetooth2),
-            new IndexableRef(3, R.xml.data_usage_metered_prefs,
-                    DataUsageMeteredSettings.class.getName(),
-                    R.drawable.ic_settings_data_usage),
-            new IndexableRef(4, R.xml.wireless_settings,
-                    WirelessSettings.class.getName(),
-                    R.drawable.empty_icon),
-            new IndexableRef(5, R.xml.home_selection,
-                    HomeSettings.class.getName(),
-                    R.drawable.ic_settings_home),
-            new IndexableRef(6, R.xml.sound_settings,
-                    SoundSettings.class.getName(),
-                    R.drawable.ic_settings_sound),
-            new IndexableRef(7, R.xml.display_settings,
-                    DisplaySettings.class.getName(),
-                    R.drawable.ic_settings_display),
-            new IndexableRef(7, NO_DATA_RES_ID,
-                    WallpaperTypeSettings.class.getName(),
-                    R.drawable.ic_settings_display),
-            new IndexableRef(8, R.xml.device_info_memory,
-                    Memory.class.getName(),
-                    R.drawable.ic_settings_storage),
-            new IndexableRef(9, R.xml.power_usage_summary,
-                    PowerUsageSummary.class.getName(),
-                    R.drawable.ic_settings_battery),
-            new IndexableRef(10, R.xml.user_settings,
-                    UserSettings.class.getName(),
-                    R.drawable.ic_settings_multiuser),
-            new IndexableRef(11, R.xml.location_settings,
-                    LocationSettings.class.getName(),
-                    R.drawable.ic_settings_location),
-            new IndexableRef(12, R.xml.security_settings,
-                    SecuritySettings.class.getName(),
-                    R.drawable.ic_settings_security),
-            new IndexableRef(13, R.xml.language_settings,
-                    InputMethodAndLanguageSettings.class.getName(),
-                    R.drawable.ic_settings_language),
-            new IndexableRef(14, R.xml.privacy_settings,
-                    PrivacySettings.class.getName(),
-                    R.drawable.ic_settings_backup),
-            new IndexableRef(15, R.xml.date_time_prefs,
-                    DateTimeSettings.class.getName(),
-                    R.drawable.ic_settings_date_time),
-            new IndexableRef(16, R.xml.accessibility_settings,
-                    AccessibilitySettings.class.getName(),
-                    R.drawable.ic_settings_accessibility),
-            new IndexableRef(17, R.xml.print_settings,
-                    PrintSettingsFragment.class.getName(),
-                    com.android.internal.R.drawable.ic_print),
-            new IndexableRef(18, R.xml.development_prefs,
-                    DevelopmentSettings.class.getName(),
-                    R.drawable.ic_settings_development),
-            new IndexableRef(19, R.xml.device_info_settings,
-                    DeviceInfoSettings.class.getName(),
-                    R.drawable.ic_settings_about),
-    };
-
     @Override
     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
         // Override the fragment title for Wallpaper settings
@@ -552,7 +480,6 @@
             getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
         }
 
-        Index.getInstance(this).addIndexableData(INDEXABLE_REFS);
         Index.getInstance(this).update();
 
         mAuthenticatorHelper = new AuthenticatorHelper();
@@ -914,22 +841,6 @@
     }
 
     /**
-     * Switch the fragment pane to show the given preference fragment.
-     *
-     * (used for initial fragment)
-     *
-     * @param fragmentName The name of the fragment to display.
-     * @param args Optional arguments to supply to the fragment.
-     * @param validate true means that the fragment's Header needs to be validated.
-     * @param title The title of the fragment to display.
-     */
-    private void switchToHeader(String fragmentName, Bundle args, boolean validate,
-                                CharSequence title) {
-        setSelectedHeader(null);
-        switchToHeaderInner(fragmentName, args, validate, false, title);
-    }
-
-    /**
      * Switch to a specific Header with taking care of validation, Title and BackStack
      */
     private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
diff --git a/src/com/android/settings/WallpaperTypeSettings.java b/src/com/android/settings/WallpaperTypeSettings.java
index f46315a..7dc5e4d 100644
--- a/src/com/android/settings/WallpaperTypeSettings.java
+++ b/src/com/android/settings/WallpaperTypeSettings.java
@@ -24,9 +24,9 @@
 import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
-import com.android.settings.indexer.Indexable;
-import com.android.settings.indexer.IndexableData;
-import com.android.settings.indexer.IndexableRef;
+import android.provider.SearchIndexableResource;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableRaw;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -64,16 +64,16 @@
         }
     }
 
-    public static final IndexDataProvider INDEX_DATA_PROVIDER =
-        new IndexDataProvider() {
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+        new SearchIndexProvider() {
             @Override
-            public List<IndexableRef> getRefsToIndex(Context context) {
+            public List<SearchIndexableResource> getXmlResourcesToIndex(Context context) {
                 return null;
             }
 
             @Override
-            public List<IndexableData> getRawDataToIndex(Context context) {
-                final List<IndexableData> result = new ArrayList<IndexableData>();
+            public List<SearchIndexableRaw> getRawDataToIndex(Context context) {
+                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
 
                 final Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);
                 final PackageManager pm = context.getPackageManager();
@@ -82,17 +82,14 @@
 
                 // Add indexable data for each of the matching activities
                 for (ResolveInfo info : rList) {
-                    Intent prefIntent = new Intent(intent);
-                    prefIntent.setComponent(new ComponentName(
-                            info.activityInfo.packageName, info.activityInfo.name));
                     CharSequence label = info.loadLabel(pm);
                     if (label == null) label = info.activityInfo.packageName;
 
-                    IndexableData data = new IndexableData();
+                    SearchIndexableRaw data = new SearchIndexableRaw(context);
                     data.title = label.toString();
-                    data.fragmentTitle = context.getResources().getString(
+                    data.screenTitle = context.getResources().getString(
                             R.string.wallpaper_settings_fragment_title);
-                    data.intentAction = intent.getAction();
+                    data.intentAction = Intent.ACTION_SET_WALLPAPER;
                     data.intentTargetPackage = info.activityInfo.packageName;
                     data.intentTargetClass = info.activityInfo.name;
                     result.add(data);
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index 0e034ff..b48e07e 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -17,13 +17,19 @@
 package com.android.settings.dashboard;
 
 import android.app.Fragment;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -36,10 +42,14 @@
 import android.widget.TextView;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
-import com.android.settings.indexer.Index;
+import com.android.settings.search.Index;
+
+import java.util.HashMap;
 
 public class DashboardSummary extends Fragment {
 
+    private static final String LOG_TAG = "DashboardSummary";
+
     private EditText mEditText;
     private ListView mListView;
 
@@ -124,13 +134,33 @@
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 closeSoftKeyboard();
+
                 final Cursor cursor = mAdapter.mCursor;
                 cursor.moveToPosition(position);
-                final String fragmentName = cursor.getString(Index.COLUMN_INDEX_FRAGMENT_NAME);
-                final String fragmentTitle = cursor.getString(Index.COLUMN_INDEX_FRAGMENT_TITLE);
 
-                ((SettingsActivity) getActivity()).startPreferencePanel(fragmentName, null, 0,
-                        fragmentTitle, null, 0);
+                final String className = cursor.getString(Index.COLUMN_INDEX_CLASS_NAME);
+                final String screenTitle = cursor.getString(Index.COLUMN_INDEX_SCREEN_TITLE);
+
+                final String action = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION);
+
+                if (TextUtils.isEmpty(action)) {
+                    ((SettingsActivity) getActivity()).startPreferencePanel(className, null, 0,
+                            screenTitle, null, 0);
+                } else {
+                    final Intent intent = new Intent(action);
+
+                    final String targetPackage = cursor.getString(
+                            Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
+                    final String targetClass = cursor.getString(
+                            Index.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS);
+                    if (!TextUtils.isEmpty(targetPackage) && !TextUtils.isEmpty(targetClass)) {
+                        final ComponentName component =
+                                new ComponentName(targetPackage, targetClass);
+                        intent.setComponent(component);
+                    }
+
+                    getActivity().startActivity(intent);
+                }
             }
         });
 
@@ -190,8 +220,10 @@
         public String title;
         public String summary;
         public int iconResId;
+        public Context context;
 
-        public SearchResult(String title, String summary, int iconResId) {
+        public SearchResult(Context context, String title, String summary, int iconResId) {
+            this.context = context;
             this.title = title;
             this.summary = summary;
             this.iconResId = iconResId;
@@ -203,9 +235,12 @@
         private Cursor mCursor;
         private LayoutInflater mInflater;
         private boolean mDataValid;
+        private Context mContext;
+        private HashMap<String, Context> mContextMap = new HashMap<String, Context>();
 
         public SearchResultsAdapter(Context context) {
-            mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            mContext = context;
+            mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             mDataValid = false;
         }
 
@@ -237,9 +272,29 @@
                 final String title = mCursor.getString(Index.COLUMN_INDEX_TITLE);
                 final String summary = mCursor.getString(Index.COLUMN_INDEX_SUMMARY);
                 final String iconResStr = mCursor.getString(Index.COLUMN_INDEX_ICON);
-                final int iconResId =
-                        TextUtils.isEmpty(iconResStr) ? 0 : Integer.parseInt(iconResStr);
-                return new SearchResult(title, summary, iconResId);
+                final String className = mCursor.getString(
+                        Index.COLUMN_INDEX_CLASS_NAME);
+                final String packageName = mCursor.getString(
+                        Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
+
+                Context packageContext;
+                if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(packageName)) {
+                    packageContext = mContextMap.get(packageName);
+                    if (packageContext == null) {
+                        try {
+                            packageContext = mContext.createPackageContext(packageName, 0);
+                        } catch (PackageManager.NameNotFoundException e) {
+                            Log.e(LOG_TAG, "Cannot create Context for package: " + packageName);
+                            return null;
+                        }
+                        mContextMap.put(packageName, packageContext);
+                    }
+                } else {
+                    packageContext = mContext;
+                }
+                final int iconResId = TextUtils.isEmpty(iconResStr) ?
+                        R.drawable.empty_icon : Integer.parseInt(iconResStr);
+                return new SearchResult(packageContext, title, summary, iconResId);
             }
             return null;
         }
@@ -278,7 +333,15 @@
             textTitle.setText(result.title);
             textSummary.setText(result.summary);
             if (result.iconResId != R.drawable.empty_icon) {
-                imageView.setImageResource(result.iconResId);
+                final Context packageContext = result.context;
+                final Drawable drawable;
+                try {
+                    drawable = packageContext.getDrawable(result.iconResId);
+                    imageView.setImageDrawable(drawable);
+                } catch (Resources.NotFoundException nfe) {
+                    // Not much we can do except logging
+                    Log.e(LOG_TAG, "Cannot load Drawable for " + result.title);
+                }
                 imageView.setBackgroundResource(R.color.background_search_result_icon);
             } else {
                 imageView.setImageDrawable(null);
diff --git a/src/com/android/settings/indexer/Index.java b/src/com/android/settings/indexer/Index.java
deleted file mode 100644
index 19b545f..0000000
--- a/src/com/android/settings/indexer/Index.java
+++ /dev/null
@@ -1,524 +0,0 @@
-/*
- * Copyright (C) 2014 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.settings.indexer;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.os.AsyncTask;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.util.Xml;
-import com.android.settings.R;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static com.android.settings.indexer.IndexDatabaseHelper.Tables;
-import static com.android.settings.indexer.IndexDatabaseHelper.IndexColumns;
-
-public class Index {
-
-    private static final String LOG_TAG = "Index";
-
-    // Those indices should match the indices of SELECT_COLUMNS !
-    public static final int COLUMN_INDEX_TITLE = 1;
-    public static final int COLUMN_INDEX_SUMMARY = 2;
-    public static final int COLUMN_INDEX_FRAGMENT_NAME = 4;
-    public static final int COLUMN_INDEX_FRAGMENT_TITLE = 5;
-    public static final int COLUMN_INDEX_ICON = 7;
-
-    // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values
-    private static final String[] SELECT_COLUMNS = new String[] {
-            IndexColumns.DATA_RANK,
-            IndexColumns.DATA_TITLE,
-            IndexColumns.DATA_SUMMARY,
-            IndexColumns.DATA_KEYWORDS,
-            IndexColumns.FRAGMENT_NAME,
-            IndexColumns.FRAGMENT_TITLE,
-            IndexColumns.INTENT,
-            IndexColumns.ICON
-    };
-
-    private static final String[] MATCH_COLUMNS = {
-            IndexColumns.DATA_TITLE,
-            IndexColumns.DATA_TITLE_NORMALIZED,
-            IndexColumns.DATA_SUMMARY,
-            IndexColumns.DATA_SUMMARY_NORMALIZED,
-            IndexColumns.DATA_KEYWORDS
-    };
-
-    private static final String EMPTY = "";
-    private static final String NON_BREAKING_HYPHEN = "\u2011";
-    private static final String HYPHEN = "-";
-
-    private static Index sInstance;
-
-    private final AtomicBoolean mIsAvailable = new AtomicBoolean(false);
-
-    private final UpdateData mUpdateData = new UpdateData();
-
-    private final Context mContext;
-
-    /**
-     * A basic singleton
-     */
-    public static Index getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new Index(context);
-        }
-        return sInstance;
-    }
-
-    public Index(Context context) {
-        mContext = context;
-    }
-
-    public boolean isAvailable() {
-        return mIsAvailable.get();
-    }
-
-    public void addIndexableData(IndexableRef[] array) {
-        synchronized (mUpdateData) {
-            final int count = array.length;
-            for (int n = 0; n < count; n++) {
-                mUpdateData.dataToAdd.add(array[n]);
-            }
-        }
-    }
-
-    public void deleteIndexableData(String[] array) {
-        synchronized (mUpdateData) {
-            final int count = array.length;
-            for (int n = 0; n < count; n++) {
-                mUpdateData.dataToDelete.add(array[n]);
-            }
-        }
-    }
-
-    public boolean update() {
-        synchronized (mUpdateData) {
-            final UpdateIndexTask task = new UpdateIndexTask();
-            task.execute(mUpdateData);
-            try {
-                final boolean result = task.get();
-                mUpdateData.clear();
-                return result;
-            } catch (InterruptedException e) {
-                Log.e(LOG_TAG, "Cannot update index: " + e.getMessage());
-                return false;
-            } catch (ExecutionException e) {
-                Log.e(LOG_TAG, "Cannot update index: " + e.getMessage());
-                return false;
-            }
-        }
-    }
-
-    public Cursor search(String query) {
-        final String sql = buildSQL(query);
-        Log.d(LOG_TAG, "Query: " + sql);
-        return getReadableDatabase().rawQuery(sql, null);
-    }
-
-    private String buildSQL(String query) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(buildSQLForColumn(query, MATCH_COLUMNS));
-        sb.append(" ORDER BY ");
-        sb.append(IndexColumns.DATA_RANK);
-        return sb.toString();
-    }
-
-    private String buildSQLForColumn(String query, String[] columnNames) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("SELECT ");
-        for (int n = 0; n < SELECT_COLUMNS.length; n++) {
-            sb.append(SELECT_COLUMNS[n]);
-            if (n < SELECT_COLUMNS.length - 1) {
-                sb.append(", ");
-            }
-        }
-        sb.append(" FROM ");
-        sb.append(Tables.TABLE_PREFS_INDEX);
-        sb.append(" WHERE ");
-        sb.append(buildWhereStringForColumns(query, columnNames));
-
-        return sb.toString();
-    }
-
-    private String buildWhereStringForColumns(String query, String[] columnNames) {
-        final StringBuilder sb = new StringBuilder(Tables.TABLE_PREFS_INDEX);
-        sb.append(" MATCH ");
-        DatabaseUtils.appendEscapedSQLString(sb, buildMatchStringForColumns(query, columnNames));
-        sb.append(" AND ");
-        sb.append(IndexColumns.LOCALE);
-        sb.append(" = ");
-        DatabaseUtils.appendEscapedSQLString(sb, Locale.getDefault().toString());
-        return sb.toString();
-    }
-
-    private String buildMatchStringForColumns(String query, String[] columnNames) {
-        final String value = query + "*";
-        StringBuilder sb = new StringBuilder();
-        final int count = columnNames.length;
-        for (int n = 0; n < count; n++) {
-            sb.append(columnNames[n]);
-            sb.append(":");
-            sb.append(value);
-            if (n < count - 1) {
-                sb.append(" OR ");
-            }
-        }
-        return sb.toString();
-    }
-
-    private SQLiteDatabase getReadableDatabase() {
-        return IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
-    }
-
-    private SQLiteDatabase getWritableDatabase() {
-        return IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
-    }
-
-    /**
-     * A private class to describe the update data for the Index database
-     */
-    private class UpdateData {
-        public List<IndexableRef> dataToAdd;
-        public List<String> dataToDelete;
-
-        public UpdateData() {
-            dataToAdd = new ArrayList<IndexableRef>();
-            dataToDelete = new ArrayList<String>();
-        }
-
-        public void clear() {
-            dataToAdd.clear();
-            dataToDelete.clear();
-        }
-    }
-
-    /**
-     * A private class for updating the Index database
-     */
-    private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Boolean> {
-
-        @Override
-        protected void onPreExecute() {
-            super.onPreExecute();
-            mIsAvailable.set(false);
-        }
-
-        @Override
-        protected void onPostExecute(Boolean aBoolean) {
-            super.onPostExecute(aBoolean);
-            mIsAvailable.set(true);
-        }
-
-        @Override
-        protected Boolean doInBackground(UpdateData... params) {
-            boolean result = false;
-
-            final List<IndexableRef> dataToAdd = params[0].dataToAdd;
-            final List<String> dataToDelete = params[0].dataToDelete;
-            final SQLiteDatabase database = getWritableDatabase();
-            final String localeStr = Locale.getDefault().toString();
-
-            try {
-                database.beginTransaction();
-                if (dataToAdd.size() > 0) {
-                    processDataToAdd(database, localeStr, dataToAdd);
-                }
-                if (dataToDelete.size() > 0) {
-                    processDataToDelete(database, localeStr, dataToDelete);
-                }
-                database.setTransactionSuccessful();
-                result = true;
-            } finally {
-                database.endTransaction();
-            }
-            return result;
-        }
-
-        private boolean processDataToDelete(SQLiteDatabase database, String localeStr,
-                                         List<String> dataToDelete) {
-
-            boolean result = false;
-            final long current = System.currentTimeMillis();
-
-            final int count = dataToDelete.size();
-            for (int n = 0; n < count; n++) {
-                final String data = dataToDelete.get(n);
-                delete(database, data);
-            }
-
-            final long now = System.currentTimeMillis();
-            Log.d(LOG_TAG, "Deleting data for locale '" + localeStr + "' took " +
-                    (now - current) + " millis");
-            return result;
-        }
-
-        private boolean processDataToAdd(SQLiteDatabase database, String localeStr,
-                                         List<IndexableRef> dataToAdd) {
-            if (isLocaleAlreadyIndexed(database, localeStr)) {
-                Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed");
-                return true;
-            }
-
-            boolean result = false;
-            final long current = System.currentTimeMillis();
-
-            final int count = dataToAdd.size();
-            for (int n = 0; n < count; n++) {
-                final IndexableRef ref = dataToAdd.get(n);
-                indexOneRef(database, localeStr, ref);
-            }
-
-            final long now = System.currentTimeMillis();
-            Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
-                    (now - current) + " millis");
-            return result;
-        }
-
-        private void indexOneRef(SQLiteDatabase database, String localeStr, IndexableRef ref) {
-            if (ref.xmlResId > 0) {
-                indexFromResource(database, localeStr, ref.xmlResId, ref.fragmentName,
-                        ref.iconResId, ref.rank);
-            } else if (!TextUtils.isEmpty(ref.fragmentName)) {
-                indexRawData(database, localeStr, ref);
-            }
-        }
-
-        private void indexRawData(SQLiteDatabase database, String localeStr, IndexableRef ref) {
-            try {
-                final Class<?> clazz = Class.forName(ref.fragmentName);
-                if (Indexable.class.isAssignableFrom(clazz)) {
-                    final Field f = clazz.getField("INDEX_DATA_PROVIDER");
-                    final Indexable.IndexDataProvider provider =
-                            (Indexable.IndexDataProvider) f.get(null);
-
-                    final List<IndexableData> data = provider.getRawDataToIndex(mContext);
-
-                    final int size = data.size();
-                    for (int i = 0; i < size; i++) {
-                        IndexableData raw = data.get(i);
-
-                        // Should be the same locale as the one we are processing
-                        if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
-                            continue;
-                        }
-
-                        inserOneRowWithFilteredData(database, localeStr,
-                                raw.title,
-                                raw.summary,
-                                ref.fragmentName,
-                                raw.fragmentTitle,
-                                ref.iconResId,
-                                ref.rank,
-                                raw.keywords);
-                    }
-                }
-            } catch (ClassNotFoundException e) {
-                Log.e(LOG_TAG, "Cannot find class: " + ref.fragmentName, e);
-            } catch (NoSuchFieldException e) {
-                Log.e(LOG_TAG, "Cannot find field 'INDEX_DATA_PROVIDER'", e);
-            } catch (IllegalAccessException e) {
-                Log.e(LOG_TAG, "Illegal access to field 'INDEX_DATA_PROVIDER'", e);
-            }
-        }
-
-        private boolean isLocaleAlreadyIndexed(SQLiteDatabase database, String locale) {
-            Cursor cursor = null;
-            boolean result = false;
-            final StringBuilder sb = new StringBuilder(IndexColumns.LOCALE);
-            sb.append(" = ");
-            DatabaseUtils.appendEscapedSQLString(sb, locale);
-            try {
-                // We care only for 1 row
-                cursor = database.query(Tables.TABLE_PREFS_INDEX, null,
-                        sb.toString(), null, null, null, null, "1");
-                final int count = cursor.getCount();
-                result = (count >= 1);
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-            return result;
-        }
-
-        private void indexFromResource(SQLiteDatabase database, String localeStr, int xmlResId,
-                String fragmentName, int iconResId, int rank) {
-            XmlResourceParser parser = null;
-            try {
-                parser = mContext.getResources().getXml(xmlResId);
-
-                int type;
-                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                        && type != XmlPullParser.START_TAG) {
-                    // Parse next until start tag is found
-                }
-
-                String nodeName = parser.getName();
-                if (!"PreferenceScreen".equals(nodeName)) {
-                    throw new RuntimeException(
-                            "XML document must start with <PreferenceScreen> tag; found"
-                                    + nodeName + " at " + parser.getPositionDescription());
-                }
-
-                final int outerDepth = parser.getDepth();
-                final AttributeSet attrs = Xml.asAttributeSet(parser);
-                final String fragmentTitle = getDataTitle(attrs);
-
-                String title = getDataTitle(attrs);
-                String summary = getDataSummary(attrs);
-                String keywords = getDataKeywords(attrs);
-
-                // Insert rows for the main PreferenceScreen node. Rewrite the data for removing
-                // hyphens.
-                inserOneRowWithFilteredData(database, localeStr, title, summary, fragmentName,
-                        fragmentTitle, iconResId, rank, keywords);
-
-                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                        && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                        continue;
-                    }
-
-                    title = getDataTitle(attrs);
-                    summary = getDataSummary(attrs);
-                    keywords = getDataKeywords(attrs);
-
-                    // Insert rows for the child nodes of PreferenceScreen
-                    inserOneRowWithFilteredData(database, localeStr, title, summary, fragmentName,
-                            fragmentTitle, iconResId, rank, keywords);
-                }
-
-            } catch (XmlPullParserException e) {
-                throw new RuntimeException("Error parsing PreferenceScreen", e);
-            } catch (IOException e) {
-                throw new RuntimeException("Error parsing PreferenceScreen", e);
-            } finally {
-                if (parser != null) parser.close();
-            }
-        }
-
-        private void inserOneRowWithFilteredData(SQLiteDatabase database, String locale,
-                String title, String summary, String fragmentName, String fragmentTitle,
-                int iconResId, int rank, String keywords) {
-
-            String updatedTitle;
-            if (title != null) {
-                updatedTitle = title.replaceAll(NON_BREAKING_HYPHEN, HYPHEN);
-            }
-            else {
-                updatedTitle = EMPTY;
-            }
-
-            String updatedSummary;
-            if (summary != null) {
-                updatedSummary = summary.replaceAll(NON_BREAKING_HYPHEN, HYPHEN);
-            } else {
-                updatedSummary = EMPTY;
-            }
-
-            String normalizedTitle = updatedTitle.replaceAll(HYPHEN, EMPTY);
-            String normalizedSummary = updatedSummary.replaceAll(HYPHEN, EMPTY);
-
-            insertOneRow(database, locale,
-                    updatedTitle, normalizedTitle, updatedSummary, normalizedSummary,
-                    fragmentName, fragmentTitle, iconResId, rank, keywords);
-        }
-
-        private void insertOneRow(SQLiteDatabase database, String locale,
-                                  String updatedTitle, String normalizedTitle,
-                                  String updatedSummary, String normalizedSummary,
-                                  String fragmentName, String fragmentTitle,
-                                  int iconResId, int rank, String keywords) {
-
-            if (TextUtils.isEmpty(updatedTitle)) {
-                return;
-            }
-            ContentValues values = new ContentValues();
-            values.put(IndexColumns.LOCALE, locale);
-            values.put(IndexColumns.DATA_RANK, rank);
-            values.put(IndexColumns.DATA_TITLE, updatedTitle);
-            values.put(IndexColumns.DATA_TITLE_NORMALIZED, normalizedTitle);
-            values.put(IndexColumns.DATA_SUMMARY, updatedSummary);
-            values.put(IndexColumns.DATA_SUMMARY_NORMALIZED, normalizedSummary);
-            values.put(IndexColumns.DATA_KEYWORDS, keywords);
-            values.put(IndexColumns.FRAGMENT_NAME, fragmentName);
-            values.put(IndexColumns.FRAGMENT_TITLE, fragmentTitle);
-            values.put(IndexColumns.INTENT, "");
-            values.put(IndexColumns.ICON, iconResId);
-
-            database.insertOrThrow(Tables.TABLE_PREFS_INDEX, null, values);
-        }
-
-        private int delete(SQLiteDatabase database, String title) {
-            final String whereClause = IndexColumns.DATA_TITLE + "=?";
-            final String[] whereArgs = new String[] { title };
-
-            return database.delete(Tables.TABLE_PREFS_INDEX, whereClause, whereArgs);
-        }
-
-        private String getDataTitle(AttributeSet attrs) {
-            return getData(attrs,
-                    com.android.internal.R.styleable.Preference,
-                    com.android.internal.R.styleable.Preference_title);
-        }
-
-        private String getDataSummary(AttributeSet attrs) {
-            return getData(attrs,
-                    com.android.internal.R.styleable.Preference,
-                    com.android.internal.R.styleable.Preference_summary);
-        }
-
-        private String getDataKeywords(AttributeSet attrs) {
-            return getData(attrs,
-                    R.styleable.Preference,
-                    R.styleable.Preference_keywords);
-        }
-
-        private String getData(AttributeSet set, int[] attrs, int resId) {
-            final TypedArray sa = mContext.obtainStyledAttributes(set, attrs);
-            final TypedValue tv = sa.peekValue(resId);
-
-            CharSequence data = null;
-            if (tv != null && tv.type == TypedValue.TYPE_STRING) {
-                if (tv.resourceId != 0) {
-                    data = mContext.getText(tv.resourceId);
-                } else {
-                    data = tv.string;
-                }
-            }
-            return (data != null) ? data.toString() : null;
-        }
-    }
-}
diff --git a/src/com/android/settings/indexer/Indexable.java b/src/com/android/settings/indexer/Indexable.java
deleted file mode 100644
index 1b29b75..0000000
--- a/src/com/android/settings/indexer/Indexable.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 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.settings.indexer;
-
-import android.content.Context;
-
-import java.util.List;
-
-/**
- * Interface for classes whose instances can provide data for indexing.
- *
- * Classes implementing the Indexable interface must have a static field called
- * <code>INDEX_DATA_PROVIDER</code>, which is an object implementing the
- * {@link Indexable.IndexDataProvider Indexable.IndexDataProvider} interface.
- *
- * See {@link IndexableRef} and {@link IndexableData}.
- *
- */
-public interface Indexable {
-
-    public interface IndexDataProvider {
-        /**
-         * Return a list of references for indexing. See {@link IndexableRef}
-         *
-         * @param context the context
-         * @return a list of {@link IndexableRef} references. Can be null.
-         */
-        List<IndexableRef> getRefsToIndex(Context context);
-
-        /**
-         * Return a list of raw data for indexing. See {@link IndexableData}
-         *
-         * @param context the context
-         * @return a list of {@link IndexableData} references. Can be null.
-         */
-        List<IndexableData> getRawDataToIndex(Context context);
-    }
-}
diff --git a/src/com/android/settings/indexer/IndexableRef.java b/src/com/android/settings/indexer/IndexableRef.java
deleted file mode 100644
index c1ebcca..0000000
--- a/src/com/android/settings/indexer/IndexableRef.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2014 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.settings.indexer;
-
-/**
- * Indexable Reference.
- *
- * This class wraps a set of information representing data that can be indexed for a high
- * level (see {@link android.preference.PreferenceScreen}).
- *
- * rank: is the rank of the data (basically its order in the list of Settings)
- * xmlResId: is the resource Id of a PreferenceScreen xml file
- * fragmentName: is the fragment class name associated with the data
- * iconRedId: is the resource Id of an icon that represents the data
- *
- * See {@link Indexable} and {@link IndexableData}.
- *
- */
-public class IndexableRef {
-
-    public int rank;
-    public int xmlResId;
-    public String fragmentName;
-    public int iconResId;
-
-    public IndexableRef(int rank, int dataResId, String name, int iconResId) {
-        this.rank = rank;
-        this.xmlResId = dataResId;
-        this.fragmentName = name;
-        this.iconResId = iconResId;
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/settings/search/Index.java b/src/com/android/settings/search/Index.java
new file mode 100644
index 0000000..3128547
--- /dev/null
+++ b/src/com/android/settings/search/Index.java
@@ -0,0 +1,741 @@
+/*
+ * Copyright (C) 2014 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.settings.search;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.SearchIndexableData;
+import android.provider.SearchIndexableResource;
+import android.provider.SearchIndexablesContract;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.util.Xml;
+import com.android.settings.R;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.android.settings.search.IndexDatabaseHelper.Tables;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns;
+
+public class Index {
+
+    private static final String LOG_TAG = "Index";
+
+    // Those indices should match the indices of SELECT_COLUMNS !
+    public static final int COLUMN_INDEX_TITLE = 1;
+    public static final int COLUMN_INDEX_SUMMARY = 2;
+    public static final int COLUMN_INDEX_CLASS_NAME = 4;
+    public static final int COLUMN_INDEX_SCREEN_TITLE = 5;
+    public static final int COLUMN_INDEX_ICON = 6;
+    public static final int COLUMN_INDEX_INTENT_ACTION = 7;
+    public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
+    public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
+
+    // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values
+    private static final String[] SELECT_COLUMNS = new String[] {
+            IndexColumns.DATA_RANK,
+            IndexColumns.DATA_TITLE,
+            IndexColumns.DATA_SUMMARY,
+            IndexColumns.DATA_KEYWORDS,
+            IndexColumns.CLASS_NAME,
+            IndexColumns.SCREEN_TITLE,
+            IndexColumns.ICON,
+            IndexColumns.INTENT_ACTION,
+            IndexColumns.INTENT_TARGET_PACKAGE,
+            IndexColumns.INTENT_TARGET_CLASS
+    };
+
+    private static final String[] MATCH_COLUMNS = {
+            IndexColumns.DATA_TITLE,
+            IndexColumns.DATA_TITLE_NORMALIZED,
+            IndexColumns.DATA_SUMMARY,
+            IndexColumns.DATA_SUMMARY_NORMALIZED,
+            IndexColumns.DATA_KEYWORDS
+    };
+
+    private static final String EMPTY = "";
+    private static final String NON_BREAKING_HYPHEN = "\u2011";
+    private static final String HYPHEN = "-";
+
+    private static Index sInstance;
+
+    private final AtomicBoolean mIsAvailable = new AtomicBoolean(false);
+
+    private final UpdateData mUpdateData = new UpdateData();
+
+    private final Context mContext;
+
+    /**
+     * A basic singleton
+     */
+    public static Index getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new Index(context);
+        }
+        return sInstance;
+    }
+
+    public Index(Context context) {
+        mContext = context;
+    }
+
+    public boolean isAvailable() {
+        return mIsAvailable.get();
+    }
+
+    public void addIndexableData(SearchIndexableData data) {
+        synchronized (mUpdateData) {
+            mUpdateData.dataToAdd.add(data);
+        }
+    }
+
+    public void addIndexableData(SearchIndexableResource[] array) {
+        synchronized (mUpdateData) {
+            final int count = array.length;
+            for (int n = 0; n < count; n++) {
+                mUpdateData.dataToAdd.add(array[n]);
+            }
+        }
+    }
+
+    public void deleteIndexableData(String[] array) {
+        synchronized (mUpdateData) {
+            final int count = array.length;
+            for (int n = 0; n < count; n++) {
+                mUpdateData.dataToDelete.add(array[n]);
+            }
+        }
+    }
+
+    public boolean update() {
+        final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
+        List<ResolveInfo> list =
+                mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+
+        final int size = list.size();
+        for (int n = 0; n < size; n++) {
+            final ResolveInfo info = list.get(n);
+            final String authority = info.providerInfo.authority;
+            final String packageName = info.providerInfo.packageName;
+            final Context packageContext;
+            try {
+                packageContext = mContext.createPackageContext(packageName, 0);
+
+                final Uri uriForResources = buildUriForXmlResources(authority);
+                addIndexablesForXmlResourceUri(packageContext, packageName, uriForResources,
+                        SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS);
+
+                final Uri uriForRawData = buildUriForRawData(authority);
+                addIndexablesForRawDataUri(packageContext, packageName, uriForRawData,
+                        SearchIndexablesContract.INDEXABLES_RAW_COLUMNS);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
+                        + Log.getStackTraceString(e));
+                continue;
+            }
+        }
+
+        return updateInternal();
+    }
+
+    private static Uri buildUriForXmlResources(String authority) {
+        return Uri.parse("content://" + authority + "/" +
+                SearchIndexablesContract.INDEXABLES_XML_RES_PATH);
+    }
+
+    private static Uri buildUriForRawData(String authority) {
+        return Uri.parse("content://" + authority + "/" +
+                SearchIndexablesContract.INDEXABLES_RAW_PATH);
+    }
+
+    private void addIndexablesForXmlResourceUri(Context context, String packageName, Uri uri,
+            String[] projection) {
+        final ContentResolver resolver = context.getContentResolver();
+
+        final Cursor cursor = resolver.query(uri, projection,
+                null, null, null);
+
+        if (cursor == null) {
+            Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
+            return;
+        }
+
+        try {
+            final int count = cursor.getCount();
+            if (count > 0) {
+                while (cursor.moveToNext()) {
+                    final int rank = cursor.getInt(0);
+                    final int xmlResId = cursor.getInt(1);
+
+                    final String className = cursor.getString(2);
+                    final int iconResId = cursor.getInt(3);
+
+                    final String action = cursor.getString(4);
+                    final String targetPackage = cursor.getString(5);
+                    final String targetClass = cursor.getString(6);
+
+                    SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.rank = rank;
+                    sir.xmlResId = xmlResId;
+                    sir.className = className;
+                    sir.packageName = packageName;
+                    sir.iconResId = iconResId;
+                    sir.intentAction = action;
+                    sir.intentTargetPackage = targetPackage;
+                    sir.intentTargetClass = targetClass;
+
+                    addIndexableData(sir);
+                }
+            }
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private void addIndexablesForRawDataUri(Context context, String packageName, Uri uri,
+                                            String[] projection) {
+        final ContentResolver resolver = context.getContentResolver();
+
+        final Cursor cursor = resolver.query(uri, projection,
+                null, null, null);
+
+        if (cursor == null) {
+            Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
+            return;
+        }
+
+        try {
+            final int count = cursor.getCount();
+            if (count > 0) {
+                while (cursor.moveToNext()) {
+                    final int rank = cursor.getInt(0);
+                    final String title = cursor.getString(1);
+                    final String summary = cursor.getString(2);
+                    final String keywords = cursor.getString(3);
+
+                    final String screenTitle = cursor.getString(4);
+
+                    final String className = cursor.getString(5);
+                    final int iconResId = cursor.getInt(6);
+
+                    final String action = cursor.getString(7);
+                    final String targetPackage = cursor.getString(8);
+                    final String targetClass = cursor.getString(9);
+
+                    SearchIndexableRaw data = new SearchIndexableRaw(context);
+                    data.rank = rank;
+                    data.title = title;
+                    data.summary = summary;
+                    data.keywords = keywords;
+                    data.screenTitle = screenTitle;
+                    data.className = className;
+                    data.packageName = packageName;
+                    data.iconResId = iconResId;
+                    data.intentAction = action;
+                    data.intentTargetPackage = targetPackage;
+                    data.intentTargetClass = targetClass;
+
+                    addIndexableData(data);
+                }
+            }
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private boolean updateInternal() {
+        synchronized (mUpdateData) {
+            final UpdateIndexTask task = new UpdateIndexTask();
+            task.execute(mUpdateData);
+            try {
+                final boolean result = task.get();
+                mUpdateData.clear();
+                return result;
+            } catch (InterruptedException e) {
+                Log.e(LOG_TAG, "Cannot update index: " + e.getMessage());
+                return false;
+            } catch (ExecutionException e) {
+                Log.e(LOG_TAG, "Cannot update index: " + e.getMessage());
+                return false;
+            }
+        }
+    }
+
+    public Cursor search(String query) {
+        final String sql = buildSQL(query);
+        Log.d(LOG_TAG, "Query: " + sql);
+        return getReadableDatabase().rawQuery(sql, null);
+    }
+
+    private String buildSQL(String query) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(buildSQLForColumn(query, MATCH_COLUMNS));
+        sb.append(" ORDER BY ");
+        sb.append(IndexColumns.DATA_RANK);
+        return sb.toString();
+    }
+
+    private String buildSQLForColumn(String query, String[] columnNames) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SELECT ");
+        for (int n = 0; n < SELECT_COLUMNS.length; n++) {
+            sb.append(SELECT_COLUMNS[n]);
+            if (n < SELECT_COLUMNS.length - 1) {
+                sb.append(", ");
+            }
+        }
+        sb.append(" FROM ");
+        sb.append(Tables.TABLE_PREFS_INDEX);
+        sb.append(" WHERE ");
+        sb.append(buildWhereStringForColumns(query, columnNames));
+
+        return sb.toString();
+    }
+
+    private String buildWhereStringForColumns(String query, String[] columnNames) {
+        final StringBuilder sb = new StringBuilder(Tables.TABLE_PREFS_INDEX);
+        sb.append(" MATCH ");
+        DatabaseUtils.appendEscapedSQLString(sb, buildMatchStringForColumns(query, columnNames));
+        sb.append(" AND ");
+        sb.append(IndexColumns.LOCALE);
+        sb.append(" = ");
+        DatabaseUtils.appendEscapedSQLString(sb, Locale.getDefault().toString());
+        return sb.toString();
+    }
+
+    private String buildMatchStringForColumns(String query, String[] columnNames) {
+        final String value = query + "*";
+        StringBuilder sb = new StringBuilder();
+        final int count = columnNames.length;
+        for (int n = 0; n < count; n++) {
+            sb.append(columnNames[n]);
+            sb.append(":");
+            sb.append(value);
+            if (n < count - 1) {
+                sb.append(" OR ");
+            }
+        }
+        return sb.toString();
+    }
+
+    private SQLiteDatabase getReadableDatabase() {
+        return IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
+    }
+
+    private SQLiteDatabase getWritableDatabase() {
+        return IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
+    }
+
+    /**
+     * A private class to describe the update data for the Index database
+     */
+    private class UpdateData {
+        public List<SearchIndexableData> dataToAdd;
+        public List<String> dataToDelete;
+
+        public UpdateData() {
+            dataToAdd = new ArrayList<SearchIndexableData>();
+            dataToDelete = new ArrayList<String>();
+        }
+
+        public void clear() {
+            dataToAdd.clear();
+            dataToDelete.clear();
+        }
+    }
+
+    /**
+     * A private class for updating the Index database
+     */
+    private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Boolean> {
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+            mIsAvailable.set(false);
+        }
+
+        @Override
+        protected void onPostExecute(Boolean aBoolean) {
+            super.onPostExecute(aBoolean);
+            mIsAvailable.set(true);
+        }
+
+        @Override
+        protected Boolean doInBackground(UpdateData... params) {
+            boolean result = false;
+
+            final List<SearchIndexableData> dataToAdd = params[0].dataToAdd;
+            final List<String> dataToDelete = params[0].dataToDelete;
+            final SQLiteDatabase database = getWritableDatabase();
+            final String localeStr = Locale.getDefault().toString();
+
+            try {
+                database.beginTransaction();
+                if (dataToAdd.size() > 0) {
+                    processDataToAdd(database, localeStr, dataToAdd);
+                }
+                if (dataToDelete.size() > 0) {
+                    processDataToDelete(database, localeStr, dataToDelete);
+                }
+                database.setTransactionSuccessful();
+                result = true;
+            } finally {
+                database.endTransaction();
+            }
+            return result;
+        }
+
+        private boolean processDataToDelete(SQLiteDatabase database, String localeStr,
+                                         List<String> dataToDelete) {
+
+            boolean result = false;
+            final long current = System.currentTimeMillis();
+
+            final int count = dataToDelete.size();
+            for (int n = 0; n < count; n++) {
+                final String data = dataToDelete.get(n);
+                delete(database, data);
+            }
+
+            final long now = System.currentTimeMillis();
+            Log.d(LOG_TAG, "Deleting data for locale '" + localeStr + "' took " +
+                    (now - current) + " millis");
+            return result;
+        }
+
+        private boolean processDataToAdd(SQLiteDatabase database, String localeStr,
+                                         List<SearchIndexableData> dataToAdd) {
+            if (isLocaleAlreadyIndexed(database, localeStr)) {
+                Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed");
+                return true;
+            }
+
+            boolean result = false;
+            final long current = System.currentTimeMillis();
+
+            final int count = dataToAdd.size();
+            for (int n = 0; n < count; n++) {
+                final SearchIndexableData data = dataToAdd.get(n);
+                if (data instanceof SearchIndexableResource) {
+                    indexOneResource(database, localeStr, (SearchIndexableResource) data);
+                } else if (data instanceof SearchIndexableRaw) {
+                    indexOneRaw(database, localeStr, (SearchIndexableRaw) data);
+                }
+            }
+
+            final long now = System.currentTimeMillis();
+            Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
+                    (now - current) + " millis");
+            return result;
+        }
+
+        private void indexOneResource(SQLiteDatabase database, String localeStr,
+                SearchIndexableResource sir) {
+            if (sir.xmlResId > 0) {
+                indexFromResource(sir.context, database, localeStr,
+                        sir.xmlResId, sir.className, sir.iconResId, sir.rank,
+                        sir.intentAction, sir.intentTargetPackage, sir.intentTargetClass);
+            } else if (!TextUtils.isEmpty(sir.className)) {
+                indexFromLocalProvider(database, localeStr, sir);
+            }
+        }
+
+        private void indexFromLocalProvider(SQLiteDatabase database, String localeStr,
+                                            SearchIndexableResource sir) {
+            try {
+                final Class<?> clazz = Class.forName(sir.className);
+                if (Indexable.class.isAssignableFrom(clazz)) {
+                    final Field f = clazz.getField("SEARCH_INDEX_DATA_PROVIDER");
+                    final Indexable.SearchIndexProvider provider =
+                            (Indexable.SearchIndexProvider) f.get(null);
+
+                    final List<SearchIndexableRaw> rawList =
+                            provider.getRawDataToIndex(sir.context);
+                    if (rawList != null) {
+                        final int rawSize = rawList.size();
+                        for (int i = 0; i < rawSize; i++) {
+                            SearchIndexableRaw raw = rawList.get(i);
+
+                            // Should be the same locale as the one we are processing
+                            if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
+                                continue;
+                            }
+
+                            insertOneRowWithFilteredData(database, localeStr,
+                                    raw.title,
+                                    raw.summary,
+                                    sir.className,
+                                    raw.screenTitle,
+                                    sir.iconResId,
+                                    sir.rank,
+                                    raw.keywords,
+                                    raw.intentAction,
+                                    raw.intentTargetPackage,
+                                    raw.intentTargetClass
+                            );
+                        }
+                    }
+
+                    final List<SearchIndexableResource> resList =
+                            provider.getXmlResourcesToIndex(sir.context);
+                    if (resList != null) {
+                        final int resSize = resList.size();
+                        for (int i = 0; i < resSize; i++) {
+                            SearchIndexableResource item = resList.get(i);
+
+                            // Should be the same locale as the one we are processing
+                            if (!item.locale.toString().equalsIgnoreCase(localeStr)) {
+                                continue;
+                            }
+
+                            indexFromResource(sir.context, database, localeStr,
+                                    item.xmlResId, item.className, item.iconResId, item.rank,
+                                    item.intentAction, item.intentTargetPackage,
+                                    item.intentTargetClass);
+                        }
+                    }
+                }
+            } catch (ClassNotFoundException e) {
+                Log.e(LOG_TAG, "Cannot find class: " + sir.className, e);
+            } catch (NoSuchFieldException e) {
+                Log.e(LOG_TAG, "Cannot find field 'SEARCH_INDEX_DATA_PROVIDER'", e);
+            } catch (IllegalAccessException e) {
+                Log.e(LOG_TAG, "Illegal access to field 'SEARCH_INDEX_DATA_PROVIDER'", e);
+            }
+        }
+
+        private void indexOneRaw(SQLiteDatabase database, String localeStr,
+                SearchIndexableRaw raw) {
+            // Should be the same locale as the one we are processing
+            if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
+                return;
+            }
+
+            insertOneRowWithFilteredData(database, localeStr,
+                    raw.title,
+                    raw.summary,
+                    raw.className,
+                    raw.screenTitle,
+                    raw.iconResId,
+                    raw.rank,
+                    raw.keywords,
+                    raw.intentAction,
+                    raw.intentTargetPackage,
+                    raw.intentTargetClass
+            );
+        }
+
+        private boolean isLocaleAlreadyIndexed(SQLiteDatabase database, String locale) {
+            Cursor cursor = null;
+            boolean result = false;
+            final StringBuilder sb = new StringBuilder(IndexColumns.LOCALE);
+            sb.append(" = ");
+            DatabaseUtils.appendEscapedSQLString(sb, locale);
+            try {
+                // We care only for 1 row
+                cursor = database.query(Tables.TABLE_PREFS_INDEX, null,
+                        sb.toString(), null, null, null, null, "1");
+                final int count = cursor.getCount();
+                result = (count >= 1);
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+            return result;
+        }
+
+        private void indexFromResource(Context context, SQLiteDatabase database, String localeStr,
+                int xmlResId, String fragmentName, int iconResId, int rank,
+                String intentAction, String intentTargetPackage, String intentTargetClass) {
+
+            XmlResourceParser parser = null;
+            try {
+                parser = context.getResources().getXml(xmlResId);
+
+                int type;
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && type != XmlPullParser.START_TAG) {
+                    // Parse next until start tag is found
+                }
+
+                String nodeName = parser.getName();
+                if (!"PreferenceScreen".equals(nodeName)) {
+                    throw new RuntimeException(
+                            "XML document must start with <PreferenceScreen> tag; found"
+                                    + nodeName + " at " + parser.getPositionDescription());
+                }
+
+                final int outerDepth = parser.getDepth();
+                final AttributeSet attrs = Xml.asAttributeSet(parser);
+                final String screenTitle = getDataTitle(context, attrs);
+
+                String title = getDataTitle(context, attrs);
+                String summary = getDataSummary(context, attrs);
+                String keywords = getDataKeywords(context, attrs);
+
+                // Insert rows for the main PreferenceScreen node. Rewrite the data for removing
+                // hyphens.
+                insertOneRowWithFilteredData(database, localeStr, title, summary, fragmentName,
+                        screenTitle, iconResId, rank, keywords,
+                        intentAction, intentTargetPackage, intentTargetClass);
+
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                        continue;
+                    }
+
+                    title = getDataTitle(context, attrs);
+                    summary = getDataSummary(context, attrs);
+                    keywords = getDataKeywords(context, attrs);
+
+                    // Insert rows for the child nodes of PreferenceScreen
+                    insertOneRowWithFilteredData(database, localeStr, title, summary, fragmentName,
+                            screenTitle, iconResId, rank, keywords,
+                            intentAction, intentTargetPackage, intentTargetClass);
+                }
+
+            } catch (XmlPullParserException e) {
+                throw new RuntimeException("Error parsing PreferenceScreen", e);
+            } catch (IOException e) {
+                throw new RuntimeException("Error parsing PreferenceScreen", e);
+            } finally {
+                if (parser != null) parser.close();
+            }
+        }
+
+        private void insertOneRowWithFilteredData(SQLiteDatabase database, String locale,
+                String title, String summary, String className, String screenTitle,
+                int iconResId, int rank, String keywords,
+                String intentAction, String intentTargetPackage, String intentTargetClass) {
+
+            String updatedTitle;
+            if (title != null) {
+                updatedTitle = title.replaceAll(NON_BREAKING_HYPHEN, HYPHEN);
+            }
+            else {
+                updatedTitle = EMPTY;
+            }
+
+            String updatedSummary;
+            if (summary != null) {
+                updatedSummary = summary.replaceAll(NON_BREAKING_HYPHEN, HYPHEN);
+            } else {
+                updatedSummary = EMPTY;
+            }
+
+            String normalizedTitle = updatedTitle.replaceAll(HYPHEN, EMPTY);
+            String normalizedSummary = updatedSummary.replaceAll(HYPHEN, EMPTY);
+
+            insertOneRow(database, locale,
+                    updatedTitle, normalizedTitle, updatedSummary, normalizedSummary,
+                    className, screenTitle, iconResId, rank, keywords,
+                    intentAction, intentTargetPackage, intentTargetClass);
+        }
+
+        private void insertOneRow(SQLiteDatabase database, String locale,
+                String updatedTitle, String normalizedTitle,
+                String updatedSummary, String normalizedSummary,
+                String className, String screenTitle,
+                int iconResId, int rank, String keywords,
+                String intentAction, String intentTargetPackage, String intentTargetClass) {
+
+            if (TextUtils.isEmpty(updatedTitle)) {
+                return;
+            }
+            ContentValues values = new ContentValues();
+            values.put(IndexColumns.LOCALE, locale);
+            values.put(IndexColumns.DATA_RANK, rank);
+            values.put(IndexColumns.DATA_TITLE, updatedTitle);
+            values.put(IndexColumns.DATA_TITLE_NORMALIZED, normalizedTitle);
+            values.put(IndexColumns.DATA_SUMMARY, updatedSummary);
+            values.put(IndexColumns.DATA_SUMMARY_NORMALIZED, normalizedSummary);
+            values.put(IndexColumns.DATA_KEYWORDS, keywords);
+            values.put(IndexColumns.CLASS_NAME, className);
+            values.put(IndexColumns.SCREEN_TITLE, screenTitle);
+            values.put(IndexColumns.INTENT_ACTION, intentAction);
+            values.put(IndexColumns.INTENT_TARGET_PACKAGE, intentTargetPackage);
+            values.put(IndexColumns.INTENT_TARGET_CLASS, intentTargetClass);
+            values.put(IndexColumns.ICON, iconResId);
+
+            database.insertOrThrow(Tables.TABLE_PREFS_INDEX, null, values);
+        }
+
+        private int delete(SQLiteDatabase database, String title) {
+            final String whereClause = IndexColumns.DATA_TITLE + "=?";
+            final String[] whereArgs = new String[] { title };
+
+            return database.delete(Tables.TABLE_PREFS_INDEX, whereClause, whereArgs);
+        }
+
+        private String getDataTitle(Context context, AttributeSet attrs) {
+            return getData(context, attrs,
+                    com.android.internal.R.styleable.Preference,
+                    com.android.internal.R.styleable.Preference_title);
+        }
+
+        private String getDataSummary(Context context, AttributeSet attrs) {
+            return getData(context, attrs,
+                    com.android.internal.R.styleable.Preference,
+                    com.android.internal.R.styleable.Preference_summary);
+        }
+
+        private String getDataKeywords(Context context, AttributeSet attrs) {
+            return getData(context, attrs,
+                    R.styleable.Preference,
+                    R.styleable.Preference_keywords);
+        }
+
+        private String getData(Context context, AttributeSet set, int[] attrs, int resId) {
+            final TypedArray sa = context.obtainStyledAttributes(set, attrs);
+            final TypedValue tv = sa.peekValue(resId);
+
+            CharSequence data = null;
+            if (tv != null && tv.type == TypedValue.TYPE_STRING) {
+                if (tv.resourceId != 0) {
+                    data = context.getText(tv.resourceId);
+                } else {
+                    data = tv.string;
+                }
+            }
+            return (data != null) ? data.toString() : null;
+        }
+    }
+}
diff --git a/src/com/android/settings/indexer/IndexDatabaseHelper.java b/src/com/android/settings/search/IndexDatabaseHelper.java
similarity index 85%
rename from src/com/android/settings/indexer/IndexDatabaseHelper.java
rename to src/com/android/settings/search/IndexDatabaseHelper.java
index 3e6396e..1132d3f 100644
--- a/src/com/android/settings/indexer/IndexDatabaseHelper.java
+++ b/src/com/android/settings/search/IndexDatabaseHelper.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.settings.indexer;
+package com.android.settings.search;
 
 import android.content.Context;
 import android.database.Cursor;
@@ -28,7 +28,7 @@
     private static final String TAG = "IndexDatabaseHelper";
 
     private static final String DATABASE_NAME = "search_index.db";
-    private static final int DATABASE_VERSION = 101;
+    private static final int DATABASE_VERSION = 102;
 
     public interface Tables {
         public static final String TABLE_PREFS_INDEX = "prefs_index";
@@ -43,9 +43,11 @@
         public static final String DATA_SUMMARY = "data_summary";
         public static final String DATA_SUMMARY_NORMALIZED = "data_summary_normalized";
         public static final String DATA_KEYWORDS = "data_keywords";
-        public static final String FRAGMENT_NAME = "fragment_name";
-        public static final String FRAGMENT_TITLE = "fragment_title";
-        public static final String INTENT = "intent";
+        public static final String CLASS_NAME = "class_name";
+        public static final String SCREEN_TITLE = "screen_title";
+        public static final String INTENT_ACTION = "intent_action";
+        public static final String INTENT_TARGET_PACKAGE = "intent_target_package";
+        public static final String INTENT_TARGET_CLASS = "intent_target_class";
         public static final String ICON = "icon";
     }
 
@@ -70,13 +72,17 @@
                     ", " +
                     IndexColumns.DATA_KEYWORDS +
                     ", " +
-                    IndexColumns.FRAGMENT_NAME +
+                    IndexColumns.SCREEN_TITLE +
                     ", " +
-                    IndexColumns.FRAGMENT_TITLE +
-                    ", " +
-                    IndexColumns.INTENT +
+                    IndexColumns.CLASS_NAME +
                     ", " +
                     IndexColumns.ICON +
+                    ", " +
+                    IndexColumns.INTENT_ACTION +
+                    ", " +
+                    IndexColumns.INTENT_TARGET_PACKAGE +
+                    ", " +
+                    IndexColumns.INTENT_TARGET_CLASS +
                     ");";
 
     private static final String CREATE_META_TABLE =
@@ -119,9 +125,9 @@
 
     @Override
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-        if (newVersion > 100) {
-            Log.w(TAG, "Detected schema version 100. " +
-                    "Index needs to be rebuilt for schema version 101");
+        if (oldVersion == 100 || oldVersion == 101) {
+            Log.w(TAG, "Detected schema version 100 or 101. " +
+                    "Index needs to be rebuilt for schema version 102");
             // We need to drop the tables and recreate them
             dropTables(db);
             bootstrapDB(db);
diff --git a/src/com/android/settings/search/Indexable.java b/src/com/android/settings/search/Indexable.java
new file mode 100644
index 0000000..54070fb
--- /dev/null
+++ b/src/com/android/settings/search/Indexable.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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.settings.search;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import java.util.List;
+
+/**
+ * Interface for classes whose instances can provide data for indexing.
+ *
+ * Classes implementing the Indexable interface must have a static field called
+ * <code>SEARCH_INDEX_DATA_PROVIDER</code>, which is an object implementing the
+ * {@link Indexable.SearchIndexProvider} interface.
+ *
+ * See {@link android.provider.SearchIndexableResource} and {@link SearchIndexableRaw}.
+ *
+ */
+public interface Indexable {
+
+    public interface SearchIndexProvider {
+        /**
+         * Return a list of references for indexing.
+         *
+         * See {@link android.provider.SearchIndexableResource}
+         *
+         * @param context the context
+         * @return a list of {@link android.provider.SearchIndexableResource} references.
+         *         Can be null.
+         */
+        List<SearchIndexableResource> getXmlResourcesToIndex(Context context);
+
+        /**
+         * Return a list of raw data for indexing. See {@link SearchIndexableRaw}
+         *
+         * @param context the context
+         * @return a list of {@link SearchIndexableRaw} references. Can be null.
+         */
+        List<SearchIndexableRaw> getRawDataToIndex(Context context);
+    }
+}
diff --git a/src/com/android/settings/indexer/IndexableData.java b/src/com/android/settings/search/SearchIndexableRaw.java
similarity index 66%
rename from src/com/android/settings/indexer/IndexableData.java
rename to src/com/android/settings/search/SearchIndexableRaw.java
index ca388de..a175be9 100644
--- a/src/com/android/settings/indexer/IndexableData.java
+++ b/src/com/android/settings/search/SearchIndexableRaw.java
@@ -14,32 +14,27 @@
  * limitations under the License.
  */
 
-package com.android.settings.indexer;
+package com.android.settings.search;
 
-import java.util.Locale;
+import android.content.Context;
+import android.provider.SearchIndexableData;
 
 /**
- * Indexable Data.
+ * Indexable raw data for Search.
  *
  * This is the raw data used by the Indexer and should match its data model.
  *
- * See {@link Indexable} and {@link IndexableRef}.
+ * See {@link Indexable} and {@link android.provider.SearchIndexableResource}.
  */
-public class IndexableData {
-
-    public Locale locale;
+public class SearchIndexableRaw extends SearchIndexableData {
 
     public String title;
     public String summary;
     public String keywords;
 
-    public String intentAction;
-    public String intentTargetPackage;
-    public String intentTargetClass;
+    public String screenTitle;
 
-    public String fragmentTitle;
-
-    public IndexableData() {
-        locale = Locale.getDefault();
+    public SearchIndexableRaw(Context context) {
+        super(context);
     }
 }
diff --git a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
new file mode 100644
index 0000000..4673572
--- /dev/null
+++ b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 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.settings.search;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.SearchIndexableResource;
+import android.provider.SearchIndexablesProvider;
+import com.android.settings.DateTimeSettings;
+import com.android.settings.DevelopmentSettings;
+import com.android.settings.DeviceInfoSettings;
+import com.android.settings.DisplaySettings;
+import com.android.settings.HomeSettings;
+import com.android.settings.PrivacySettings;
+import com.android.settings.R;
+import com.android.settings.SecuritySettings;
+import com.android.settings.SoundSettings;
+import com.android.settings.WallpaperTypeSettings;
+import com.android.settings.WirelessSettings;
+import com.android.settings.accessibility.AccessibilitySettings;
+import com.android.settings.bluetooth.BluetoothSettings;
+import com.android.settings.deviceinfo.Memory;
+import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
+import com.android.settings.location.LocationSettings;
+import com.android.settings.net.DataUsageMeteredSettings;
+import com.android.settings.print.PrintSettingsFragment;
+import com.android.settings.users.UserSettings;
+import com.android.settings.wifi.WifiSettings;
+
+import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
+import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
+
+public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
+    private static final String TAG = "SettingsSearchIndexablesProvider";
+
+    private static int NO_DATA_RES_ID = 0;
+
+    private static SearchIndexableResource[] INDEXABLE_REFS = new SearchIndexableResource[] {
+            new SearchIndexableResource(1, NO_DATA_RES_ID,
+                    WifiSettings.class.getName(),
+                    R.drawable.ic_settings_wireless),
+            new SearchIndexableResource(2, R.xml.bluetooth_settings,
+                    BluetoothSettings.class.getName(),
+                    R.drawable.ic_settings_bluetooth2),
+            new SearchIndexableResource(3, R.xml.data_usage_metered_prefs,
+                    DataUsageMeteredSettings.class.getName(),
+                    R.drawable.ic_settings_data_usage),
+            new SearchIndexableResource(4, R.xml.wireless_settings,
+                    WirelessSettings.class.getName(),
+                    R.drawable.empty_icon),
+            new SearchIndexableResource(5, R.xml.home_selection,
+                    HomeSettings.class.getName(),
+                    R.drawable.ic_settings_home),
+            new SearchIndexableResource(6, R.xml.sound_settings,
+                    SoundSettings.class.getName(),
+                    R.drawable.ic_settings_sound),
+            new SearchIndexableResource(7, R.xml.display_settings,
+                    DisplaySettings.class.getName(),
+                    R.drawable.ic_settings_display),
+            new SearchIndexableResource(7, NO_DATA_RES_ID,
+                    WallpaperTypeSettings.class.getName(),
+                    R.drawable.ic_settings_display),
+            new SearchIndexableResource(8, R.xml.device_info_memory,
+                    Memory.class.getName(),
+                    R.drawable.ic_settings_storage),
+            new SearchIndexableResource(9, R.xml.power_usage_summary,
+                    PowerUsageSummary.class.getName(),
+                    R.drawable.ic_settings_battery),
+            new SearchIndexableResource(10, R.xml.user_settings,
+                    UserSettings.class.getName(),
+                    R.drawable.ic_settings_multiuser),
+            new SearchIndexableResource(11, R.xml.location_settings,
+                    LocationSettings.class.getName(),
+                    R.drawable.ic_settings_location),
+            new SearchIndexableResource(12, R.xml.security_settings,
+                    SecuritySettings.class.getName(),
+                    R.drawable.ic_settings_security),
+            new SearchIndexableResource(13, R.xml.language_settings,
+                    InputMethodAndLanguageSettings.class.getName(),
+                    R.drawable.ic_settings_language),
+            new SearchIndexableResource(14, R.xml.privacy_settings,
+                    PrivacySettings.class.getName(),
+                    R.drawable.ic_settings_backup),
+            new SearchIndexableResource(15, R.xml.date_time_prefs,
+                    DateTimeSettings.class.getName(),
+                    R.drawable.ic_settings_date_time),
+            new SearchIndexableResource(16, R.xml.accessibility_settings,
+                    AccessibilitySettings.class.getName(),
+                    R.drawable.ic_settings_accessibility),
+            new SearchIndexableResource(17, R.xml.print_settings,
+                    PrintSettingsFragment.class.getName(),
+                    com.android.internal.R.drawable.ic_print),
+            new SearchIndexableResource(18, R.xml.development_prefs,
+                    DevelopmentSettings.class.getName(),
+                    R.drawable.ic_settings_development),
+            new SearchIndexableResource(19, R.xml.device_info_settings,
+                    DeviceInfoSettings.class.getName(),
+                    R.drawable.ic_settings_about),
+    };
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor queryXmlResources(String[] projection) {
+        MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
+        final int count = INDEXABLE_REFS.length;
+        for (int n = 0; n < count; n++) {
+            Object[] ref = new Object[7];
+            ref[0] = INDEXABLE_REFS[n].rank;
+            ref[1] = INDEXABLE_REFS[n].xmlResId;
+            ref[2] = INDEXABLE_REFS[n].className;
+            ref[3] = INDEXABLE_REFS[n].iconResId;
+            ref[4] = null; // intent action
+            ref[5] = null; // intent target package
+            ref[6] = null; // intent target class
+            cursor.addRow(ref);
+        }
+        return cursor;
+    }
+
+    @Override
+    public Cursor queryRawData(String[] projection) {
+        Cursor result = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
+        return result;
+    }
+}
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index d6e68a3..702d6ae 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -20,12 +20,12 @@
 import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
 
 import android.preference.PreferenceActivity;
+import android.provider.SearchIndexableResource;
 import com.android.settings.R;
 import com.android.settings.RestrictedSettingsFragment;
 import com.android.settings.SettingsActivity;
-import com.android.settings.indexer.Indexable;
-import com.android.settings.indexer.IndexableData;
-import com.android.settings.indexer.IndexableRef;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.wifi.p2p.WifiP2pSettings;
 
 import android.app.ActionBar;
@@ -1165,22 +1165,22 @@
         }
     }
 
-    public static final Indexable.IndexDataProvider INDEX_DATA_PROVIDER =
-        new Indexable.IndexDataProvider() {
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+        new SearchIndexProvider() {
             @Override
-            public List<IndexableRef> getRefsToIndex(Context context) {
+            public List<SearchIndexableResource> getXmlResourcesToIndex(Context context) {
                 return null;
             }
 
             @Override
-            public List<IndexableData> getRawDataToIndex(Context context) {
-                final List<IndexableData> result = new ArrayList<IndexableData>();
+            public List<SearchIndexableRaw> getRawDataToIndex(Context context) {
+                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
                 final Resources res = context.getResources();
 
                 // Add fragment title
-                IndexableData data = new IndexableData();
+                SearchIndexableRaw data = new SearchIndexableRaw(context);
                 data.title = res.getString(R.string.wifi_settings);
-                data.fragmentTitle = res.getString(R.string.wifi_settings);
+                data.screenTitle = res.getString(R.string.wifi_settings);
                 result.add(data);
 
                 // Add available Wi-Fi access points
@@ -1191,9 +1191,9 @@
                 for (AccessPoint accessPoint : accessPoints) {
                     // We are indexing only the saved Wi-Fi networks.
                     if (accessPoint.getConfig() == null) continue;
-                    data = new IndexableData();
+                    data = new SearchIndexableRaw(context);
                     data.title = accessPoint.getTitle().toString();
-                    data.fragmentTitle = res.getString(R.string.wifi_settings);
+                    data.screenTitle = res.getString(R.string.wifi_settings);
                     result.add(data);
                 }