系统定制-屏蔽特定的设置页面搜索栏中出现的搜索项

定位代码所在模块

搜索栏在设置页面,那么第一想法,肯定是在Settings模块中,而且在Settings模块中也找到了search的相关文件夹与代码,如果你也跟我一样的话,那么你也会在这上面浪费一天甚至更多的时间,停下来先看看这篇文章,会让你节省出这宝贵的时间。

坑位记录:

在点击搜索栏后,可以通过adb命令查看顶层Activity的包名可知
(写一下查询顶层Activity的命令吧:adb shell dumpsys activity | grep -i run
其实搜索功能是在SettingsIntelligence模块中。

从业务入手,看源码逻辑也能追根溯源:
在Settings模块里,初始化搜索框是在SettingsActivity类。在函数中,会监听Toolbar,点击Toolbar后触发初始化。

在点击监听中,能找到函数 indexSliceDataAsync ,也是对数据库进行操作的,会初始化数据库。但是在第二步的搜索数据过程中发现,这里操作的数据库与查找的数据库不是同一个。

其业务流程如下(图片出处):
搜索的业务逻辑流程图

源码分析(基于9.0)

在SettingsIntelligence模块中,找到SearchActivity,初始化添加了碎片SearchFragment

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.search_main);
        // Keeps layouts in-place when keyboard opens.
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

        FragmentManager fragmentManager = getFragmentManager();
        Fragment fragment = fragmentManager.findFragmentById(R.id.main_content);
        if (fragment == null) {
            fragmentManager.beginTransaction()
                    .add(R.id.main_content, new SearchFragment())
                    .commit();
        }
    }

关键类SearchFragment中,在onCreate时初始化了数据库:

mSearchFeatureProvider.updateIndexAsync(getContext(), this /* indexingCallback */);

在DatabaseIndexingManager中开启一个异步任务,去执行初始化操作

public void performIndexing() {
        final long startTime = System.currentTimeMillis();
        final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
        final List<ResolveInfo> providers =
                mContext.getPackageManager().queryIntentContentProviders(intent, 0);

        final String localeStr = Locale.getDefault().toString();
        final String fingerprint = Build.FINGERPRINT;
        final String providerVersionedNames =
                IndexDatabaseHelper.buildProviderVersionedNames(providers);

        final boolean isFullIndex = isFullIndex(mContext, localeStr, fingerprint,
                providerVersionedNames);

        if (isFullIndex) {
            rebuildDatabase();
        }

        PreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex);

        final long updateDatabaseStartTime = System.currentTimeMillis();
        updateDatabase(indexData, isFullIndex);
        if (SettingsSearchIndexablesProvider.DEBUG) {
            final long updateDatabaseTime = System.currentTimeMillis() - updateDatabaseStartTime;
            Log.d(LOG_TAG, "performIndexing updateDatabase took time: " + updateDatabaseTime);
        }

        //TODO(63922686): Setting indexed should be a single method, not 3 separate setters.
        IndexDatabaseHelper.setLocaleIndexed(mContext, localeStr);
        IndexDatabaseHelper.setBuildIndexed(mContext, fingerprint);
        IndexDatabaseHelper.setProvidersIndexed(mContext, providerVersionedNames);

        if (SettingsSearchIndexablesProvider.DEBUG) {
            final long indexingTime = System.currentTimeMillis() - startTime;
            Log.d(LOG_TAG, "performIndexing took time: " + indexingTime
                    + "ms. Full index? " + isFullIndex);
        }
    }

获取到的数据会保存到数据库中

updateDatabase(indexData, isFullIndex);

