Add support for saving and using the Preference's key value

- modify the SQlite data model
- update Index code for managing the key value
- pass the key when launching a Fragment or and Activity
- implement a small animation for highlighting the Preference
from a Search result

Change-Id: I617643a4e5e3b752ece8f45ce7d5429037e479da
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 7cdf781..6128690 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -146,6 +146,11 @@
     public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
 
     /**
+     * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
+     */
+    public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+
+    /**
      * When starting this activity, the invoking Intent can contain this extra
      * boolean that the header list should not be displayed.  This is most often
      * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
@@ -461,6 +466,7 @@
                 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
                 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
                 setTitle(mInitialTitle);
+
                 switchToFragment( initialFragmentName, initialArguments, true, false,
                         mInitialTitle, false);
             } else {
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 35893ff..d575153 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -26,12 +26,15 @@
 import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceGroupAdapter;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.widget.Button;
+import android.widget.ListAdapter;
 
 /**
  * Base class for Settings fragments, with some helper functions and dialog management.
@@ -41,6 +44,7 @@
     private static final String TAG = "SettingsPreferenceFragment";
 
     private static final int MENU_HELP = Menu.FIRST + 100;
+    private static final int HIGHLIGHT_DURATION_MILLIS = 750;
 
     private SettingsDialogFragment mDialogFragment;
 
@@ -66,6 +70,47 @@
         if (!TextUtils.isEmpty(mHelpUrl)) {
             setHasOptionsMenu(true);
         }
+
+        final Bundle args = getArguments();
+        if (args != null) {
+            final String key = args.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
+            final int position = findPositionFromKey(getPreferenceScreen(), key);
+            if (position >= 0) {
+                final ListAdapter adapter = getListView().getAdapter();
+                if (adapter instanceof PreferenceGroupAdapter) {
+                    ((PreferenceGroupAdapter) adapter).setActivated(position);
+
+                    getListView().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            ((PreferenceGroupAdapter) adapter).setActivated(-1);
+                            ((PreferenceGroupAdapter) adapter).notifyDataSetChanged();
+                        }
+                    }, HIGHLIGHT_DURATION_MILLIS);
+                }
+            }
+        }
+    }
+
+    private int findPositionFromKey(PreferenceGroup group, String key) {
+        if (group != null) {
+            int count = group.getPreferenceCount();
+            for (int n = 0; n < count; n++) {
+                final Preference preference = group.getPreference(n);
+                final String preferenceKey = preference.getKey();
+                if (preferenceKey != null && preferenceKey.equals(key)) {
+                    return n;
+                }
+                if (preference instanceof PreferenceGroup) {
+                    PreferenceGroup nestedGroup = (PreferenceGroup) preference;
+                    final int nestedPosition = findPositionFromKey(nestedGroup, key);
+                    if (nestedPosition >= 0) {
+                        return n + 1 + nestedPosition;
+                    }
+                }
+            }
+        }
+        return -1;
     }
 
     protected void removePreference(String key) {
diff --git a/src/com/android/settings/dashboard/SearchResultsSummary.java b/src/com/android/settings/dashboard/SearchResultsSummary.java
index 706ae0f..22326a6 100644
--- a/src/com/android/settings/dashboard/SearchResultsSummary.java
+++ b/src/com/android/settings/dashboard/SearchResultsSummary.java
@@ -109,15 +109,18 @@
 
                 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);
+                final String key = cursor.getString(Index.COLUMN_INDEX_KEY);
 
                 final SettingsActivity sa = (SettingsActivity) getActivity();
 
                 sa.needToRevertToInitialFragment();
 
                 if (TextUtils.isEmpty(action)) {
-                    sa.startWithFragment(className, null, null, 0, screenTitle);
+                    Bundle args = new Bundle();
+                    args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
+
+                    sa.startWithFragment(className, args, null, 0, screenTitle);
                 } else {
                     final Intent intent = new Intent(action);
 
@@ -130,6 +133,7 @@
                                 new ComponentName(targetPackage, targetClass);
                         intent.setComponent(component);
                     }
+                    intent.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
 
                     sa.startActivity(intent);
                 }
@@ -194,21 +198,23 @@
     }
 
     private static class SearchResult {
+        public Context context;
         public String title;
         public String summaryOn;
         public String summaryOff;
         public String entries;
         public int iconResId;
-        public Context context;
+        public String key;
 
         public SearchResult(Context context, String title, String summaryOn, String summaryOff,
-                String entries, int iconResId) {
+                            String entries, int iconResId, String key) {
             this.context = context;
             this.title = title;
             this.summaryOn = summaryOn;
             this.summaryOff = summaryOff;
             this.entries = entries;
             this.iconResId = iconResId;
+            this.key = key;
         }
     }
 
@@ -263,6 +269,8 @@
                         Index.COLUMN_INDEX_CLASS_NAME);
                 final String packageName = mCursor.getString(
                         Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
+                final String key = mCursor.getString(
+                        Index.COLUMN_INDEX_KEY);
 
                 Context packageContext;
                 if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(packageName)) {
@@ -284,7 +292,7 @@
                         R.drawable.empty_icon : Integer.parseInt(iconResStr);
 
                 return new SearchResult(packageContext, title, summaryOn, summaryOff,
-                        entries, iconResId);
+                        entries, iconResId, key);
             }
             return null;
         }
diff --git a/src/com/android/settings/search/Index.java b/src/com/android/settings/search/Index.java
index 987539b..138c896 100644
--- a/src/com/android/settings/search/Index.java
+++ b/src/com/android/settings/search/Index.java
@@ -71,6 +71,7 @@
     public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 12;
     public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 13;
     public static final int COLUMN_INDEX_ENABLED = 14;
+    public static final int COLUMN_INDEX_KEY = 15;
 
     // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values
     private static final String[] SELECT_COLUMNS = new String[] {
@@ -87,7 +88,9 @@
             IndexColumns.ICON,                    // 10
             IndexColumns.INTENT_ACTION,           // 11
             IndexColumns.INTENT_TARGET_PACKAGE,   // 12
-            IndexColumns.INTENT_TARGET_CLASS      // 13
+            IndexColumns.INTENT_TARGET_CLASS,     // 13
+            IndexColumns.ENABLED,                 // 14
+            IndexColumns.DATA_KEY_REF             // 15
     };
 
     private static final String[] MATCH_COLUMNS = {
@@ -375,6 +378,8 @@
                     final String targetPackage = cursor.getString(12);
                     final String targetClass = cursor.getString(13);
 
+                    final String key = cursor.getString(15);
+
                     SearchIndexableRaw data = new SearchIndexableRaw(packageContext);
                     data.rank = rank;
                     data.title = title;
@@ -391,6 +396,7 @@
                     data.intentAction = action;
                     data.intentTargetPackage = targetPackage;
                     data.intentTargetClass = targetClass;
+                    data.key = key;
 
                     addIndexableData(data);
                 }
@@ -503,12 +509,13 @@
             String title = getDataTitle(context, attrs);
             String summary = getDataSummary(context, attrs);
             String keywords = getDataKeywords(context, attrs);
+            String key = getDataKey(context, attrs);
 
             // Insert rows for the main PreferenceScreen node. Rewrite the data for removing
             // hyphens.
             updateOneRowWithFilteredData(database, localeStr, title, summary, null, null,
-                    null, null, fragmentName, screenTitle, iconResId, rank, keywords,
-                    intentAction, intentTargetPackage, intentTargetClass, true);
+                    null, null, fragmentName, screenTitle, iconResId, rank,
+                    keywords, intentAction, intentTargetPackage, intentTargetClass, true, key);
 
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -520,6 +527,7 @@
 
                 title = getDataTitle(context, attrs);
                 keywords = getDataKeywords(context, attrs);
+                key = getDataKey(context, attrs);
 
                 if (!nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) {
                     summary = getDataSummary(context, attrs);
@@ -537,15 +545,17 @@
 
                     // Insert rows for the child nodes of PreferenceScreen
                     updateOneRowWithFilteredData(database, localeStr, title, summary, null, entries,
-                            switchOn, switchOff, fragmentName, screenTitle, iconResId, rank, keywords,
-                            intentAction, intentTargetPackage, intentTargetClass, true);
-                } else if (nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) {
+                            switchOn, switchOff, fragmentName, screenTitle, iconResId, rank,
+                            keywords, intentAction, intentTargetPackage, intentTargetClass,
+                            true, key);
+                } else {
                     final String summaryOn = getDataSummaryOn(context, attrs);
                     final String summaryOff = getDataSummaryOff(context, attrs);
 
                     updateOneRowWithFilteredData(database, localeStr, title, summaryOn, summaryOff,
-                            null, null, null, fragmentName, screenTitle, iconResId, rank, keywords,
-                            intentAction, intentTargetPackage, intentTargetClass, true);
+                            null, null, null, fragmentName, screenTitle, iconResId, rank,
+                            keywords, intentAction, intentTargetPackage, intentTargetClass,
+                            true, key);
                 }
             }
 
@@ -580,7 +590,8 @@
                 raw.intentAction,
                 raw.intentTargetPackage,
                 raw.intentTargetClass,
-                raw.enabled);
+                raw.enabled,
+                raw.key);
     }
 
     private void indexFromLocalProvider(SQLiteDatabase database, String localeStr,
@@ -619,7 +630,8 @@
                                 raw.intentAction,
                                 raw.intentTargetPackage,
                                 raw.intentTargetClass,
-                                raw.enabled);
+                                raw.enabled,
+                                raw.key);
                     }
                 }
 
@@ -657,7 +669,7 @@
             String switchOn, String switchOff, String className,
             String screenTitle, int iconResId, int rank, String keywords,
             String intentAction, String intentTargetPackage, String intentTargetClass,
-            boolean enabled) {
+            boolean enabled, String key) {
 
         String updatedTitle;
         if (title != null) {
@@ -706,7 +718,7 @@
                 updatedSummaryOff, normalizedSummaryOff, entries,
                 updatedSwitchOn, normalizedSwitchOn, updatedSwitchOff, normalizedSwitchOff,
                 className, screenTitle, iconResId,
-                rank, keywords, intentAction, intentTargetPackage, intentTargetClass, enabled);
+                rank, keywords, intentAction, intentTargetPackage, intentTargetClass, enabled, key);
     }
 
     private void updateOneRow(SQLiteDatabase database, String locale,
@@ -717,7 +729,7 @@
             String updatedSwitchOff, String normalizedSwitchOff,
             String className, String screenTitle, int iconResId, int rank, String keywords,
             String intentAction, String intentTargetPackage, String intentTargetClass,
-            boolean enabled) {
+            boolean enabled, String key) {
 
         if (TextUtils.isEmpty(updatedTitle)) {
             return;
@@ -746,10 +758,17 @@
         values.put(IndexColumns.INTENT_TARGET_CLASS, intentTargetClass);
         values.put(IndexColumns.ICON, iconResId);
         values.put(IndexColumns.ENABLED, enabled);
+        values.put(IndexColumns.DATA_KEY_REF, key);
 
         database.replaceOrThrow(Tables.TABLE_PREFS_INDEX, null, values);
     }
 
+    private String getDataKey(Context context, AttributeSet attrs) {
+        return getData(context, attrs,
+                com.android.internal.R.styleable.Preference,
+                com.android.internal.R.styleable.Preference_key);
+    }
+
     private String getDataTitle(Context context, AttributeSet attrs) {
         return getData(context, attrs,
                 com.android.internal.R.styleable.Preference,
diff --git a/src/com/android/settings/search/IndexDatabaseHelper.java b/src/com/android/settings/search/IndexDatabaseHelper.java
index bd035c3..bc827f4 100644
--- a/src/com/android/settings/search/IndexDatabaseHelper.java
+++ b/src/com/android/settings/search/IndexDatabaseHelper.java
@@ -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 = 106;
+    private static final int DATABASE_VERSION = 107;
 
     public interface Tables {
         public static final String TABLE_PREFS_INDEX = "prefs_index";
@@ -58,6 +58,7 @@
         public static final String INTENT_TARGET_CLASS = "intent_target_class";
         public static final String ICON = "icon";
         public static final String ENABLED = "enabled";
+        public static final String DATA_KEY_REF = "data_key_reference";
     }
 
     public interface MetaColumns {
@@ -108,6 +109,8 @@
                     IndexColumns.INTENT_TARGET_CLASS +
                     ", " +
                     IndexColumns.ENABLED +
+                    ", " +
+                    IndexColumns.DATA_KEY_REF +
                     ");";
 
     private static final String CREATE_META_TABLE =