cmparts: Search improvements

 * Add proper support for raw data
 * Make the contributors easter egg more eggy
 * Supply all the relevant columns for raw data correctly so items
   are not duplicated

Change-Id: I9058f2ab010d03cdae38fb20c3592b43077f8ceb
diff --git a/src/org/cyanogenmod/cmparts/contributors/ContributorsCloudFragment.java b/src/org/cyanogenmod/cmparts/contributors/ContributorsCloudFragment.java
index c2cd9ec..4362b07 100644
--- a/src/org/cyanogenmod/cmparts/contributors/ContributorsCloudFragment.java
+++ b/src/org/cyanogenmod/cmparts/contributors/ContributorsCloudFragment.java
@@ -40,7 +40,6 @@
 import android.text.Html;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
-import android.util.ArraySet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -58,8 +57,10 @@
 import android.widget.SearchView;
 import android.widget.TextView;
 
+import org.cyanogenmod.cmparts.PartsActivity;
 import org.cyanogenmod.cmparts.R;
 import org.cyanogenmod.cmparts.search.BaseSearchIndexProvider;
+import org.cyanogenmod.cmparts.search.SearchIndexableRaw;
 import org.cyanogenmod.cmparts.search.Searchable;
 
 import java.io.File;
@@ -71,7 +72,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 
 public class ContributorsCloudFragment extends Fragment implements SearchView.OnQueryTextListener,
         SearchView.OnCloseListener, MenuItem.OnActionExpandListener, Searchable {
@@ -107,6 +107,8 @@
 
     private Handler mHandler;
 
+    private static final String KEY_PREFIX = "contributor_";
+
     private static class ViewInfo {
         Bitmap mBitmap;
         float mFocusX;
@@ -333,6 +335,20 @@
     }
 
     @Override
+    public void onStart() {
+        super.onStart();
+
+        Bundle args = getArguments();
+        if (args != null) {
+            String c = args.getString(PartsActivity.EXTRA_FRAGMENT_ARG_KEY);
+            if (c != null && c.startsWith(KEY_PREFIX)) {
+                onContributorSelected(Integer.valueOf(c.substring(KEY_PREFIX.length())));
+                args.remove(PartsActivity.EXTRA_FRAGMENT_ARG_KEY);
+            }
+        }
+    }
+
+    @Override
     public boolean onMenuItemActionExpand(MenuItem item) {
         if (item.getItemId() == mSearchMenuItem.getItemId()) {
             animateFadeOutFadeIn(mImageView, mSearchResults);
@@ -378,12 +394,20 @@
     }
 
     private void showMenuItems(boolean visible) {
-        mSearchMenuItem.setVisible(visible);
-        mContributorInfoMenuItem.setVisible(mSelectedContributor != -1 && visible);
-        mContributionsInfoMenuItem.setVisible(visible);
+        if (mSearchMenuItem != null) {
+            mSearchMenuItem.setVisible(visible);
+        }
+        if (mContributorInfoMenuItem != null) {
+            mContributorInfoMenuItem.setVisible(mSelectedContributor != -1 && visible);
+        }
+        if (mContributionsInfoMenuItem != null) {
+            mContributionsInfoMenuItem.setVisible(visible);
+        }
         if (!visible) {
             mSearchView.setQuery("", false);
-            mSearchMenuItem.collapseActionView();
+            if (mSearchMenuItem != null) {
+                mSearchMenuItem.collapseActionView();
+            }
         }
     }
 
@@ -450,7 +474,7 @@
         }
     }
 