@VisibleForTesting
    void insertIndexData(SQLiteDatabase database, List<IndexData> indexData) {
        ContentValues values;

        for (IndexData dataRow : indexData) {
            if (TextUtils.isEmpty(dataRow.normalizedTitle)) {
                continue;
            }

            values = new ContentValues();
            values.put(IndexDatabaseHelper.IndexColumns.DOCID, dataRow.getDocId());
            values.put(LOCALE, dataRow.locale);
            values.put(DATA_TITLE, dataRow.updatedTitle);
            values.put(DATA_TITLE_NORMALIZED, dataRow.normalizedTitle);
            values.put(DATA_SUMMARY_ON, dataRow.updatedSummaryOn);
            values.put(DATA_SUMMARY_ON_NORMALIZED, dataRow.normalizedSummaryOn);
            values.put(DATA_ENTRIES, dataRow.entries);
            values.put(DATA_KEYWORDS, dataRow.spaceDelimitedKeywords);
            values.put(CLASS_NAME, dataRow.className);
            values.put(SCREEN_TITLE, dataRow.screenTitle);
            values.put(INTENT_ACTION, dataRow.intentAction);
            values.put(INTENT_TARGET_PACKAGE, dataRow.intentTargetPackage);
            values.put(INTENT_TARGET_CLASS, dataRow.intentTargetClass);
            values.put(ICON, dataRow.iconResId);
            values.put(ENABLED, dataRow.enabled);
            values.put(DATA_KEY_REF, dataRow.key);
            values.put(USER_ID, dataRow.userId);
            values.put(PAYLOAD_TYPE, dataRow.payloadType);
            values.put(PAYLOAD, dataRow.payload);

            database.replaceOrThrow(TABLE_PREFS_INDEX, null, values);

            if (!TextUtils.isEmpty(dataRow.className)
                    && !TextUtils.isEmpty(dataRow.childClassName)) {
                final ContentValues siteMapPair = new ContentValues();
                siteMapPair.put(SiteMapColumns.PARENT_CLASS, dataRow.className);
                siteMapPair.put(SiteMapColumns.PARENT_TITLE, dataRow.screenTitle);
                siteMapPair.put(SiteMapColumns.CHILD_CLASS, dataRow.childClassName);
                siteMapPair.put(SiteMapColumns.CHILD_TITLE, dataRow.updatedTitle);

                database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_SITE_MAP,
                        null /* nullColumnHack */, siteMapPair);
            }
        }
    }

处理数据集合

修改代码需要关注的是数据最后的整理和使用,在SearchResultAggregator类中,函数mergeSearchResults整合了所有查询到的数据并整理为一个list集合

// TODO (b/68255021) scale the dynamic search results ranks
    private List<? extends SearchResult> mergeSearchResults(
            Map<Integer, List<? extends SearchResult>> taskResults) {

        final List<SearchResult> searchResults = new ArrayList<>();
        // First add db results as a special case
        searchResults.addAll(taskResults.remove(DatabaseResultTask.QUERY_WORKER_ID));

        // Merge the rest into result list: add everything to heap then pop them out one by one.
        final PriorityQueue<SearchResult> heap = new PriorityQueue<>();
        for (List<? extends SearchResult> taskResult : taskResults.values()) {
            heap.addAll(taskResult);
        }
        while (!heap.isEmpty()) {
            searchResults.add(heap.poll());
        }
        return searchResults;
    }

最终的最终,还是回到了SearchFragment类来,onLoadFinished函数接收了最终的数据,并使用adapter来适配展示数据:

@Override
    public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
                               List<? extends SearchResult> data) {
        mSearchAdapter.postSearchResults(searchResults);
    }

现在业务逻辑一目了然,如果要对展示的数据做处理的话,在给adapter设置数据前对这个list数据做修改就可以了,数据模型SearchResult查看源码可知:

/**
 * Data class as an interface for all Search Results.
 */
public class SearchResult implements Comparable<SearchResult> {

    private static final String TAG = "SearchResult";

    /**
     * Defines the lowest rank for a search result to be considered as ranked. Results with ranks
     * higher than this have no guarantee for sorting order.
     */
    public static final int BOTTOM_RANK = 10;

    /**
     * Defines the highest rank for a search result. Used for special search results only.
     */
    public static final int TOP_RANK = 0;

    /**
     * The title of the result and main text displayed.
     * Intent Results: Displays as the primary
     */
    public final CharSequence title;

    /**
     * Summary / subtitle text
     * Intent Results: Displays the text underneath the title
     */
    final public CharSequence summary;

    /**
     * An ordered list of the information hierarchy.
     * Intent Results: Displayed a hierarchy of selections to reach the setting from the home screen
     */
    public final List<String> breadcrumbs;

