Hệ thống tìm kiếm cài đặt SettingsIntelligence

Tóm tắt

Hệ thống cài đặt Android có rất nhiều mục cần thiết, trong đó một số mục rất khó tìm kiếm. Google đã hỗ trợ tính năng tìm kiếm thông qua mô-đun SettingsIntelligence nằm trong thư mục packages/apps/SettingsIntelligence. Mô-đun này có tên gói là com.android.settings.intelligence và thực hiện việc trích xuất dữ liệu từ các lớp kế thừa SearchIndexablesProvider, lưu trữ vào cơ sở dữ liệu tại đường dẫn /data/data/com.android.settings.intelligence/databases/search_index.db. Bài viết tập trung phân tích quy trình lưu trữ dữ liệu vào cơ sở dữ liệu.

Bản đồ luồng mã

Khi người dùng lần đầu truy cập tính năng tìm kiếm từ giao diện cài đặt, hệ thống sẽ chuyển hướng đến SettingsIntelligence để tải cơ sở dữ liệu. Dưới đây là sơ đồ luồng xử lý.

Phân tích luồng mã

1. SettingsHomepageActivity.java

Lớp này đại diện cho giao diện chính của cài đặt. Khi người dùng nhấn vào thanh tìm kiếm, sẽ thực hiện các bước sau:


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initializeSearchInterface(); // Khởi tạo thành phần tìm kiếm
}

private void initializeSearchInterface() {
    Toolbar searchToolbar = findViewById(R.id.top_toolbar); // Lấy thanh công cụ tìm kiếm
    FeatureFactory.getFactory(this).getSearchFeatureProvider()
            .setupSearchInterface(this /* activity */, searchToolbar, 
 SettingsEnums.SETTINGS_HOMEPAGE);

    if (isSplitScreenSupported()) { // Thiết bị hỗ trợ chia màn hình
        Toolbar secondaryToolbar = findViewById(R.id.bottom_toolbar);
        FeatureFactory.getFactory(this).getSearchFeatureProvider()
                .setupSearchInterface(this /* activity */, secondaryToolbar,
                        SettingsEnums.SETTINGS_HOMEPAGE);
    }
}

2. SearchFeatureProvider.java

Lớp này xử lý logic liên quan đến tìm kiếm, với lớp triển khai cụ thể là SearchFeatureProviderImpl:


default void setupSearchInterface(FragmentActivity activity, Toolbar toolbar, int screenId) {
    Context appContext = activity.getApplicationContext();
    Intent searchIntent = createSearchActivityIntent(appContext, screenId)
            .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

    toolbar.setOnClickListener(toolbarView -> launchSearchActivity(appContext, activity, screenId, searchIntent));
}

private static void launchSearchActivity(
        Context appContext, FragmentActivity activity, int screenId, Intent searchIntent) {
    FeatureFactory.getFactory(appContext).getSlicesFeatureProvider()
            .indexSliceDataAsync(appContext);

    FeatureFactory.getFactory(appContext).getMetricsFeatureProvider()
            .logSettingsTileInteraction("TOPBAR_SEARCH", screenId);

    Bundle transitionBundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle();
    activity.startActivity(searchIntent, transitionBundle);
}

3. SearchFeatureProviderImpl.java

Lớp này thực hiện việc chuyển hướng đến SearchActivity:


@Override
public Intent createSearchActivityIntent(Context appContext, int screenId) {
    return new Intent(Settings.ACTION_APP_SEARCH_SETTINGS)
            .setPackage(getSettingsIntelligencePackageName(appContext))
            .putExtra(Intent.EXTRA_REFERRER, buildReferrer(appContext, screenId));
}

default String getSettingsIntelligencePackageName(Context appContext) {
    return appContext.getString(R.string.config_settingsintelligence_package_name);
}

4. SearchActivity.java

Lớp này chứa giao diện tìm kiếm:


public class SearchActivity extends FragmentActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.search_layout);

        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment currentFragment = fragmentManager.findFragmentById(R.id.content_container);
        if (currentFragment == null) {
            fragmentManager.beginTransaction()
                    .add(R.id.content_container, new SearchFragment()) // Chèn fragment tìm kiếm
                    .commit();
        }
    }

    @Override
    public boolean onNavigateUp() {
        finish();
        return true;
    }
}

5. SearchFragment.java

