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

Commit 1a71c05c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Handle tap on intent based search results."

parents e5a2be9d 12daf683
Loading
Loading
Loading
Loading
+93 −32
Original line number Diff line number Diff line
@@ -16,32 +16,37 @@

package com.android.settings.search2;

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.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;

import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.search.Index;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.utils.AsyncLoader;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK;
import java.util.Map;


/**
 * AsyncTask to retrieve Settings, First party app and any intent based results.
 */
public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
    private static final String LOG = "DatabaseResultLoader";
    private final String mQueryText;
    private final Context mContext;
    protected final SQLiteDatabase mDatabase;
@@ -78,9 +83,11 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {

    protected String getSQLQuery() {
        return String.format("SELECT data_rank, data_title, data_summary_on, " +
                "data_summary_off, data_entries, data_keywords, class_name, screen_title, icon, " +
                        "data_summary_off, data_entries, data_keywords, class_name, screen_title,"
                        + " icon, " +
                        "intent_action, intent_target_package, intent_target_class, enabled, " +
                "data_key_reference FROM prefs_index WHERE prefs_index MATCH 'data_title:%s* " +
                        "data_key_reference FROM prefs_index WHERE prefs_index MATCH "
                        + "'data_title:%s* " +
                        "OR data_title_normalized:%s* OR data_keywords:%s*' AND locale = 'en_US'",
                mQueryText, mQueryText, mQueryText);
    }
@@ -90,35 +97,89 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
        if (cursorResults == null) {
            return null;
        }
        final Map<String, Context> contextMap = new HashMap<>();
        final ArrayList<SearchResult> results = new ArrayList<>();

        while (cursorResults.moveToNext()) {
            final String title = cursorResults.getString(Index.COLUMN_INDEX_TITLE);
            final String summaryOn = cursorResults.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
            final ArrayList<String> breadcrumbs = new ArrayList<>();
            final int rank = cursorResults.getInt(COLUMN_INDEX_XML_RES_RANK);

            final String intentString = cursorResults.getString(Index.COLUMN_INDEX_INTENT_ACTION);
            final IntentPayload intentPayload = new IntentPayload(new Intent(intentString));
            final int iconID = cursorResults.getInt(COLUMN_INDEX_RAW_ICON_RESID);
            Drawable icon;
            try {
                icon = mContext.getDrawable(iconID);
            } catch (Resources.NotFoundException nfe) {
                icon = mContext.getDrawable(R.drawable.ic_search_history);
            SearchResult result = buildSingleSearchResultFromCursor(contextMap, cursorResults);
            if (result != null) {
                results.add(result);
            }
        }
        Collections.sort(results);
        return results;
    }

    private SearchResult buildSingleSearchResultFromCursor(Map<String, Context> contextMap,
            Cursor cursor) {
        final String pkgName = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
        final String action = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION);
        final String title = cursor.getString(Index.COLUMN_INDEX_TITLE);
        final String summaryOn = cursor.getString(Index.COLUMN_INDEX_SUMMARY_ON);
        final String className = cursor.getString(Index.COLUMN_INDEX_CLASS_NAME);
        final int rank = cursor.getInt(Index.COLUMN_INDEX_RANK);
        final String key = cursor.getString(Index.COLUMN_INDEX_KEY);
        final String iconResStr = cursor.getString(Index.COLUMN_INDEX_ICON);

        final ResultPayload payload;
        if (TextUtils.isEmpty(action)) {
            final String screenTitle = cursor.getString(Index.COLUMN_INDEX_SCREEN_TITLE);
            // Action is null, we will launch it as a sub-setting
            final Bundle args = new Bundle();
            args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
            final Intent intent = Utils.onBuildStartFragmentIntent(mContext,
                    className, args, null, 0, screenTitle, false);
            payload = new IntentPayload(intent);
        } else {
            final Intent intent = new Intent(action);
            final String targetClass = cursor.getString(
                    Index.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS);
            if (!TextUtils.isEmpty(pkgName) && !TextUtils.isEmpty(targetClass)) {
                final ComponentName component = new ComponentName(pkgName, targetClass);
                intent.setComponent(component);
            }
            intent.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
            payload = new IntentPayload(intent);
        }
        SearchResult.Builder builder = new SearchResult.Builder();
        builder.addTitle(title)
                .addSummary(summaryOn)
                    .addBreadcrumbs(breadcrumbs)
                .addRank(rank)
                    .addIcon(icon)
                    .addPayload(intentPayload);
            results.add(builder.build());
                .addIcon(getIconForPackage(contextMap, pkgName, className, iconResStr))
                .addPayload(payload);
        return builder.build();
    }
        Collections.sort(results);
        return results;

    private Drawable getIconForPackage(Map<String, Context> contextMap, String pkgName,
            String className, String iconResStr) {
        final int iconId = TextUtils.isEmpty(iconResStr)
                ? 0 : Integer.parseInt(iconResStr);
        Drawable icon;
        Context packageContext;
        if (iconId == 0) {
            icon = null;
        } else {
            if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(pkgName)) {
                packageContext = contextMap.get(pkgName);
                if (packageContext == null) {
                    try {
                        packageContext = mContext.createPackageContext(pkgName, 0);
                    } catch (PackageManager.NameNotFoundException e) {
                        Log.e(LOG, "Cannot create Context for package: " + pkgName);
                        return null;
                    }
                    contextMap.put(pkgName, packageContext);
                }
            } else {
                packageContext = mContext;
            }
            try {
                icon = packageContext.getDrawable(iconId);
            } catch (Resources.NotFoundException nfe) {
                icon = null;
            }
        }
        return icon;
    }

}
+3 −0
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@ public class IntentSearchViewHolder extends SearchViewHolder {
        titleView.setText(result.title);
        summaryView.setText(result.summary);
        iconView.setImageDrawable(result.icon);
        if (result.icon == null) {
            iconView.setBackgroundResource(R.drawable.empty_icon);
        }
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
+11 −0
Original line number Diff line number Diff line
@@ -31,8 +31,19 @@ public abstract class ResultPayload implements Parcelable {
    @IntDef({PayloadType.INLINE_SLIDER, PayloadType.INLINE_SWITCH, PayloadType.INTENT})
    @Retention(RetentionPolicy.SOURCE)
    public @interface PayloadType {
        /**
         * Resulting page will be started using an intent
         */
        int INTENT = 0;

        /**
         * Result is a inline widget, using a slider widget as UI.
         */
        int INLINE_SLIDER = 1;

        /**
         * Result is a inline widget, using a toggle widget as UI.
         */
        int INLINE_SWITCH = 2;
    }

+3 −9
Original line number Diff line number Diff line
@@ -83,7 +83,6 @@ public class SearchResult implements Comparable<SearchResult> {
        payload = builder.mResultPayload;
        viewType = payload.getType();
        stableId = Objects.hash(title, summary, breadcrumbs, rank, icon, payload, viewType);

    }

    @Override
@@ -98,7 +97,7 @@ public class SearchResult implements Comparable<SearchResult> {
        protected CharSequence mTitle;
        protected CharSequence mSummary;
        protected ArrayList<String> mBreadcrumbs;
        protected int mRank = -1;
        protected int mRank = 42;
        protected ResultPayload mResultPayload;
        protected Drawable mIcon;

@@ -118,10 +117,9 @@ public class SearchResult implements Comparable<SearchResult> {
        }

        public Builder addRank(int rank) {
            if (rank < 0 || rank > 9) {
                rank = 42;
            }
            if (rank >= 0 && rank <= 9) {
                mRank = rank;
            }
            return this;
        }

@@ -139,10 +137,6 @@ public class SearchResult implements Comparable<SearchResult> {
            // Check that all of the mandatory fields are set.
            if (mTitle == null) {
                throw new IllegalArgumentException("SearchResult missing title argument");
            } else if (mRank == -1) {
                throw new IllegalArgumentException("SearchResult missing rank argument");
            } else if (mIcon == null) {
                throw new IllegalArgumentException("SearchResult missing icon argument");
            } else if (mResultPayload == null) {
                throw new IllegalArgumentException("SearchResult missing Payload argument");
            }
+55 −10
Original line number Diff line number Diff line
@@ -22,20 +22,23 @@ import android.content.Context;
import android.content.Intent;
import android.database.MatrixCursor;
import android.graphics.drawable.Drawable;

import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.SubSettings;
import com.android.settings.TestConfig;
import com.android.settings.gestures.GestureSettings;
import com.android.settings.search2.DatabaseResultLoader;
import com.android.settings.search2.IntentPayload;
import com.android.settings.search2.ResultPayload;
import com.android.settings.search2.ResultPayload.PayloadType;
import com.android.settings.search2.SearchResult;
import com.android.settings.R;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.robolectric.annotation.Config;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;

import java.util.ArrayList;
import java.util.List;
@@ -47,6 +50,10 @@ import static com.google.common.truth.Truth.assertThat;
public class DatabaseResultLoaderTest {
    private DatabaseResultLoader mLoader;

    private static final String[] COLUMNS = new String[]{"rank", "title", "summary_on",
            "summary off", "entries", "keywords", "class name", "screen title", "icon",
            "intent action", "target package", "target class", "enabled", "key", "user id"};

    private static final String[] TITLES = new String[]{"title1", "title2", "title3"};
    private static final String SUMMARY = "SUMMARY";
    private static final int EXAMPLES = 3;
@@ -107,6 +114,16 @@ public class DatabaseResultLoaderTest {
        }
    }

    @Test
    public void testParseCursor_NoIcon() {
        List<SearchResult> results = mLoader.parseCursorForSearch(
                getDummyCursor(false /* hasIcon */));
        for (int i = 0; i < EXAMPLES; i++) {
            Drawable resultDrawable = results.get(i).icon;
            assertThat(resultDrawable).isNull();
        }
    }

    @Test
    public void testParseCursor_MatchesPayloadType() {
        List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
@@ -117,6 +134,33 @@ public class DatabaseResultLoaderTest {
        }
    }

    @Test
    public void testParseCursor_MatchesIntentForSubSettings() {
        MatrixCursor cursor = new MatrixCursor(COLUMNS);
        final String BLANK = "";
        cursor.addRow(new Object[]{
                0,       // rank
                TITLES[0],
                SUMMARY,
                SUMMARY, // summary off
                BLANK,   // entries
                BLANK,   // Keywords
                GestureSettings.class.getName(),
                BLANK,   // screen title
                null,    // icon
                BLANK,   // action
                null,    // target package
                BLANK,   // target class
                BLANK,   // enabled
                BLANK,   // key
                BLANK    // user id
        });
        List<SearchResult> results = mLoader.parseCursorForSearch(cursor);
        IntentPayload payload = (IntentPayload) results.get(0).payload;
        Intent intent = payload.intent;
        assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
    }

    @Test
    public void testParseCursor_MatchesIntentPayload() {
        List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
@@ -129,14 +173,15 @@ public class DatabaseResultLoaderTest {
    }

    private MatrixCursor getDummyCursor() {
        String[] columns = new String[] {"rank", "title",  "summary_on", "summary off", "entries",
                "keywords", "class name", "screen title", "icon", "intent action",
                "target package", "target class", "enabled", "key", "user id"};
        MatrixCursor cursor = new MatrixCursor(columns);
        return getDummyCursor(true /* hasIcon */);
    }

    private MatrixCursor getDummyCursor(boolean hasIcon) {
        MatrixCursor cursor = new MatrixCursor(COLUMNS);
        final String BLANK = "";

        for (int i = 0; i < EXAMPLES; i++) {
            ArrayList<String> item = new ArrayList<>(columns.length);
            ArrayList<String> item = new ArrayList<>(COLUMNS.length);
            item.add(Integer.toString(i));
            item.add(TITLES[i]);
            item.add(SUMMARY);
@@ -145,7 +190,7 @@ public class DatabaseResultLoaderTest {
            item.add(BLANK); // keywords
            item.add(BLANK); // classname
            item.add(BLANK); // screen title
            item.add(Integer.toString(mIcon));
            item.add(hasIcon ? Integer.toString(mIcon) : null);
            item.add(mIntent.getAction());
            item.add(BLANK); // target package
            item.add(BLANK); // target class
Loading