Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4c98c4c authored by Karl Rosaen's avatar Karl Rosaen
Browse files

Track clicks that occur after pivoting into an app from global search.

(framework portion).

Global search has a write-only content provider for reporting clicks that SearchDialog
makes use of.

update: addressed code review feedback
parent b26e685d
Loading
Loading
Loading
Loading
+106 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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.
     */
@@ -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();
+35 −0
Original line number Diff line number Diff line
@@ -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>
     */