-    private ViewInfo generateViewInfo(Context context, int selectedId) {
+    private synchronized ViewInfo generateViewInfo(Context context, int selectedId) {
         Bitmap bitmap = null;
         float focusX = -1, focusY = -1;
         final Resources res = context.getResources();
@@ -729,10 +753,16 @@
     }
 
     private void onContributorSelected(ContributorsDataHolder contributor) {
-        mSelectedContributor = contributor.mId;
+        onContributorSelected(contributor.mId);
+    }
+
+    private void onContributorSelected(int contributorId) {
+        mSelectedContributor = contributorId;
         ContributorCloudLoaderTask task = new ContributorCloudLoaderTask(true, true);
         task.execute();
-        mSearchMenuItem.collapseActionView();
+        if (mSearchMenuItem != null) {
+            mSearchMenuItem.collapseActionView();
+        }
     }
 
     private boolean hasLargeHeap() {
@@ -776,7 +806,7 @@
             new BaseSearchIndexProvider() {
 
                 @Override
-                public Set<String> getSearchKeywords(Context context) {
+                public List<SearchIndexableRaw> getRawDataToIndex(Context context) {
 
                     // Index the top 100 contributors, for fun :)
                     File dbPath = context.getDatabasePath(DB_NAME);
@@ -796,11 +826,15 @@
                         return null;
                     }
 
-                    Set<String> result = new ArraySet<>();
+                    List<SearchIndexableRaw> result = new ArrayList<>();
                     Cursor c = db.rawQuery(
-                            "select username from metadata order by commits desc limit 100;", null);
+                            "select id, username from metadata order by commits desc limit 100;", null);
                     while (c.moveToNext()) {
-                        result.add(c.getString(0));
+                        SearchIndexableRaw raw = new SearchIndexableRaw(context);
+                        raw.key = KEY_PREFIX + c.getString(0);
+                        raw.rank = 10;
+                        raw.title = c.getString(1);
+                        result.add(raw);
                     }
                     c.close();
                     db.close();
diff --git a/src/org/cyanogenmod/cmparts/livedisplay/LiveDisplay.java b/src/org/cyanogenmod/cmparts/livedisplay/LiveDisplay.java
index 4b3fec5..b7e96c7 100644
--- a/src/org/cyanogenmod/cmparts/livedisplay/LiveDisplay.java
+++ b/src/org/cyanogenmod/cmparts/livedisplay/LiveDisplay.java
@@ -27,6 +27,7 @@
 import android.support.v7.preference.ListPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceCategory;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -35,8 +36,11 @@
 import org.cyanogenmod.cmparts.R;
 import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
 import org.cyanogenmod.cmparts.search.BaseSearchIndexProvider;
+import org.cyanogenmod.cmparts.search.SearchIndexableRaw;
 import org.cyanogenmod.cmparts.search.Searchable;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
 import cyanogenmod.hardware.CMHardwareManager;
@@ -413,7 +417,7 @@
         }
 
         @Override
-        public Set<String> getSearchKeywords(Context context) {
+        public List<SearchIndexableRaw> getRawDataToIndex(Context context) {
             final LiveDisplayConfig config = LiveDisplayManager.getInstance(context).getConfig();
             final Set<String> result = new ArraySet<>();
 
@@ -426,7 +430,12 @@
                     }
                 }
             }
-            return result;
+            final SearchIndexableRaw raw = new SearchIndexableRaw(context);
+            raw.entries = TextUtils.join(" ", result);
+            raw.key = KEY_LIVE_DISPLAY_COLOR_PROFILE;
+            raw.title = context.getString(R.string.live_display_color_profile_title);
+            raw.rank = 2;
+            return Collections.singletonList(raw);
         }
     };
 }