Fragment này chứa logic chính của giao diện tìm kiếm:


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    long startTime = System.currentTimeMillis();
    setHasOptionsMenu(true);

    LoaderManager loaderManager = getLoaderManager();
    mSearchAdapter = new SearchResultsAdapter(this);
    mSavedQueriesManager = new SavedQueriesManager(
            getContext(), loaderManager, mSearchAdapter);
    mSearchFeatureProvider.initializeFeedbackButton();

    if (savedInstanceState != null) {
        mQuery = savedInstanceState.getString(SearchConstants.QUERY_KEY);
        mNeverEnteredQuery = savedInstanceState.getBoolean(SearchConstants.NEVER_ENTERED_KEY);
        mShowSavedQueries = savedInstanceState.getBoolean(SearchConstants.SHOW_SAVED_KEY);
    } else {
        mShowSavedQueries = true;
    }

    mSearchFeatureProvider.updateIndexAsync(getContext(), this /* callback */); // Bắt đầu tải cơ sở dữ liệu
}

@Override
public void onIndexingComplete() {
    if (getActivity() == null) return;
    if (mShowSavedQueries) {
        mSavedQueriesManager.loadSavedQueries();
    } else {
        LoaderManager loaderManager = getLoaderManager();
        loaderManager.initLoader(SearchConstants.RESULT_LOADER_ID, null, this);
    }
    refreshResults();
}

6. SettingsIntelligence-SearchFeatureProviderImpl.java

Lớp này thực hiện việc kết nối với DatabaseIndexingManager:


@Override
public void updateIndexAsync(Context appContext, IndexingCallback callback) {
    if (DEBUG) Log.d(TAG, "Updating index asynchronously");
    getIndexingManager(appContext).processIndexing(callback);
}

7. DatabaseIndexingManager.java

Lớp này thực hiện việc thu thập và lưu trữ dữ liệu:


public void processIndexing(IndexingCallback callback) {
    IndexingTask task = new IndexingTask(callback);
    task.execute();
}

private void performIndexing() {
    Intent queryIntent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
    List<ResolveInfo> providers = mContext.getPackageManager()
            .queryIntentContentProviders(queryIntent, 0);

    boolean fullRebuild = IndexDatabaseHelper.isFullRebuildRequired(mContext, providers);
    if (fullRebuild) {
        rebuildDatabase(); // Xóa và tạo lại bảng nếu cần
    }

    PreIndexData collectedData = collectIndexableData(providers, fullRebuild);
    updateDatabase(collectedData, fullRebuild);

    IndexDatabaseHelper.markIndexed(mContext, providers);
}

private void updateDatabase(PreIndexData data, boolean fullRebuild) {
    SQLiteDatabase db = getWritableDatabase();
    for (IndexData entry : data.indexEntries) {
        ContentValues values = new ContentValues();
        values.put(TITLE_COLUMN, entry.title);
        values.put(NORMALIZED_TITLE, entry.normalizedTitle);
        values.put(SUMMARY_COLUMN, entry.summary);
        values.put(PACKAGE_COLUMN, entry.packageName);
        values.put(CLASS_NAME_COLUMN, entry.className);
        db.replaceOrThrow(TABLE_NAME, null, values);
    }
}

8. PreIndexDataCollector.java

Lớp này thực hiện việc thu thập dữ liệu:


public PreIndexData collectIndexableData(List<ResolveInfo> providers, boolean fullRebuild) {
    PreIndexData result = new PreIndexData();
    for (ResolveInfo provider : providers) {
        if (!isKnownProvider(provider)) continue;
        String packageId = provider.providerInfo.packageName;
        String authority = provider.providerInfo.authority;

        if (fullRebuild) {
            fetchIndexableEntries(packageId, authority); // Thu thập dữ liệu
        }
        filterNonIndexableEntries(packageId, authority); // Loại bỏ dữ liệu không cần thiết
    }
    return result;
}

9. IndexDatabaseHelper.java

Lớp này quản lý cơ sở dữ liệu:


public class IndexDatabaseHelper extends SQLiteOpenHelper {
    public static final String TABLE_NAME = "search_entries";
    public static final String TITLE_COLUMN = "entry_title";
    public static final String NORMALIZED_TITLE = "normalized_title";
    public static final String SUMMARY_COLUMN = "summary_text";
    public static final String PACKAGE_COLUMN = "package_name";
    public static final String CLASS_NAME_COLUMN = "class_name";

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
                TITLE_COLUMN + " TEXT, " +
                NORMALIZED_TITLE + " TEXT, " +
                SUMMARY_COLUMN + " TEXT, " +
                PACKAGE_COLUMN + " TEXT, " +
                CLASS_NAME_COLUMN + " TEXT)");
    }

    public void rebuildDatabase(SQLiteDatabase db) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }
}

Thẻ: Android SQLite SearchIndexablesProvider Fragment Intent

Đăng vào ngày 14 tháng 6 lúc 21:39