Merge "Support search "Mobile data"" into main

This commit is contained in:
Chaohui Wang
2024-07-12 05:39:09 +00:00
committed by Android (Google) Code Review
8 changed files with 247 additions and 4 deletions

View File

@@ -5187,6 +5187,15 @@
<activity android:name=".spa.SpaBridgeActivity" android:exported="false"/> <activity android:name=".spa.SpaBridgeActivity" android:exported="false"/>
<activity android:name=".spa.SpaAppBridgeActivity" android:exported="false"/> <activity android:name=".spa.SpaAppBridgeActivity" android:exported="false"/>
<activity
android:name=".spa.search.SpaSearchLandingActivity"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.settings.SPA_SEARCH_LANDING" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".Settings$FingerprintSettingsActivityV2" <activity android:name=".Settings$FingerprintSettingsActivityV2"
android:label="@string/security_settings_fingerprint_preference_title" android:label="@string/security_settings_fingerprint_preference_title"
android:exported="false" android:exported="false"

View File

@@ -22,11 +22,18 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.provider.Settings import android.provider.Settings
import com.android.settings.search.SearchIndexableResourcesFactory.createSearchIndexableResources import com.android.settings.search.SearchIndexableResourcesFactory.createSearchIndexableResources
import com.android.settings.spa.search.SpaSearchRepository
import com.android.settingslib.search.SearchIndexableResources import com.android.settingslib.search.SearchIndexableResources
/** FeatureProvider for the refactored search code. */ /** FeatureProvider for the refactored search code. */
open class SearchFeatureProviderImpl : SearchFeatureProvider { open class SearchFeatureProviderImpl : SearchFeatureProvider {
private val lazySearchIndexableResources by lazy { createSearchIndexableResources() } private val lazySearchIndexableResources by lazy {
createSearchIndexableResources().apply {
for (searchIndexableData in SpaSearchRepository().getSearchIndexableDataList()) {
addIndex(searchIndexableData)
}
}
}
override fun verifyLaunchSearchResultPageCaller(context: Context, callerPackage: String) { override fun verifyLaunchSearchResultPageCaller(context: Context, callerPackage: String) {
require(callerPackage.isNotEmpty()) { require(callerPackage.isNotEmpty()) {

View File

@@ -46,10 +46,14 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R import com.android.settings.R
import com.android.settings.flags.Flags
import com.android.settings.network.SubscriptionInfoListViewModel import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.SubscriptionUtil
import com.android.settings.network.telephony.DataSubscriptionRepository import com.android.settings.network.telephony.DataSubscriptionRepository
import com.android.settings.network.telephony.MobileDataRepository import com.android.settings.network.telephony.MobileDataRepository
import com.android.settings.network.telephony.requireSubscriptionManager
import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo
import com.android.settings.spa.search.SearchablePage
import com.android.settings.wifi.WifiPickerTrackerHelper import com.android.settings.wifi.WifiPickerTrackerHelper
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.common.SettingsPageProvider
@@ -62,6 +66,7 @@ import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -78,7 +83,7 @@ import kotlinx.coroutines.withContext
/** /**
* Showing the sim onboarding which is the process flow of sim switching on. * Showing the sim onboarding which is the process flow of sim switching on.
*/ */
open class NetworkCellularGroupProvider : SettingsPageProvider { open class NetworkCellularGroupProvider : SettingsPageProvider, SearchablePage {
override val name = fileName override val name = fileName
override val metricsCategory = SettingsEnums.MOBILE_NETWORK_LIST override val metricsCategory = SettingsEnums.MOBILE_NETWORK_LIST
private val owner = createSettingsPage() private val owner = createSettingsPage()
@@ -191,8 +196,24 @@ open class NetworkCellularGroupProvider : SettingsPageProvider {
open fun OtherSection(){ open fun OtherSection(){
// Do nothing // Do nothing
} }
override fun getSearchableTitles(context: Context): List<String> {
if (!isPageSearchable(context)) return emptyList()
return buildList {
if (context.requireSubscriptionManager().activeSubscriptionInfoCount > 0) {
add(context.getString(R.string.mobile_data_settings_title))
}
}
}
companion object { companion object {
const val fileName = "NetworkCellularGroupProvider" const val fileName = "NetworkCellularGroupProvider"
private fun isPageSearchable(context: Context) =
Flags.isDualSimOnboardingEnabled() &&
SubscriptionUtil.isSimHardwareVisible(context) &&
!com.android.settingslib.Utils.isWifiOnly(context) &&
context.userManager.isAdminUser
} }
} }

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 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.settings.spa.search
import android.content.Context
interface SearchablePage {
/** Gets the searchable titles at the current moment. */
fun getSearchableTitles(context: Context): List<String>
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2024 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.settings.spa.search
import android.app.Activity
import android.os.Bundle
import com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settings.password.PasswordUtils
import com.android.settings.spa.SpaDestination
class SpaSearchLandingActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!isValidCall()) return
val destination = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY)
if (destination.isNullOrBlank()) return
SpaDestination(destination = destination, highlightMenuKey = null)
.startFromExportedActivity(this)
finish()
}
private fun isValidCall() =
PasswordUtils.getCallingAppPackageName(activityToken) ==
featureFactory.searchFeatureProvider.getSettingsIntelligencePkgName(this)
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2024 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.settings.spa.search
import android.content.Context
import android.provider.SearchIndexableResource
import android.util.Log
import androidx.annotation.VisibleForTesting
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
class SpaSearchRepository(
private val spaEnvironment: SpaEnvironment = SpaEnvironmentFactory.instance,
) {
fun getSearchIndexableDataList(): List<SearchIndexableData> {
Log.d(TAG, "getSearchIndexableDataList")
return spaEnvironment.pageProviderRepository.value.getAllProviders().mapNotNull { page ->
if (page is SearchablePage) {
page.createSearchIndexableData(page::getSearchableTitles)
} else null
}
}
companion object {
private const val TAG = "SpaSearchRepository"
@VisibleForTesting
fun SettingsPageProvider.createSearchIndexableData(
titlesProvider: (context: Context) -> List<String>,
): SearchIndexableData {
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> =
titlesProvider(context).map { title ->
createSearchIndexableRaw(context, title)
}
override fun getNonIndexableKeys(context: Context): List<String> = emptyList()
}
return SearchIndexableData(this::class.java, searchIndexProvider)
}
private fun SettingsPageProvider.createSearchIndexableRaw(context: Context, title: String) =
SearchIndexableRaw(context).apply {
key = name
this.title = title
intentAction = SEARCH_LANDING_ACTION
packageName = context.packageName
className = SpaSearchLandingActivity::class.qualifiedName
}
private const val SEARCH_LANDING_ACTION = "android.settings.SPA_SEARCH_LANDING"
}
}

View File

@@ -29,6 +29,7 @@ import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import com.android.settings.network.NetworkProviderSettings; import com.android.settings.network.NetworkProviderSettings;
import com.android.settings.spa.search.SearchablePage;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.FakeIndexProvider; import com.android.settings.testutils.FakeIndexProvider;
import com.android.settingslib.search.SearchIndexableData; import com.android.settingslib.search.SearchIndexableData;
@@ -117,8 +118,10 @@ public class SearchIndexableResourcesTest {
public void testAllClassNamesHaveProviders() { public void testAllClassNamesHaveProviders() {
for (SearchIndexableData data : for (SearchIndexableData data :
mSearchProvider.getSearchIndexableResources().getProviderValues()) { mSearchProvider.getSearchIndexableResources().getProviderValues()) {
if (DatabaseIndexingUtils.getSearchIndexProvider(data.getTargetClass()) == null) { Class<?> targetClass = data.getTargetClass();
fail(data.getTargetClass().getName() + "is not an index provider"); if (DatabaseIndexingUtils.getSearchIndexProvider(targetClass) == null
&& !SearchablePage.class.isAssignableFrom(targetClass)) {
fail(targetClass.getName() + " is not an index provider");
} }
} }
} }

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2024 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.settings.spa.search
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.spa.search.SpaSearchRepository.Companion.createSearchIndexableData
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class SpaSearchRepositoryTest {
@Test
fun createSearchIndexableData() {
val pageProvider =
object : SettingsPageProvider {
override val name = PAGE_NAME
}
val searchIndexableData = pageProvider.createSearchIndexableData { listOf(TITLE) }
val dynamicRawDataToIndex =
searchIndexableData.searchIndexProvider.getDynamicRawDataToIndex(mock<Context>(), true)
assertThat(searchIndexableData.targetClass).isEqualTo(pageProvider::class.java)
assertThat(dynamicRawDataToIndex).hasSize(1)
assertThat(dynamicRawDataToIndex[0].title).isEqualTo(TITLE)
}
private companion object {
const val PAGE_NAME = "PageName"
const val TITLE = "Title"
}
}