    /**
     * A suggestion for the ranking of the result.
     * Based on Settings Rank:
     * 1 is a near perfect match
     * 9 is the weakest match
     * TODO subject to change
     */
    public final int rank;

    /**
     * Identifier for the recycler view adapter.
     */
    @ResultPayload.PayloadType
    public final int viewType;

    /**
     * Metadata for the specific result types.
     */
    public final ResultPayload payload;

    /**
     * Result's icon.
     */
    public final Drawable icon;

    /**
     * A unique key for this object.
     */
    public final String dataKey;

    protected SearchResult(Builder builder) {
        dataKey = builder.mDataKey;
        title = builder.mTitle;
        summary = builder.mSummary;
        breadcrumbs = builder.mBreadcrumbs;
        rank = builder.mRank;
        icon = builder.mIcon;
        payload = builder.mResultPayload;
        viewType = payload.getType();
    }

    @Override
    public int compareTo(SearchResult searchResult) {
        if (searchResult == null) {
            return -1;
        }
        return this.rank - searchResult.rank;
    }

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (!(that instanceof SearchResult)) {
            return false;
        }
        return TextUtils.equals(dataKey, ((SearchResult) that).dataKey);
    }

    @Override
    public int hashCode() {
        return dataKey.hashCode();
    }

    public static class Builder {
        private CharSequence mTitle;
        private CharSequence mSummary;
        private List<String> mBreadcrumbs;
        private int mRank = 42;
        private ResultPayload mResultPayload;
        private Drawable mIcon;
        private String mDataKey;

        public Builder setTitle(CharSequence title) {
            mTitle = title;
            return this;
        }

        public Builder setSummary(CharSequence summary) {
            mSummary = summary;
            return this;
        }

        public Builder addBreadcrumbs(List<String> breadcrumbs) {
            mBreadcrumbs = breadcrumbs;
            return this;
        }

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

        public Builder setIcon(Drawable icon) {
            mIcon = icon;
            return this;
        }

        public Builder setPayload(ResultPayload payload) {
            mResultPayload = payload;
            return this;
        }

        public Builder setDataKey(String key) {
            mDataKey = key;
            return this;
        }

        public SearchResult build() {
            // Check that all of the mandatory fields are set.
            if (TextUtils.isEmpty(mTitle)) {
                throw new IllegalStateException("SearchResult missing title argument");
            } else if (TextUtils.isEmpty(mDataKey)) {
                Log.v(TAG, "No data key on SearchResult with title: " + mTitle);
                throw new IllegalStateException("SearchResult missing stableId argument");
            } else if (mResultPayload == null) {
                throw new IllegalStateException("SearchResult missing Payload argument");
            }
            return new SearchResult(this);
        }
    }
}

其中有public final ResultPayload payload;对象需要注意,此对象中添加有Intent意图,显然每个要跳转的功能的意图都是不一样的,以此来做数据删选是比较合理的,那么onLoadFinished函数里,可以做如下修改:

@Override
    public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
                               List<? extends SearchResult> data) {
        List<SearchResult> searchResults = new ArrayList<>();
        if (data != null) {
            for (int i = 0; i < data.size(); i++) {
                SearchResult st = (SearchResult) data.get(i);
                Intent intent = st.payload.getIntent();
                for (int j = 0; j < dataSize.length; j++) {
                    if (!intent.getExtras().toString().contains("=" + dataSize[j] + "]")) {
                        searchResults.add(st);
                    }
                }
            }
        }
        mSearchAdapter.postSearchResults(searchResults);
    }

//这就是你要剔除的功能选项
private final String[] dataSize = new String[]{"376"};

那么问题来了我是如何知道哪个功能的dataSize是什么呢,framework又不能debug调试,对的,虽然不能调试,但是我们可以另辟蹊径,将每个功能的dataSize直接显示在列表上,就能直观的看到对应关系了,这个我就不给你一步一步说明了,直接,SearchViewHolder类是搜索时的列表的Holder类,改这里吧,在onBind(SearchFragment fragment, SearchResult result)函数内,修改titleView.setText(result.title);
为:

Intent intent = result.payload.getIntent();
titleView.setText(result.title+"cs:"+intent.getExtras().toString());

就能直观的在UI上看到对应关系,然后修改集合就行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值