diff --git a/src/org/cyanogenmod/cmparts/search/BaseSearchIndexProvider.java b/src/org/cyanogenmod/cmparts/search/BaseSearchIndexProvider.java
index 5791c4c..56df441 100644
--- a/src/org/cyanogenmod/cmparts/search/BaseSearchIndexProvider.java
+++ b/src/org/cyanogenmod/cmparts/search/BaseSearchIndexProvider.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -26,7 +27,7 @@
 public class BaseSearchIndexProvider implements Searchable.SearchIndexProvider {
 
     @Override
-    public Set<String> getSearchKeywords(Context context) {
+    public List<SearchIndexableRaw> getRawDataToIndex(Context context) {
         return null;
     }
 
diff --git a/src/org/cyanogenmod/cmparts/search/CMPartsSearchIndexablesProvider.java b/src/org/cyanogenmod/cmparts/search/CMPartsSearchIndexablesProvider.java
index 5e586b7..10f4ea5 100644
--- a/src/org/cyanogenmod/cmparts/search/CMPartsSearchIndexablesProvider.java
+++ b/src/org/cyanogenmod/cmparts/search/CMPartsSearchIndexablesProvider.java
@@ -18,20 +18,20 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.provider.SearchIndexablesProvider;
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
-import org.cyanogenmod.cmparts.PartsActivity;
 import org.cyanogenmod.cmparts.search.Searchable.SearchIndexProvider;
 import org.cyanogenmod.internal.cmparts.PartInfo;
 import org.cyanogenmod.platform.internal.R;
 
 import java.lang.reflect.Field;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
@@ -39,6 +39,7 @@
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK;
+import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
@@ -53,7 +54,6 @@
 import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
 import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
 import static org.cyanogenmod.internal.cmparts.PartsList.CMPARTS_ACTIVITY;
-import static org.cyanogenmod.internal.cmparts.PartsList.CMPARTS_PACKAGE;
 import static org.cyanogenmod.internal.cmparts.PartsList.getPartInfo;
 import static org.cyanogenmod.internal.cmparts.PartsList.getPartsList;
 
@@ -115,29 +115,38 @@
 
             // don't create a duplicate entry if no custom keywords are provided
             // and a resource was already indexed
-            Set<String> keywordList = sip.getSearchKeywords(getContext());
-            if ((keywordList == null || keywordList.size() == 0) && i.getXmlRes() > 0) {
-                continue;
+            List<SearchIndexableRaw> rawList = sip.getRawDataToIndex(getContext());
+            if (rawList == null || rawList.size() == 0) {
+                if (i.getXmlRes() > 0) {
+                    continue;
+                }
+                rawList = Collections.singletonList(new SearchIndexableRaw(getContext()));
             }
 
-            String keywords = null;
-            if (keywordList != null && keywordList.size() > 0) {
-                keywords = TextUtils.join(" ", keywordList);
+            for (SearchIndexableRaw raw : rawList) {
+                Object[] ref = new Object[14];
+                ref[COLUMN_INDEX_RAW_RANK] = raw.rank > 0 ?
+                        raw.rank : 2;
+                ref[COLUMN_INDEX_RAW_TITLE] = raw.title != null ?
+                        raw.title : i.getTitle();
+                ref[COLUMN_INDEX_RAW_SUMMARY_ON] = i.getSummary();
+                ref[COLUMN_INDEX_RAW_KEYWORDS] = raw.keywords;
+                ref[COLUMN_INDEX_RAW_ENTRIES] = raw.entries;
+                ref[COLUMN_INDEX_RAW_SCREEN_TITLE] = raw.screenTitle != null ?
+                        raw.screenTitle : i.getTitle();
+                ref[COLUMN_INDEX_RAW_ICON_RESID] = raw.iconResId > 0 ? raw.iconResId :
+                        (i.getIconRes() > 0 ? i.getIconRes() : R.drawable.ic_launcher_cyanogenmod);
+                ref[COLUMN_INDEX_RAW_INTENT_ACTION] = raw.intentAction != null ?
+                        raw.intentAction : i.getAction();
+                ref[COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE] = raw.intentTargetPackage != null ?
+                        raw.intentTargetPackage : CMPARTS_ACTIVITY.getPackageName();
+                ref[COLUMN_INDEX_RAW_INTENT_TARGET_CLASS] = raw.intentTargetClass != null ?
+                        raw.intentTargetClass : CMPARTS_ACTIVITY.getClassName();
+                ref[COLUMN_INDEX_RAW_KEY] = raw.key != null ?
+                        raw.key : i.getName();
+                ref[COLUMN_INDEX_RAW_USER_ID] = -1;
+                cursor.addRow(ref);
             }
-
-            Object[] ref = new Object[14];
-            ref[COLUMN_INDEX_RAW_RANK] = 2;
-            ref[COLUMN_INDEX_RAW_TITLE] = i.getTitle();
-            ref[COLUMN_INDEX_RAW_SUMMARY_ON] = i.getSummary();
-            ref[COLUMN_INDEX_RAW_KEYWORDS] = keywords;
-            ref[COLUMN_INDEX_RAW_ICON_RESID] = i.getIconRes() > 0 ? i.getIconRes() :
-                    R.drawable.ic_launcher_cyanogenmod;
-            ref[COLUMN_INDEX_RAW_INTENT_ACTION] = i.getAction();
-            ref[COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE] = CMPARTS_ACTIVITY.getPackageName();
-            ref[COLUMN_INDEX_RAW_INTENT_TARGET_CLASS] = CMPARTS_ACTIVITY.getClassName();
-            ref[COLUMN_INDEX_RAW_KEY] = i.getName();
-            ref[COLUMN_INDEX_RAW_USER_ID] = -1;
-            cursor.addRow(ref);
         }
         return cursor;
     }
diff --git a/src/org/cyanogenmod/cmparts/search/SearchIndexableRaw.java b/src/org/cyanogenmod/cmparts/search/SearchIndexableRaw.java
new file mode 100644
index 0000000..ce52bc0
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/search/SearchIndexableRaw.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.cyanogenmod.cmparts.search;
+
+import android.content.Context;
+import android.provider.SearchIndexableData;
+
+/**
+ * Indexable raw data for Search.
+ *
+ * This is the raw data used by the Indexer and should match its data model.
+ *
+ * See {@link Searchable} and {@link android.provider.SearchIndexableResource}.
+ */
+public class SearchIndexableRaw extends SearchIndexableData {
+
+    /**
+     * Title's raw data.
+     */
+    public String title;
+
+    /**
+     * Summary's raw data when the data is "ON".
+     */
+    public String summaryOn;
+
+    /**
+     * Summary's raw data when the data is "OFF".
+     */
+    public String summaryOff;
+
+    /**
+     * Entries associated with the raw data (when the data can have several values).
+     */
+    public String entries;
+
+    /**
+     * Keywords' raw data.
+     */
+    public String keywords;
+
+    /**
+     * Fragment's or Activity's title associated with the raw data.
+     */
+    public String screenTitle;
+
+    public SearchIndexableRaw(Context context) {
+        super(context);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/search/Searchable.java b/src/org/cyanogenmod/cmparts/search/Searchable.java
index 9e69855..8aa14ae 100644
--- a/src/org/cyanogenmod/cmparts/search/Searchable.java
+++ b/src/org/cyanogenmod/cmparts/search/Searchable.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -33,7 +34,7 @@
 
     public interface SearchIndexProvider {
 
-        public Set<String> getSearchKeywords(Context context);
+        public List<SearchIndexableRaw> getRawDataToIndex(Context context);
 
         public Set<String> getNonIndexableKeys(Context context);
     }