Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/search/SpaSearchContract.kt 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 /** * Intent action used to identify SpaSearchProvider instances. This is used in the {@code * <intent-filter>} of a {@code <provider>}. */ const val PROVIDER_INTERFACE = "android.content.action.SPA_SEARCH_PROVIDER" /** ContentProvider path for search static data */ const val SEARCH_STATIC_DATA = "search_static_data" /** ContentProvider path for search dynamic data */ const val SEARCH_DYNAMIC_DATA = "search_dynamic_data" /** ContentProvider path for search immutable status */ const val SEARCH_IMMUTABLE_STATUS = "search_immutable_status" /** ContentProvider path for search mutable status */ const val SEARCH_MUTABLE_STATUS = "search_mutable_status" /** Enum to define all column names in provider. */ enum class ColumnEnum(val id: String) { ENTRY_ID("entryId"), SEARCH_TITLE("searchTitle"), SEARCH_KEYWORD("searchKw"), SEARCH_PATH("searchPath"), INTENT_TARGET_PACKAGE("intentTargetPackage"), INTENT_TARGET_CLASS("intentTargetClass"), INTENT_EXTRAS("intentExtras"), SLICE_URI("sliceUri"), LEGACY_KEY("legacyKey"), ENTRY_DISABLED("entryDisabled"), } /** Enum to define all queries supported in the provider. */ @SuppressWarnings("Immutable") enum class QueryEnum( val queryPath: String, val columnNames: List<ColumnEnum> ) { SEARCH_STATIC_DATA_QUERY( SEARCH_STATIC_DATA, listOf( ColumnEnum.ENTRY_ID, ColumnEnum.SEARCH_TITLE, ColumnEnum.SEARCH_KEYWORD, ColumnEnum.SEARCH_PATH, ColumnEnum.INTENT_TARGET_PACKAGE, ColumnEnum.INTENT_TARGET_CLASS, ColumnEnum.INTENT_EXTRAS, ColumnEnum.SLICE_URI, ColumnEnum.LEGACY_KEY ) ), SEARCH_DYNAMIC_DATA_QUERY( SEARCH_DYNAMIC_DATA, listOf( ColumnEnum.ENTRY_ID, ColumnEnum.SEARCH_TITLE, ColumnEnum.SEARCH_KEYWORD, ColumnEnum.SEARCH_PATH, ColumnEnum.INTENT_TARGET_PACKAGE, ColumnEnum.INTENT_TARGET_CLASS, ColumnEnum.SLICE_URI, ColumnEnum.LEGACY_KEY ) ), SEARCH_IMMUTABLE_STATUS_DATA_QUERY( SEARCH_IMMUTABLE_STATUS, listOf( ColumnEnum.ENTRY_ID, ColumnEnum.ENTRY_DISABLED, ) ), SEARCH_MUTABLE_STATUS_DATA_QUERY( SEARCH_MUTABLE_STATUS, listOf( ColumnEnum.ENTRY_ID, ColumnEnum.ENTRY_DISABLED, ) ), } packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/search/SpaSearchProvider.kt +52 −24 Original line number Diff line number Diff line Loading @@ -19,22 +19,22 @@ package com.android.settingslib.spa.search import android.content.ContentProvider import android.content.ContentValues import android.content.Context import android.content.Intent import android.content.UriMatcher import android.content.pm.ProviderInfo import android.database.Cursor import android.database.MatrixCursor import android.net.Uri import android.os.Parcel import android.os.Parcelable import android.util.Log import androidx.annotation.VisibleForTesting import com.android.settingslib.spa.framework.common.ColumnEnum import com.android.settingslib.spa.framework.common.QueryEnum import com.android.settingslib.spa.framework.common.EntryStatusData import com.android.settingslib.spa.framework.common.SettingsEntry import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory import com.android.settingslib.spa.framework.common.addUri import com.android.settingslib.spa.framework.common.getColumns import com.android.settingslib.spa.framework.util.SESSION_SEARCH import com.android.settingslib.spa.framework.util.createIntent import com.android.settingslib.spa.slice.fromEntry private const val TAG = "SpaSearchProvider" Loading @@ -42,18 +42,25 @@ private const val TAG = "SpaSearchProvider" * The content provider to return entry related data, which can be used for search and hierarchy. * One can query the provider result by: * $ adb shell content query --uri content://<AuthorityPath>/<QueryPath> * For gallery, AuthorityPath = com.android.spa.gallery.provider * For Settings, AuthorityPath = com.android.settings.spa.provider * For gallery, AuthorityPath = com.android.spa.gallery.search.provider * For Settings, AuthorityPath = com.android.settings.spa.search.provider" * Some examples: * $ adb shell content query --uri content://<AuthorityPath>/search_static * $ adb shell content query --uri content://<AuthorityPath>/search_dynamic * $ adb shell content query --uri content://<AuthorityPath>/search_mutable_status * $ adb shell content query --uri content://<AuthorityPath>/search_static_data * $ adb shell content query --uri content://<AuthorityPath>/search_dynamic_data * $ adb shell content query --uri content://<AuthorityPath>/search_immutable_status * $ adb shell content query --uri content://<AuthorityPath>/search_mutable_status */ class SpaSearchProvider : ContentProvider() { private val spaEnvironment get() = SpaEnvironmentFactory.instance private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH) private val queryMatchCode = mapOf( SEARCH_STATIC_DATA to 301, SEARCH_DYNAMIC_DATA to 302, SEARCH_MUTABLE_STATUS to 303, SEARCH_IMMUTABLE_STATUS to 304 ) override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int { TODO("Implement this to handle requests to delete one or more rows") } Loading Loading @@ -85,10 +92,9 @@ class SpaSearchProvider : ContentProvider() { override fun attachInfo(context: Context?, info: ProviderInfo?) { if (info != null) { QueryEnum.SEARCH_STATIC_DATA_QUERY.addUri(uriMatcher, info.authority) QueryEnum.SEARCH_DYNAMIC_DATA_QUERY.addUri(uriMatcher, info.authority) QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY.addUri(uriMatcher, info.authority) QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY.addUri(uriMatcher, info.authority) for (entry in queryMatchCode) { uriMatcher.addURI(info.authority, entry.key, entry.value) } } super.attachInfo(context, info) } Loading @@ -102,11 +108,11 @@ class SpaSearchProvider : ContentProvider() { ): Cursor? { return try { when (uriMatcher.match(uri)) { QueryEnum.SEARCH_STATIC_DATA_QUERY.queryMatchCode -> querySearchStaticData() QueryEnum.SEARCH_DYNAMIC_DATA_QUERY.queryMatchCode -> querySearchDynamicData() QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY.queryMatchCode -> queryMatchCode[SEARCH_STATIC_DATA] -> querySearchStaticData() queryMatchCode[SEARCH_DYNAMIC_DATA] -> querySearchDynamicData() queryMatchCode[SEARCH_MUTABLE_STATUS] -> querySearchMutableStatusData() QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY.queryMatchCode -> queryMatchCode[SEARCH_IMMUTABLE_STATUS] -> querySearchImmutableStatusData() else -> throw UnsupportedOperationException("Unknown Uri $uri") } Loading Loading @@ -167,23 +173,45 @@ class SpaSearchProvider : ContentProvider() { // Fetch search data. We can add runtime arguments later if necessary val searchData = entry.getSearchData() ?: return val intent = entry.createIntent(SESSION_SEARCH) ?: Intent() cursor.newRow() .add(ColumnEnum.ENTRY_ID.id, entry.id) .add(ColumnEnum.ENTRY_INTENT_URI.id, intent.toUri(Intent.URI_INTENT_SCHEME)) val intent = entry.createIntent(SESSION_SEARCH) val row = cursor.newRow().add(ColumnEnum.ENTRY_ID.id, entry.id) .add(ColumnEnum.SEARCH_TITLE.id, searchData.title) .add(ColumnEnum.SEARCH_KEYWORD.id, searchData.keyword) .add( ColumnEnum.SEARCH_PATH.id, entryRepository.getEntryPathWithTitle(entry.id, searchData.title) ) intent?.let { row.add(ColumnEnum.INTENT_TARGET_PACKAGE.id, spaEnvironment.appContext.packageName) .add(ColumnEnum.INTENT_TARGET_CLASS.id, spaEnvironment.browseActivityClass?.name) .add(ColumnEnum.INTENT_EXTRAS.id, marshall(intent.extras)) } if (entry.hasSliceSupport) row.add( ColumnEnum.SLICE_URI.id, Uri.Builder() .fromEntry(entry, spaEnvironment.sliceProviderAuthorities) ) // TODO: support legacy key } private fun fetchStatusData(entry: SettingsEntry, cursor: MatrixCursor) { // Fetch status data. We can add runtime arguments later if necessary val statusData = entry.getStatusData() ?: return val statusData = entry.getStatusData() ?: EntryStatusData() cursor.newRow() .add(ColumnEnum.ENTRY_ID.id, entry.id) .add(ColumnEnum.SEARCH_STATUS_DISABLED.id, statusData.isDisabled) .add(ColumnEnum.ENTRY_DISABLED.id, statusData.isDisabled) } private fun QueryEnum.getColumns(): Array<String> { return columnNames.map { it.id }.toTypedArray() } private fun marshall(parcelable: Parcelable?): ByteArray? { if (parcelable == null) return null val parcel = Parcel.obtain() parcelable.writeToParcel(parcel, 0) val bytes = parcel.marshall() parcel.recycle() return bytes } } packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/search/SpaSearchProviderTest.kt +101 −8 Original line number Diff line number Diff line Loading @@ -18,15 +18,14 @@ package com.android.settingslib.spa.search import android.content.Context import android.database.Cursor import android.os.Bundle import android.os.Parcel import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.framework.common.ColumnEnum import com.android.settingslib.spa.framework.common.QueryEnum import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory import com.android.settingslib.spa.framework.common.createSettingsPage import com.android.settingslib.spa.framework.common.getIndex import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest import com.android.settingslib.spa.tests.testutils.SppForSearch import com.google.common.truth.Truth Loading @@ -39,51 +38,145 @@ class SpaSearchProviderTest { private val spaEnvironment = SpaEnvironmentForTest(context, listOf(SppForSearch.createSettingsPage())) private val searchProvider = SpaSearchProvider() private val pageOwner = spaEnvironment.createPage("SppForSearch") @Test fun testQuerySearchStatusData() { SpaEnvironmentFactory.reset(spaEnvironment) val pageOwner = spaEnvironment.createPage("SppForSearch") val immutableStatus = searchProvider.querySearchImmutableStatusData() Truth.assertThat(immutableStatus.count).isEqualTo(1) Truth.assertThat(immutableStatus.count).isEqualTo(2) immutableStatus.moveToFirst() immutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchStaticWithNoStatus") ) immutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_DISABLED, false.toString() ) immutableStatus.moveToNext() immutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchDynamicWithImmutableStatus") ) immutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_DISABLED, true.toString() ) val mutableStatus = searchProvider.querySearchMutableStatusData() Truth.assertThat(mutableStatus.count).isEqualTo(2) mutableStatus.moveToFirst() mutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchStaticWithMutableStatus") ) mutableStatus.checkValue( QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_DISABLED, false.toString() ) mutableStatus.moveToNext() mutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchDynamicWithMutableStatus") ) mutableStatus.checkValue( QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_DISABLED, true.toString() ) } @Test fun testQuerySearchIndexData() { SpaEnvironmentFactory.reset(spaEnvironment) val staticData = searchProvider.querySearchStaticData() Truth.assertThat(staticData.count).isEqualTo(2) staticData.moveToFirst() staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchStaticWithNoStatus") ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.SEARCH_TITLE, "SearchStaticWithNoStatus" ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.SEARCH_KEYWORD, listOf("").toString() ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.SEARCH_PATH, listOf("SearchStaticWithNoStatus", "SppForSearch").toString() ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.INTENT_TARGET_PACKAGE, spaEnvironment.appContext.packageName ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.INTENT_TARGET_CLASS, "com.android.settingslib.spa.tests.testutils.BlankActivity" ) // Check extras in intent val bundle = staticData.getExtras(QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.INTENT_EXTRAS) Truth.assertThat(bundle).isNotNull() Truth.assertThat(bundle!!.size()).isEqualTo(3) Truth.assertThat(bundle.getString("spaActivityDestination")).isEqualTo("SppForSearch") Truth.assertThat(bundle.getString("highlightEntry")) .isEqualTo(pageOwner.getEntryId("SearchStaticWithNoStatus")) Truth.assertThat(bundle.getString("sessionSource")).isEqualTo("search") staticData.moveToNext() staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchStaticWithMutableStatus") ) val dynamicData = searchProvider.querySearchDynamicData() Truth.assertThat(dynamicData.count).isEqualTo(2) dynamicData.moveToFirst() dynamicData.checkValue( QueryEnum.SEARCH_DYNAMIC_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchDynamicWithMutableStatus") ) dynamicData.moveToNext() dynamicData.checkValue( QueryEnum.SEARCH_DYNAMIC_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchDynamicWithImmutableStatus") ) dynamicData.checkValue( QueryEnum.SEARCH_DYNAMIC_DATA_QUERY, ColumnEnum.SEARCH_KEYWORD, listOf("kw1", "kw2").toString() ) } } private fun Cursor.checkValue(query: QueryEnum, column: ColumnEnum, value: String) { Truth.assertThat(getString(query.getIndex(column))).isEqualTo(value) Truth.assertThat(getString(query.columnNames.indexOf(column))).isEqualTo(value) } private fun Cursor.getExtras(query: QueryEnum, column: ColumnEnum): Bundle? { val extrasByte = getBlob(query.columnNames.indexOf(column)) ?: return null val parcel = Parcel.obtain() parcel.unmarshall(extrasByte, 0, extrasByte.size) parcel.setDataPosition(0) val bundle = Bundle() bundle.readFromParcel(parcel) return bundle } private fun SettingsPage.getEntryId(name: String): String { Loading Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/search/SpaSearchContract.kt 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 /** * Intent action used to identify SpaSearchProvider instances. This is used in the {@code * <intent-filter>} of a {@code <provider>}. */ const val PROVIDER_INTERFACE = "android.content.action.SPA_SEARCH_PROVIDER" /** ContentProvider path for search static data */ const val SEARCH_STATIC_DATA = "search_static_data" /** ContentProvider path for search dynamic data */ const val SEARCH_DYNAMIC_DATA = "search_dynamic_data" /** ContentProvider path for search immutable status */ const val SEARCH_IMMUTABLE_STATUS = "search_immutable_status" /** ContentProvider path for search mutable status */ const val SEARCH_MUTABLE_STATUS = "search_mutable_status" /** Enum to define all column names in provider. */ enum class ColumnEnum(val id: String) { ENTRY_ID("entryId"), SEARCH_TITLE("searchTitle"), SEARCH_KEYWORD("searchKw"), SEARCH_PATH("searchPath"), INTENT_TARGET_PACKAGE("intentTargetPackage"), INTENT_TARGET_CLASS("intentTargetClass"), INTENT_EXTRAS("intentExtras"), SLICE_URI("sliceUri"), LEGACY_KEY("legacyKey"), ENTRY_DISABLED("entryDisabled"), } /** Enum to define all queries supported in the provider. */ @SuppressWarnings("Immutable") enum class QueryEnum( val queryPath: String, val columnNames: List<ColumnEnum> ) { SEARCH_STATIC_DATA_QUERY( SEARCH_STATIC_DATA, listOf( ColumnEnum.ENTRY_ID, ColumnEnum.SEARCH_TITLE, ColumnEnum.SEARCH_KEYWORD, ColumnEnum.SEARCH_PATH, ColumnEnum.INTENT_TARGET_PACKAGE, ColumnEnum.INTENT_TARGET_CLASS, ColumnEnum.INTENT_EXTRAS, ColumnEnum.SLICE_URI, ColumnEnum.LEGACY_KEY ) ), SEARCH_DYNAMIC_DATA_QUERY( SEARCH_DYNAMIC_DATA, listOf( ColumnEnum.ENTRY_ID, ColumnEnum.SEARCH_TITLE, ColumnEnum.SEARCH_KEYWORD, ColumnEnum.SEARCH_PATH, ColumnEnum.INTENT_TARGET_PACKAGE, ColumnEnum.INTENT_TARGET_CLASS, ColumnEnum.SLICE_URI, ColumnEnum.LEGACY_KEY ) ), SEARCH_IMMUTABLE_STATUS_DATA_QUERY( SEARCH_IMMUTABLE_STATUS, listOf( ColumnEnum.ENTRY_ID, ColumnEnum.ENTRY_DISABLED, ) ), SEARCH_MUTABLE_STATUS_DATA_QUERY( SEARCH_MUTABLE_STATUS, listOf( ColumnEnum.ENTRY_ID, ColumnEnum.ENTRY_DISABLED, ) ), }
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/search/SpaSearchProvider.kt +52 −24 Original line number Diff line number Diff line Loading @@ -19,22 +19,22 @@ package com.android.settingslib.spa.search import android.content.ContentProvider import android.content.ContentValues import android.content.Context import android.content.Intent import android.content.UriMatcher import android.content.pm.ProviderInfo import android.database.Cursor import android.database.MatrixCursor import android.net.Uri import android.os.Parcel import android.os.Parcelable import android.util.Log import androidx.annotation.VisibleForTesting import com.android.settingslib.spa.framework.common.ColumnEnum import com.android.settingslib.spa.framework.common.QueryEnum import com.android.settingslib.spa.framework.common.EntryStatusData import com.android.settingslib.spa.framework.common.SettingsEntry import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory import com.android.settingslib.spa.framework.common.addUri import com.android.settingslib.spa.framework.common.getColumns import com.android.settingslib.spa.framework.util.SESSION_SEARCH import com.android.settingslib.spa.framework.util.createIntent import com.android.settingslib.spa.slice.fromEntry private const val TAG = "SpaSearchProvider" Loading @@ -42,18 +42,25 @@ private const val TAG = "SpaSearchProvider" * The content provider to return entry related data, which can be used for search and hierarchy. * One can query the provider result by: * $ adb shell content query --uri content://<AuthorityPath>/<QueryPath> * For gallery, AuthorityPath = com.android.spa.gallery.provider * For Settings, AuthorityPath = com.android.settings.spa.provider * For gallery, AuthorityPath = com.android.spa.gallery.search.provider * For Settings, AuthorityPath = com.android.settings.spa.search.provider" * Some examples: * $ adb shell content query --uri content://<AuthorityPath>/search_static * $ adb shell content query --uri content://<AuthorityPath>/search_dynamic * $ adb shell content query --uri content://<AuthorityPath>/search_mutable_status * $ adb shell content query --uri content://<AuthorityPath>/search_static_data * $ adb shell content query --uri content://<AuthorityPath>/search_dynamic_data * $ adb shell content query --uri content://<AuthorityPath>/search_immutable_status * $ adb shell content query --uri content://<AuthorityPath>/search_mutable_status */ class SpaSearchProvider : ContentProvider() { private val spaEnvironment get() = SpaEnvironmentFactory.instance private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH) private val queryMatchCode = mapOf( SEARCH_STATIC_DATA to 301, SEARCH_DYNAMIC_DATA to 302, SEARCH_MUTABLE_STATUS to 303, SEARCH_IMMUTABLE_STATUS to 304 ) override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int { TODO("Implement this to handle requests to delete one or more rows") } Loading Loading @@ -85,10 +92,9 @@ class SpaSearchProvider : ContentProvider() { override fun attachInfo(context: Context?, info: ProviderInfo?) { if (info != null) { QueryEnum.SEARCH_STATIC_DATA_QUERY.addUri(uriMatcher, info.authority) QueryEnum.SEARCH_DYNAMIC_DATA_QUERY.addUri(uriMatcher, info.authority) QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY.addUri(uriMatcher, info.authority) QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY.addUri(uriMatcher, info.authority) for (entry in queryMatchCode) { uriMatcher.addURI(info.authority, entry.key, entry.value) } } super.attachInfo(context, info) } Loading @@ -102,11 +108,11 @@ class SpaSearchProvider : ContentProvider() { ): Cursor? { return try { when (uriMatcher.match(uri)) { QueryEnum.SEARCH_STATIC_DATA_QUERY.queryMatchCode -> querySearchStaticData() QueryEnum.SEARCH_DYNAMIC_DATA_QUERY.queryMatchCode -> querySearchDynamicData() QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY.queryMatchCode -> queryMatchCode[SEARCH_STATIC_DATA] -> querySearchStaticData() queryMatchCode[SEARCH_DYNAMIC_DATA] -> querySearchDynamicData() queryMatchCode[SEARCH_MUTABLE_STATUS] -> querySearchMutableStatusData() QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY.queryMatchCode -> queryMatchCode[SEARCH_IMMUTABLE_STATUS] -> querySearchImmutableStatusData() else -> throw UnsupportedOperationException("Unknown Uri $uri") } Loading Loading @@ -167,23 +173,45 @@ class SpaSearchProvider : ContentProvider() { // Fetch search data. We can add runtime arguments later if necessary val searchData = entry.getSearchData() ?: return val intent = entry.createIntent(SESSION_SEARCH) ?: Intent() cursor.newRow() .add(ColumnEnum.ENTRY_ID.id, entry.id) .add(ColumnEnum.ENTRY_INTENT_URI.id, intent.toUri(Intent.URI_INTENT_SCHEME)) val intent = entry.createIntent(SESSION_SEARCH) val row = cursor.newRow().add(ColumnEnum.ENTRY_ID.id, entry.id) .add(ColumnEnum.SEARCH_TITLE.id, searchData.title) .add(ColumnEnum.SEARCH_KEYWORD.id, searchData.keyword) .add( ColumnEnum.SEARCH_PATH.id, entryRepository.getEntryPathWithTitle(entry.id, searchData.title) ) intent?.let { row.add(ColumnEnum.INTENT_TARGET_PACKAGE.id, spaEnvironment.appContext.packageName) .add(ColumnEnum.INTENT_TARGET_CLASS.id, spaEnvironment.browseActivityClass?.name) .add(ColumnEnum.INTENT_EXTRAS.id, marshall(intent.extras)) } if (entry.hasSliceSupport) row.add( ColumnEnum.SLICE_URI.id, Uri.Builder() .fromEntry(entry, spaEnvironment.sliceProviderAuthorities) ) // TODO: support legacy key } private fun fetchStatusData(entry: SettingsEntry, cursor: MatrixCursor) { // Fetch status data. We can add runtime arguments later if necessary val statusData = entry.getStatusData() ?: return val statusData = entry.getStatusData() ?: EntryStatusData() cursor.newRow() .add(ColumnEnum.ENTRY_ID.id, entry.id) .add(ColumnEnum.SEARCH_STATUS_DISABLED.id, statusData.isDisabled) .add(ColumnEnum.ENTRY_DISABLED.id, statusData.isDisabled) } private fun QueryEnum.getColumns(): Array<String> { return columnNames.map { it.id }.toTypedArray() } private fun marshall(parcelable: Parcelable?): ByteArray? { if (parcelable == null) return null val parcel = Parcel.obtain() parcelable.writeToParcel(parcel, 0) val bytes = parcel.marshall() parcel.recycle() return bytes } }
packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/search/SpaSearchProviderTest.kt +101 −8 Original line number Diff line number Diff line Loading @@ -18,15 +18,14 @@ package com.android.settingslib.spa.search import android.content.Context import android.database.Cursor import android.os.Bundle import android.os.Parcel import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.framework.common.ColumnEnum import com.android.settingslib.spa.framework.common.QueryEnum import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory import com.android.settingslib.spa.framework.common.createSettingsPage import com.android.settingslib.spa.framework.common.getIndex import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest import com.android.settingslib.spa.tests.testutils.SppForSearch import com.google.common.truth.Truth Loading @@ -39,51 +38,145 @@ class SpaSearchProviderTest { private val spaEnvironment = SpaEnvironmentForTest(context, listOf(SppForSearch.createSettingsPage())) private val searchProvider = SpaSearchProvider() private val pageOwner = spaEnvironment.createPage("SppForSearch") @Test fun testQuerySearchStatusData() { SpaEnvironmentFactory.reset(spaEnvironment) val pageOwner = spaEnvironment.createPage("SppForSearch") val immutableStatus = searchProvider.querySearchImmutableStatusData() Truth.assertThat(immutableStatus.count).isEqualTo(1) Truth.assertThat(immutableStatus.count).isEqualTo(2) immutableStatus.moveToFirst() immutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchStaticWithNoStatus") ) immutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_DISABLED, false.toString() ) immutableStatus.moveToNext() immutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchDynamicWithImmutableStatus") ) immutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_DISABLED, true.toString() ) val mutableStatus = searchProvider.querySearchMutableStatusData() Truth.assertThat(mutableStatus.count).isEqualTo(2) mutableStatus.moveToFirst() mutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchStaticWithMutableStatus") ) mutableStatus.checkValue( QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_DISABLED, false.toString() ) mutableStatus.moveToNext() mutableStatus.checkValue( QueryEnum.SEARCH_IMMUTABLE_STATUS_DATA_QUERY, QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchDynamicWithMutableStatus") ) mutableStatus.checkValue( QueryEnum.SEARCH_MUTABLE_STATUS_DATA_QUERY, ColumnEnum.ENTRY_DISABLED, true.toString() ) } @Test fun testQuerySearchIndexData() { SpaEnvironmentFactory.reset(spaEnvironment) val staticData = searchProvider.querySearchStaticData() Truth.assertThat(staticData.count).isEqualTo(2) staticData.moveToFirst() staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchStaticWithNoStatus") ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.SEARCH_TITLE, "SearchStaticWithNoStatus" ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.SEARCH_KEYWORD, listOf("").toString() ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.SEARCH_PATH, listOf("SearchStaticWithNoStatus", "SppForSearch").toString() ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.INTENT_TARGET_PACKAGE, spaEnvironment.appContext.packageName ) staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.INTENT_TARGET_CLASS, "com.android.settingslib.spa.tests.testutils.BlankActivity" ) // Check extras in intent val bundle = staticData.getExtras(QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.INTENT_EXTRAS) Truth.assertThat(bundle).isNotNull() Truth.assertThat(bundle!!.size()).isEqualTo(3) Truth.assertThat(bundle.getString("spaActivityDestination")).isEqualTo("SppForSearch") Truth.assertThat(bundle.getString("highlightEntry")) .isEqualTo(pageOwner.getEntryId("SearchStaticWithNoStatus")) Truth.assertThat(bundle.getString("sessionSource")).isEqualTo("search") staticData.moveToNext() staticData.checkValue( QueryEnum.SEARCH_STATIC_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchStaticWithMutableStatus") ) val dynamicData = searchProvider.querySearchDynamicData() Truth.assertThat(dynamicData.count).isEqualTo(2) dynamicData.moveToFirst() dynamicData.checkValue( QueryEnum.SEARCH_DYNAMIC_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchDynamicWithMutableStatus") ) dynamicData.moveToNext() dynamicData.checkValue( QueryEnum.SEARCH_DYNAMIC_DATA_QUERY, ColumnEnum.ENTRY_ID, pageOwner.getEntryId("SearchDynamicWithImmutableStatus") ) dynamicData.checkValue( QueryEnum.SEARCH_DYNAMIC_DATA_QUERY, ColumnEnum.SEARCH_KEYWORD, listOf("kw1", "kw2").toString() ) } } private fun Cursor.checkValue(query: QueryEnum, column: ColumnEnum, value: String) { Truth.assertThat(getString(query.getIndex(column))).isEqualTo(value) Truth.assertThat(getString(query.columnNames.indexOf(column))).isEqualTo(value) } private fun Cursor.getExtras(query: QueryEnum, column: ColumnEnum): Bundle? { val extrasByte = getBlob(query.columnNames.indexOf(column)) ?: return null val parcel = Parcel.obtain() parcel.unmarshall(extrasByte, 0, extrasByte.size) parcel.setDataPosition(0) val bundle = Bundle() bundle.readFromParcel(parcel) return bundle } private fun SettingsPage.getEntryId(name: String): String { Loading