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

Commit c9f150aa authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Create Spa search converters

SearchIndexableDataConverter - could cover the current Settings usages
including SIMs page and SIM page.

SpaSearchIndexablesProvider - could support search usage outside
of Settings.

Bug: 414693711
Flag: EXEMPT refactor
Test: manual - test Settings search
Test: unit test
Change-Id: Idbb7a892790f20a60187064af6a3401abeeadab6
parent f5f0bcf3
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.spa.search

import android.content.Context
import android.provider.SearchIndexableResource
import com.android.settingslib.search.Indexable
import com.android.settingslib.search.SearchIndexableData
import com.android.settingslib.search.SearchIndexableRaw

/**
 * A converter which could provide search data through
 * [com.android.settingslib.search.SearchIndexableResources].
 *
 * @param intentAction The Intent action for the search landing activity.
 * @param intentTargetClass The Intent target class for the search landing activity.
 */
class SearchIndexableDataConverter(
    private val intentAction: String,
    private val intentTargetClass: String,
) {
    fun toSearchIndexableData(searchIndexablePage: SpaSearchIndexablePage): SearchIndexableData {
        val targetClass = searchIndexablePage.targetClass
        val searchIndexProvider =
            object : Indexable.SearchIndexProvider {
                override fun getXmlResourcesToIndex(
                    context: Context,
                    enabled: Boolean,
                ): List<SearchIndexableResource> = emptyList()

                override fun getRawDataToIndex(
                    context: Context,
                    enabled: Boolean,
                ): List<SearchIndexableRaw> = emptyList()

                override fun getDynamicRawDataToIndex(
                    context: Context,
                    enabled: Boolean,
                ): List<SearchIndexableRaw> =
                    searchIndexablePage.itemsProvider(context).map { item ->
                        item.toSearchIndexableRaw(context, targetClass)
                    }

                override fun getNonIndexableKeys(context: Context): List<String> = emptyList()
            }
        return SearchIndexableData(targetClass, searchIndexProvider)
    }

    private fun SpaSearchIndexableItem.toSearchIndexableRaw(
        context: Context,
        targetClass: Class<*>,
    ): SearchIndexableRaw =
        SearchIndexableRaw(context).apply {
            key = searchLandingKey.encodeToString()
            title = itemTitle
            keywords = this@toSearchIndexableRaw.keywords
            screenTitle = pageTitle
            intentAction = this@SearchIndexableDataConverter.intentAction
            intentTargetClass = this@SearchIndexableDataConverter.intentTargetClass
            packageName = context.packageName
            className = targetClass.name
        }
}
+1 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ interface SearchablePage {
    data class SearchItem(val itemTitle: String, val keywords: String? = null)

    /** Gets the title of the page. */
    fun getPageTitleForSearch(context: Context): String = ""
    fun getPageTitleForSearch(context: Context): String

    /** Gets the titles of the searchable items at the current moment. */
    fun getSearchItems(context: Context): List<SearchItem>
+31 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.spa.search

import android.content.Context

data class SpaSearchIndexablePage(
    val targetClass: Class<*>,
    val itemsProvider: (Context) -> List<SpaSearchIndexableItem>,
)

data class SpaSearchIndexableItem(
    val searchLandingKey: SpaSearchLanding.SpaSearchLandingKey,
    val pageTitle: String,
    val itemTitle: String,
    val keywords: String? = null,
)
+61 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.spa.search

import android.database.Cursor
import android.database.MatrixCursor
import android.provider.SearchIndexablesContract
import android.provider.SearchIndexablesContract.RawData
import android.provider.SearchIndexablesProvider

/** A abstract class which could provide search data through [SearchIndexablesProvider]. */
abstract class SpaSearchIndexablesProvider : SearchIndexablesProvider() {
    override fun onCreate() = true

    override fun queryXmlResources(projection: Array<out String?>?) =
        MatrixCursor(SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS)

    override fun queryRawData(projection: Array<out String?>?) =
        MatrixCursor(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS)

    override fun queryNonIndexableKeys(projection: Array<out String?>?) =
        MatrixCursor(SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS)

    override fun queryDynamicRawData(projection: Array<out String?>?): Cursor {
        val context = requireContext()
        val cursor = MatrixCursor(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS)
        for (searchIndexablePage in getSpaSearchIndexablePageList()) {
            for (item in searchIndexablePage.itemsProvider(context)) {
                cursor
                    .newRow()
                    .add(RawData.COLUMN_KEY, item.searchLandingKey)
                    .add(RawData.COLUMN_TITLE, item.itemTitle)
                    .add(RawData.COLUMN_KEYWORDS, item.keywords)
                    .add(RawData.COLUMN_SCREEN_TITLE, item.pageTitle)
                    .add(RawData.COLUMN_INTENT_ACTION, intentAction)
            }
        }
        return cursor
    }

    /** The Intent action for the search landing activity. */
    abstract val intentAction: String

    /** The search indexable page data. */
    open fun getSpaSearchIndexablePageList(): List<SpaSearchIndexablePage> =
        SpaSearchRepository().getSearchIndexablePageList()
}
+23 −89
Original line number Diff line number Diff line
@@ -17,39 +17,24 @@
package com.android.settingslib.spa.search

import android.content.Context
import android.provider.SearchIndexableResource
import android.util.Log
import com.android.settingslib.search.Indexable
import com.android.settingslib.search.SearchIndexableData
import com.android.settingslib.search.SearchIndexableRaw
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.search.SearchablePage.SearchItem
import com.android.settingslib.spa.search.SpaSearchLanding.SpaSearchLandingKey
import com.android.settingslib.spa.search.SpaSearchLanding.SpaSearchLandingSpaPage

class SpaSearchRepository(
    private val spaEnvironment: SpaEnvironment = SpaEnvironmentFactory.instance
) {
    /**
     * Gets the search indexable data list
     *
     * @param intentAction The Intent action for the search landing activity.
     * @param intentTargetClass The Intent target class for the search landing activity.
     */
    fun getSearchIndexableDataList(
        intentAction: String,
        intentTargetClass: String,
    ): List<SearchIndexableData> {
        Log.d(TAG, "getSearchIndexableDataList")
        return spaEnvironment.pageProviderRepository.value.getAllProviders().mapNotNull { page ->
class SpaSearchRepository() {
    /** Gets the search indexable data list */
    fun getSearchIndexablePageList(): List<SpaSearchIndexablePage> {
        Log.d(TAG, "getSearchIndexablePage")
        return SpaEnvironmentFactory.instance.pageProviderRepository.value
            .getAllProviders()
            .mapNotNull { page ->
                if (page is SearchablePage) {
                page.createSearchIndexableData(
                    intentAction = intentAction,
                    intentTargetClass = intentTargetClass,
                    page.createSpaSearchIndexablePage(
                        getPageTitleForSearch = page::getPageTitleForSearch,
                    searchItemsProvider = page::getSearchItems,
                        getSearchItems = page::getSearchItems,
                    )
                } else null
            }
@@ -58,76 +43,25 @@ class SpaSearchRepository(
    companion object {
        private const val TAG = "SpaSearchRepository"

        fun SettingsPageProvider.createSearchIndexableData(
            intentAction: String,
            intentTargetClass: String,
        private fun SettingsPageProvider.createSpaSearchIndexablePage(
            getPageTitleForSearch: (context: Context) -> String,
            searchItemsProvider: (context: Context) -> List<SearchItem>,
        ): SearchIndexableData {
            val key =
            getSearchItems: (context: Context) -> List<SearchItem>,
        ): SpaSearchIndexablePage {
            val searchLandingKey =
                SpaSearchLandingKey.newBuilder()
                    .setSpaPage(SpaSearchLandingSpaPage.newBuilder().setDestination(name))
                    .build()
            val indexableClass = this::class.java
            val searchIndexProvider = searchIndexProviderOf { context ->
            return SpaSearchIndexablePage(targetClass = this::class.java) { context ->
                val pageTitle = getPageTitleForSearch(context)
                searchItemsProvider(context).map { searchItem ->
                    createSearchIndexableRaw(
                        context = context,
                        spaSearchLandingKey = key,
                        itemTitle = searchItem.itemTitle,
                        indexableClass = indexableClass,
                getSearchItems(context).map { searchItem ->
                    SpaSearchIndexableItem(
                        searchLandingKey = searchLandingKey,
                        pageTitle = pageTitle,
                        intentAction = intentAction,
                        intentTargetClass = intentTargetClass,
                        itemTitle = searchItem.itemTitle,
                        keywords = searchItem.keywords,
                    )
                }
            }
            return SearchIndexableData(indexableClass, searchIndexProvider)
        }

        fun searchIndexProviderOf(
            getDynamicRawDataToIndex: (context: Context) -> List<SearchIndexableRaw>
        ) =
            object : Indexable.SearchIndexProvider {
                override fun getXmlResourcesToIndex(
                    context: Context,
                    enabled: Boolean,
                ): List<SearchIndexableResource> = emptyList()

                override fun getRawDataToIndex(
                    context: Context,
                    enabled: Boolean,
                ): List<SearchIndexableRaw> = emptyList()

                override fun getDynamicRawDataToIndex(
                    context: Context,
                    enabled: Boolean,
                ): List<SearchIndexableRaw> = getDynamicRawDataToIndex(context)

                override fun getNonIndexableKeys(context: Context): List<String> = emptyList()
            }

        fun createSearchIndexableRaw(
            context: Context,
            spaSearchLandingKey: SpaSearchLandingKey,
            itemTitle: String,
            indexableClass: Class<*>,
            pageTitle: String,
            intentAction: String,
            intentTargetClass: String,
            keywords: String? = null,
        ) =
            SearchIndexableRaw(context).apply {
                key = spaSearchLandingKey.encodeToString()
                title = itemTitle
                this.keywords = keywords
                this.intentAction = intentAction
                this.intentTargetClass = intentTargetClass
                packageName = context.packageName
                className = indexableClass.name
                screenTitle = pageTitle
        }
    }
}
Loading