Loading core/java/android/app/SearchDialog.java +106 −3 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ContentResolver; import android.content.ContentValues; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; Loading Loading @@ -1202,10 +1204,23 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS protected boolean launchSuggestion(int position, int actionKey, String actionMsg) { Cursor c = mSuggestionsAdapter.getCursor(); if ((c != null) && c.moveToPosition(position)) { Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); // report back about the click if (mGlobalSearchMode) { // in global search mode, do it via cursor mSuggestionsAdapter.callCursorOnClick(c, position); } else if (intent != null && mPreviousComponents != null && !mPreviousComponents.isEmpty()) { // in-app search (and we have pivoted in as told by mPreviousComponents, // which is used for keeping track of what we pop back to when we are pivoting into // in app search.) reportInAppClickToGlobalSearch(c, intent); } // launch the intent Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); launchIntent(intent); return true; Loading @@ -1213,6 +1228,94 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return false; } /** * Report a click from an in app search result back to global search for shortcutting porpoises. * * @param c The cursor that is pointing to the clicked position. * @param intent The intent that will be launched for the click. */ private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) { // for in app search, still tell global search via content provider Uri uri = getClickReportingUri(); final ContentValues cv = new ContentValues(); cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery); final ComponentName source = mSearchable.getSearchActivity(); cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString()); // grab the intent columns from the intent we created since it has additional // logic for falling back on the searchable default cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction()); cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString()); cv.put(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA, intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); // ensure the icons will work for global search cv.put(SearchManager.SUGGEST_COLUMN_ICON_1, wrapIconForPackage( source, getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1))); cv.put(SearchManager.SUGGEST_COLUMN_ICON_2, wrapIconForPackage( source, getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2))); // the rest can be passed through directly cv.put(SearchManager.SUGGEST_COLUMN_FORMAT, getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT)); cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1, getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1)); cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2, getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2)); cv.put(SearchManager.SUGGEST_COLUMN_QUERY, getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY)); cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID)); // note: deliberately omitting background color since it is only for global search // "more results" entries mContext.getContentResolver().insert(uri, cv); } /** * @return A URI appropriate for reporting a click. */ private Uri getClickReportingUri() { Uri.Builder uriBuilder = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY); uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH); return uriBuilder .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() .fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() .build(); } /** * Wraps an icon for a particular package. If the icon is a resource id, it is converted into * an android.resource:// URI. * * @param source The source of the icon * @param icon The icon retrieved from a suggestion column * @return An icon string appropriate for the package. */ private String wrapIconForPackage(ComponentName source, String icon) { if (icon == null || icon.length() == 0 || "0".equals(icon)) { // SearchManager specifies that null or zero can be returned to indicate // no icon. We also allow empty string. return null; } else if (!Character.isDigit(icon.charAt(0))){ return icon; } else { String packageName = source.getPackageName(); return new Uri.Builder() .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) .authority(packageName) .encodedPath(icon) .toString(); } } /** * Launches an intent. Also dismisses the search dialog if not in global search mode. */ Loading Loading @@ -1244,7 +1347,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** * Handles SearchManager#INTENT_ACTION_CHANGE_SOURCE. * Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}. */ private void handleChangeSourceIntent(Intent intent) { Uri dataUri = intent.getData(); Loading core/java/android/app/SearchManager.java +35 −0 Original line number Diff line number Diff line Loading @@ -1256,6 +1256,41 @@ public class SearchManager */ public final static String SHORTCUT_MIME_TYPE = "vnd.android.cursor.item/vnd.android.search.suggest"; /** * The authority of the provider to report clicks to when a click is detected after pivoting * into a specific app's search from global search. * * In addition to the columns below, the suggestion columns are used to pass along the full * suggestion so it can be shortcutted. * * @hide */ public final static String SEARCH_CLICK_REPORT_AUTHORITY = "com.android.globalsearch.stats"; /** * The path the write goes to. * * @hide */ public final static String SEARCH_CLICK_REPORT_URI_PATH = "click"; /** * The column storing the query for the click. * * @hide */ public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query"; /** * The column storing the component name of the application that was pivoted into. * * @hide */ public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component"; /** * Column name for suggestions cursor. <i>Unused - can be null or column can be omitted.</i> */ Loading Loading
core/java/android/app/SearchDialog.java +106 −3 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ContentResolver; import android.content.ContentValues; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; Loading Loading @@ -1202,10 +1204,23 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS protected boolean launchSuggestion(int position, int actionKey, String actionMsg) { Cursor c = mSuggestionsAdapter.getCursor(); if ((c != null) && c.moveToPosition(position)) { Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); // report back about the click if (mGlobalSearchMode) { // in global search mode, do it via cursor mSuggestionsAdapter.callCursorOnClick(c, position); } else if (intent != null && mPreviousComponents != null && !mPreviousComponents.isEmpty()) { // in-app search (and we have pivoted in as told by mPreviousComponents, // which is used for keeping track of what we pop back to when we are pivoting into // in app search.) reportInAppClickToGlobalSearch(c, intent); } // launch the intent Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); launchIntent(intent); return true; Loading @@ -1213,6 +1228,94 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return false; } /** * Report a click from an in app search result back to global search for shortcutting porpoises. * * @param c The cursor that is pointing to the clicked position. * @param intent The intent that will be launched for the click. */ private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) { // for in app search, still tell global search via content provider Uri uri = getClickReportingUri(); final ContentValues cv = new ContentValues(); cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery); final ComponentName source = mSearchable.getSearchActivity(); cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString()); // grab the intent columns from the intent we created since it has additional // logic for falling back on the searchable default cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction()); cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString()); cv.put(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA, intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); // ensure the icons will work for global search cv.put(SearchManager.SUGGEST_COLUMN_ICON_1, wrapIconForPackage( source, getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1))); cv.put(SearchManager.SUGGEST_COLUMN_ICON_2, wrapIconForPackage( source, getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2))); // the rest can be passed through directly cv.put(SearchManager.SUGGEST_COLUMN_FORMAT, getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT)); cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1, getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1)); cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2, getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2)); cv.put(SearchManager.SUGGEST_COLUMN_QUERY, getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY)); cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID)); // note: deliberately omitting background color since it is only for global search // "more results" entries mContext.getContentResolver().insert(uri, cv); } /** * @return A URI appropriate for reporting a click. */ private Uri getClickReportingUri() { Uri.Builder uriBuilder = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY); uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH); return uriBuilder .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() .fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() .build(); } /** * Wraps an icon for a particular package. If the icon is a resource id, it is converted into * an android.resource:// URI. * * @param source The source of the icon * @param icon The icon retrieved from a suggestion column * @return An icon string appropriate for the package. */ private String wrapIconForPackage(ComponentName source, String icon) { if (icon == null || icon.length() == 0 || "0".equals(icon)) { // SearchManager specifies that null or zero can be returned to indicate // no icon. We also allow empty string. return null; } else if (!Character.isDigit(icon.charAt(0))){ return icon; } else { String packageName = source.getPackageName(); return new Uri.Builder() .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) .authority(packageName) .encodedPath(icon) .toString(); } } /** * Launches an intent. Also dismisses the search dialog if not in global search mode. */ Loading Loading @@ -1244,7 +1347,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** * Handles SearchManager#INTENT_ACTION_CHANGE_SOURCE. * Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}. */ private void handleChangeSourceIntent(Intent intent) { Uri dataUri = intent.getData(); Loading
core/java/android/app/SearchManager.java +35 −0 Original line number Diff line number Diff line Loading @@ -1256,6 +1256,41 @@ public class SearchManager */ public final static String SHORTCUT_MIME_TYPE = "vnd.android.cursor.item/vnd.android.search.suggest"; /** * The authority of the provider to report clicks to when a click is detected after pivoting * into a specific app's search from global search. * * In addition to the columns below, the suggestion columns are used to pass along the full * suggestion so it can be shortcutted. * * @hide */ public final static String SEARCH_CLICK_REPORT_AUTHORITY = "com.android.globalsearch.stats"; /** * The path the write goes to. * * @hide */ public final static String SEARCH_CLICK_REPORT_URI_PATH = "click"; /** * The column storing the query for the click. * * @hide */ public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query"; /** * The column storing the component name of the application that was pivoted into. * * @hide */ public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component"; /** * Column name for suggestions cursor. <i>Unused - can be null or column can be omitted.</i> */ Loading