diff --git a/res/layout/search_bar_unified_version.xml b/res/layout/search_bar_unified_version.xml index 14f46ec78e6..e9b3c107b90 100644 --- a/res/layout/search_bar_unified_version.xml +++ b/res/layout/search_bar_unified_version.xml @@ -44,7 +44,7 @@ style="@style/TextAppearance.SearchBar" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingStart="8dp" + android:layout_marginStart="8dp" android:paddingEnd="8dp" android:text="@string/homepage_search"/> diff --git a/res/layout/settings_homepage_container_v2.xml b/res/layout/settings_homepage_container_v2.xml index b2445790347..a67b743ce13 100644 --- a/res/layout/settings_homepage_container_v2.xml +++ b/res/layout/settings_homepage_container_v2.xml @@ -69,7 +69,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:paddingVertical="8dp" + android:paddingTop="8dp" + android:paddingBottom="16dp" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> diff --git a/res/values/strings.xml b/res/values/strings.xml index ab883ee1c85..0fcb0d68502 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1864,6 +1864,10 @@ Device details Keyboard settings + + More settings + + Firmware updates, about, and more Device\'s Bluetooth address: %1$s @@ -1884,6 +1888,9 @@ Disconnect app + + More settings + Maximum connected Bluetooth audio devices @@ -4493,10 +4500,10 @@ Reverse scrolling Content moves up when you scroll down - - Bottom-right tap - - Tap the bottom right corner of the touchpad for more options + + Bottom-right click + + Click in the bottom right corner of the touchpad for more options Pointer speed @@ -8184,10 +8191,10 @@ other {{effect_1}, {effect_2}, and # more} } - - Limit what can notify you - - No interruptions are filtered + + Allow all notifications + + People, apps, and sounds can interrupt Display options for filtered diff --git a/res/xml/bluetooth_device_more_settings_fragment.xml b/res/xml/bluetooth_device_more_settings_fragment.xml new file mode 100644 index 00000000000..4fb4acae03c --- /dev/null +++ b/res/xml/bluetooth_device_more_settings_fragment.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index 825d72cd3e8..4f16e12f394 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -176,12 +176,11 @@ settings:searchable="false" settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/> + diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml index d2f573c56cf..4f9b685592e 100644 --- a/res/xml/modes_rule_settings.xml +++ b/res/xml/modes_rule_settings.xml @@ -59,8 +59,8 @@ android:key="modes_filters"> + android:key="allow_all" + android:title="@string/zen_mode_allow_all_notifications"/> keys = mFormatter.getVisiblePreferenceKeysForMainPage(); + List keys = + mFormatter.getVisiblePreferenceKeys( + FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE); Lifecycle lifecycle = getSettingsLifecycle(); if (keys == null || keys.contains(controller.getPreferenceKey())) { super.addPreferenceController(controller); diff --git a/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt b/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt index 8fe3c255d34..d29795efee7 100644 --- a/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt +++ b/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt @@ -66,15 +66,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.DialogProperties import com.android.settings.R +import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel import com.android.settings.bluetooth.ui.composable.Icon as DeviceSettingComposeIcon -import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel -import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel import com.android.settingslib.spa.framework.theme.SettingsDimension import com.android.settingslib.spa.widget.dialog.getDialogWidth @Composable fun MultiTogglePreferenceGroup( - preferenceModels: List, + preferenceModels: List, ) { var settingIdForPopUp by remember { mutableStateOf(null) } @@ -115,7 +114,7 @@ fun MultiTogglePreferenceGroup( colors = getButtonColors(preferenceModel.isActive), contentPadding = PaddingValues(0.dp)) { DeviceSettingComposeIcon( - preferenceModel.toggles[preferenceModel.state.selectedIndex] + preferenceModel.toggles[preferenceModel.selectedIndex] .icon, modifier = Modifier.size(24.dp)) } @@ -144,7 +143,7 @@ private fun getButtonColors(isActive: Boolean) = @OptIn(ExperimentalMaterial3Api::class) @Composable private fun dialog( - multiTogglePreference: DeviceSettingModel.MultiTogglePreference, + multiTogglePreference: DeviceSettingPreferenceModel.MultiTogglePreference, onDismiss: () -> Unit ) { BasicAlertDialog( @@ -179,7 +178,7 @@ private fun dialog( } @Composable -private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiTogglePreference) { +private fun dialogContent(multiTogglePreference: DeviceSettingPreferenceModel.MultiTogglePreference) { Column { Row( modifier = Modifier.fillMaxWidth().height(24.dp), @@ -219,7 +218,7 @@ private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiToggleP } Row { for ((idx, toggle) in multiTogglePreference.toggles.withIndex()) { - val selected = idx == multiTogglePreference.state.selectedIndex + val selected = idx == multiTogglePreference.selectedIndex Column( modifier = Modifier.weight(1f) @@ -237,8 +236,7 @@ private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiToggleP ) { Button( onClick = { - multiTogglePreference.updateState( - DeviceSettingStateModel.MultiTogglePreferenceState(idx)) + multiTogglePreference.onSelectedChange(idx) }, modifier = Modifier.fillMaxSize(), colors = diff --git a/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt b/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt new file mode 100644 index 00000000000..6612591fce4 --- /dev/null +++ b/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt @@ -0,0 +1,69 @@ +/* + * 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.bluetooth.ui.model + +import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId +import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon +import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel + +/** Models a device setting preference. */ +sealed interface DeviceSettingPreferenceModel { + @DeviceSettingId + val id: Int + + /** Models a plain preference. */ + data class PlainPreference( + @DeviceSettingId override val id: Int, + val title: String, + val summary: String? = null, + val icon: DeviceSettingIcon? = null, + val onClick: (() -> Unit)? = null, + ) : DeviceSettingPreferenceModel + + /** Models a switch preference. */ + data class SwitchPreference( + @DeviceSettingId override val id: Int, + val title: String, + val summary: String? = null, + val icon: DeviceSettingIcon? = null, + val checked: Boolean, + val onCheckedChange: ((Boolean) -> Unit), + val onPrimaryClick: (() -> Unit)? = null, + ) : DeviceSettingPreferenceModel + + /** Models a multi-toggle preference. */ + data class MultiTogglePreference( + @DeviceSettingId override val id: Int, + val title: String, + val toggles: List, + val isActive: Boolean, + val selectedIndex: Int, + val isAllowedChangingState: Boolean, + val onSelectedChange: (Int) -> Unit, + ) : DeviceSettingPreferenceModel + + /** Models a footer preference. */ + data class FooterPreference( + @DeviceSettingId override val id: Int, + val footerText: String, + ) : DeviceSettingPreferenceModel + + /** Models a preference which could navigate to more settings fragment. */ + data class MoreSettingsPreference( + @DeviceSettingId override val id: Int, + ) : DeviceSettingPreferenceModel +} diff --git a/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt b/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt new file mode 100644 index 00000000000..19858c4ba7d --- /dev/null +++ b/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt @@ -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.bluetooth.ui.model + +/** Models a device details fragment type. */ +sealed interface FragmentTypeModel { + /** Device details main page. */ + data object DeviceDetailsMainFragment : FragmentTypeModel + /** Device details more settings page. */ + data object DeviceDetailsMoreSettingsFragment : FragmentTypeModel +} diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt index b75579dfa0d..c933c754b7e 100644 --- a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt +++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt @@ -19,47 +19,52 @@ package com.android.settings.bluetooth.ui.view import android.bluetooth.BluetoothAdapter import android.content.Context import android.media.AudioManager -import android.util.Log +import android.os.Bundle import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.preference.Preference +import com.android.settings.R import com.android.settings.SettingsPreferenceFragment import com.android.settings.bluetooth.ui.composable.Icon import com.android.settings.bluetooth.ui.composable.MultiTogglePreferenceGroup import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout +import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel +import com.android.settings.bluetooth.ui.model.FragmentTypeModel +import com.android.settings.bluetooth.ui.view.DeviceDetailsMoreSettingsFragment.Companion.KEY_DEVICE_ADDRESS import com.android.settings.bluetooth.ui.viewmodel.BluetoothDeviceDetailsViewModel +import com.android.settings.core.SubSettingLauncher import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.settings.spa.preference.ComposePreference import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel -import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel -import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel +import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.widget.preference.Preference as SpaPreference import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference +import com.android.settingslib.spa.widget.ui.Footer import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import com.android.settingslib.spa.widget.preference.Preference as SpaPreference - /** Handles device details fragment layout according to config. */ interface DeviceDetailsFragmentFormatter { /** Gets keys of visible preferences in built-in preference in xml. */ - fun getVisiblePreferenceKeysForMainPage(): List? + fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List? /** Updates device details fragment layout. */ - fun updateLayout() + fun updateLayout(fragmentType: FragmentTypeModel) } @OptIn(ExperimentalCoroutinesApi::class) @@ -79,23 +84,25 @@ class DeviceDetailsFragmentFormatterImpl( ViewModelProvider( fragment, BluetoothDeviceDetailsViewModel.Factory( + fragment.requireActivity().application, repository, spatialAudioInteractor, cachedDevice, )) .get(BluetoothDeviceDetailsViewModel::class.java) - override fun getVisiblePreferenceKeysForMainPage(): List? = runBlocking { - viewModel - .getItems() - ?.filterIsInstance() - ?.mapNotNull { it.preferenceKey } - } + override fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List? = + runBlocking { + viewModel + .getItems(fragmentType) + ?.filterIsInstance() + ?.mapNotNull { it.preferenceKey } + } /** Updates bluetooth device details fragment layout. */ - override fun updateLayout() = runBlocking { - val items = viewModel.getItems() ?: return@runBlocking - val layout = viewModel.getLayout() ?: return@runBlocking + override fun updateLayout(fragmentType: FragmentTypeModel) = runBlocking { + val items = viewModel.getItems(fragmentType) ?: return@runBlocking + val layout = viewModel.getLayout(fragmentType) ?: return@runBlocking val prefKeyToSettingId = items .filterIsInstance() @@ -124,6 +131,8 @@ class DeviceDetailsFragmentFormatterImpl( fragment.preferenceScreen.addPreference(pref) } } + // TODO(b/343317785): figure out how to remove the foot preference. + fragment.preferenceScreen.addPreference(Preference(context).apply { order = 10000 }) } @Composable @@ -132,7 +141,7 @@ class DeviceDetailsFragmentFormatterImpl( remember(row) { layout.rows[row].settingIds.flatMapLatest { settingIds -> if (settingIds.isEmpty()) { - flowOf(emptyList()) + flowOf(emptyList()) } else { combine( settingIds.map { settingId -> @@ -150,72 +159,104 @@ class DeviceDetailsFragmentFormatterImpl( 0 -> {} 1 -> { when (val setting = settings[0]) { - is DeviceSettingModel.ActionSwitchPreference -> { - buildActionSwitchPreference(setting) + is DeviceSettingPreferenceModel.PlainPreference -> { + buildPlainPreference(setting) } - is DeviceSettingModel.MultiTogglePreference -> { + is DeviceSettingPreferenceModel.SwitchPreference -> { + buildSwitchPreference(setting) + } + is DeviceSettingPreferenceModel.MultiTogglePreference -> { buildMultiTogglePreference(listOf(setting)) } - null -> {} - else -> { - Log.w(TAG, "Unknown preference type ${setting.id}, skip.") + is DeviceSettingPreferenceModel.FooterPreference -> { + buildFooterPreference(setting) } + is DeviceSettingPreferenceModel.MoreSettingsPreference -> { + buildMoreSettingsPreference() + } + null -> {} } } else -> { - if (!settings.all { it is DeviceSettingModel.MultiTogglePreference }) { + if (!settings.all { it is DeviceSettingPreferenceModel.MultiTogglePreference }) { return } buildMultiTogglePreference( - settings.filterIsInstance()) + settings.filterIsInstance()) } } } @Composable - private fun buildMultiTogglePreference(prefs: List) { + private fun buildMultiTogglePreference( + prefs: List + ) { MultiTogglePreferenceGroup(prefs) } @Composable - private fun buildActionSwitchPreference(model: DeviceSettingModel.ActionSwitchPreference) { - if (model.switchState != null) { - val switchPrefModel = - object : SwitchPreferenceModel { - override val title = model.title - override val summary = { model.summary ?: "" } - override val checked = { model.switchState?.checked } - override val onCheckedChange = { newChecked: Boolean -> - model.updateState?.invoke( - DeviceSettingStateModel.ActionSwitchPreferenceState(newChecked)) - Unit - } - override val icon = @Composable { deviceSettingIcon(model) } + private fun buildSwitchPreference(model: DeviceSettingPreferenceModel.SwitchPreference) { + val switchPrefModel = + object : SwitchPreferenceModel { + override val title = model.title + override val summary = { model.summary ?: "" } + override val checked = { model.checked } + override val onCheckedChange = { newChecked: Boolean -> + model.onCheckedChange(newChecked) } - if (model.intent != null) { - TwoTargetSwitchPreference(switchPrefModel) { context.startActivity(model.intent) } - } else { - SwitchPreference(switchPrefModel) + override val icon = @Composable { deviceSettingIcon(model.icon) } } + if (model.onPrimaryClick != null) { + TwoTargetSwitchPreference( + switchPrefModel, primaryOnClick = model.onPrimaryClick::invoke) } else { - SpaPreference( - object : PreferenceModel { - override val title = model.title - override val summary = { model.summary ?: "" } - override val onClick = { - model.intent?.let { context.startActivity(it) } - Unit - } - override val icon = @Composable { deviceSettingIcon(model) } - }) + SwitchPreference(switchPrefModel) } } @Composable - private fun deviceSettingIcon(model: DeviceSettingModel.ActionSwitchPreference) { - model.icon?.let { icon -> - Icon(icon, modifier = Modifier.size(SettingsDimension.itemIconSize)) - } + private fun buildPlainPreference(model: DeviceSettingPreferenceModel.PlainPreference) { + SpaPreference( + object : PreferenceModel { + override val title = model.title + override val summary = { model.summary ?: "" } + override val onClick = { + model.onClick?.invoke() + Unit + } + override val icon = @Composable { deviceSettingIcon(model.icon) } + }) + } + + @Composable + fun buildMoreSettingsPreference() { + SpaPreference( + object : PreferenceModel { + override val title = + stringResource(R.string.bluetooth_device_more_settings_preference_title) + override val summary = { + context.getString(R.string.bluetooth_device_more_settings_preference_summary) + } + override val onClick = { + SubSettingLauncher(context) + .setDestination(DeviceDetailsMoreSettingsFragment::class.java.name) + .setSourceMetricsCategory(fragment.getMetricsCategory()) + .setArguments( + Bundle().apply { putString(KEY_DEVICE_ADDRESS, cachedDevice.address) }) + .launch() + } + override val icon = @Composable { deviceSettingIcon(null) } + }) + } + + @Composable + fun buildFooterPreference(model: DeviceSettingPreferenceModel.FooterPreference) { + Footer(footerText = model.footerText) + } + + @Composable + private fun deviceSettingIcon(icon: DeviceSettingIcon?) { + icon?.let { Icon(it, modifier = Modifier.size(SettingsDimension.itemIconSize)) } } private fun getPreferenceKey(settingId: Int) = "DEVICE_SETTING_${settingId}" diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt new file mode 100644 index 00000000000..c648a3e9b43 --- /dev/null +++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt @@ -0,0 +1,92 @@ +/* + * 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.bluetooth.ui.view + +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.content.Context +import android.os.Bundle +import com.android.settings.R +import com.android.settings.bluetooth.BluetoothDetailsProfilesController +import com.android.settings.bluetooth.Utils +import com.android.settings.bluetooth.ui.model.FragmentTypeModel +import com.android.settings.dashboard.DashboardFragment +import com.android.settings.overlay.FeatureFactory.Companion.featureFactory +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.settingslib.core.AbstractPreferenceController +import com.android.settingslib.core.lifecycle.LifecycleObserver + +class DeviceDetailsMoreSettingsFragment : DashboardFragment() { + private lateinit var formatter: DeviceDetailsFragmentFormatter + private lateinit var localBluetoothManager: LocalBluetoothManager + private lateinit var cachedDevice: CachedBluetoothDevice + + // TODO(b/343317785): add metrics category + override fun getMetricsCategory(): Int = 0 + + override fun getPreferenceScreenResId(): Int { + return R.xml.bluetooth_device_more_settings_fragment + } + + override fun addPreferenceController(controller: AbstractPreferenceController) { + val keys: List? = + formatter.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMoreSettingsFragment) + val lifecycle = settingsLifecycle + if (keys == null || keys.contains(controller.preferenceKey)) { + super.addPreferenceController(controller) + } else if (controller is LifecycleObserver) { + lifecycle.removeObserver((controller as LifecycleObserver)) + } + } + + private fun getCachedDevice(): CachedBluetoothDevice? { + val bluetoothAddress = arguments?.getString(KEY_DEVICE_ADDRESS) ?: return null + localBluetoothManager = Utils.getLocalBtManager(context) ?: return null + val remoteDevice: BluetoothDevice = + localBluetoothManager.bluetoothAdapter.getRemoteDevice(bluetoothAddress) ?: return null + return Utils.getLocalBtManager(context).cachedDeviceManager.findDevice(remoteDevice) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + super.onCreatePreferences(savedInstanceState, rootKey) + formatter.updateLayout(FragmentTypeModel.DeviceDetailsMoreSettingsFragment) + } + + override fun createPreferenceControllers(context: Context): List { + val bluetoothManager = context.getSystemService(BluetoothManager::class.java) + cachedDevice = + getCachedDevice() + ?: run { + finish() + return emptyList() + } + formatter = + featureFactory.bluetoothFeatureProvider.getDeviceDetailsFragmentFormatter( + requireContext(), this, bluetoothManager.adapter, cachedDevice) + return listOf( + BluetoothDetailsProfilesController( + context, this, localBluetoothManager, cachedDevice, settingsLifecycle)) + } + + override fun getLogTag(): String = TAG + + companion object { + const val TAG: String = "DeviceMoreSettingsFrg" + const val KEY_DEVICE_ADDRESS: String = "device_address" + } +} diff --git a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt index befff830da3..c85015cc71b 100644 --- a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt +++ b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt @@ -16,17 +16,22 @@ package com.android.settings.bluetooth.ui.viewmodel +import android.app.Application +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow +import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel +import com.android.settings.bluetooth.ui.model.FragmentTypeModel import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel +import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -38,30 +43,81 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn class BluetoothDeviceDetailsViewModel( + private val application: Application, private val deviceSettingRepository: DeviceSettingRepository, private val spatialAudioInteractor: SpatialAudioInteractor, private val cachedDevice: CachedBluetoothDevice, -) : ViewModel() { +) : AndroidViewModel(application){ + private val items = viewModelScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) { deviceSettingRepository.getDeviceSettingsConfig(cachedDevice) } - suspend fun getItems(): List? = items.await()?.mainItems + suspend fun getItems(fragment: FragmentTypeModel): List? = + when (fragment) { + is FragmentTypeModel.DeviceDetailsMainFragment -> items.await()?.mainItems + is FragmentTypeModel.DeviceDetailsMoreSettingsFragment -> + items.await()?.moreSettingsItems + } fun getDeviceSetting( cachedDevice: CachedBluetoothDevice, @DeviceSettingId settingId: Int - ): Flow { + ): Flow { + if (settingId == DeviceSettingId.DEVICE_SETTING_ID_MORE_SETTINGS) { + return flowOf(DeviceSettingPreferenceModel.MoreSettingsPreference(settingId)) + } return when (settingId) { DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE -> spatialAudioInteractor.getDeviceSetting(cachedDevice) else -> deviceSettingRepository.getDeviceSetting(cachedDevice, settingId) + }.map { it?.toPreferenceModel() } + } + + private fun DeviceSettingModel.toPreferenceModel(): DeviceSettingPreferenceModel? { + return when (this) { + is DeviceSettingModel.ActionSwitchPreference -> { + if (switchState != null) { + DeviceSettingPreferenceModel.SwitchPreference( + id = id, + title = title, + summary = summary, + icon = icon, + checked = switchState?.checked ?: false, + onCheckedChange = { newState -> + updateState?.invoke( + DeviceSettingStateModel.ActionSwitchPreferenceState(newState)) + }, + onPrimaryClick = { intent?.let { application.startActivity(it) } }) + } else { + DeviceSettingPreferenceModel.PlainPreference( + id = id, + title = title, + summary = summary, + icon = icon, + onClick = { intent?.let { application.startActivity(it) } }) + } + } + is DeviceSettingModel.FooterPreference -> + DeviceSettingPreferenceModel.FooterPreference(id = id, footerText = footerText) + is DeviceSettingModel.MultiTogglePreference -> + DeviceSettingPreferenceModel.MultiTogglePreference( + id = id, + title = title, + toggles = toggles, + isActive = isActive, + selectedIndex = state.selectedIndex, + isAllowedChangingState = isAllowedChangingState, + onSelectedChange = { newState -> + updateState(DeviceSettingStateModel.MultiTogglePreferenceState(newState)) + }) + is DeviceSettingModel.Unknown -> null } } - suspend fun getLayout(): DeviceSettingLayout? { - val configItems = getItems() ?: return null + suspend fun getLayout(fragment: FragmentTypeModel): DeviceSettingLayout? { + val configItems = getItems(fragment) ?: return null val idToDeviceSetting = configItems .filterIsInstance() @@ -80,7 +136,7 @@ class BluetoothDeviceDetailsViewModel( if (!isXmlPreference && setting == null) { continue } - if (setting !is DeviceSettingModel.MultiTogglePreference) { + if (setting !is DeviceSettingPreferenceModel.MultiTogglePreference) { multiToggleSettingIds = null positionMapping[i] = listOf(configItem.settingId) continue @@ -103,6 +159,7 @@ class BluetoothDeviceDetailsViewModel( } class Factory( + private val application: Application, private val deviceSettingRepository: DeviceSettingRepository, private val spatialAudioInteractor: SpatialAudioInteractor, private val cachedDevice: CachedBluetoothDevice, @@ -110,7 +167,7 @@ class BluetoothDeviceDetailsViewModel( override fun create(modelClass: Class): T { @Suppress("UNCHECKED_CAST") return BluetoothDeviceDetailsViewModel( - deviceSettingRepository, spatialAudioInteractor, cachedDevice) + application, deviceSettingRepository, spatialAudioInteractor, cachedDevice) as T } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 8396e48afd1..a8021326c41 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -43,6 +43,7 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; +import com.android.settings.R; import com.android.settings.bluetooth.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; @@ -128,6 +129,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + ", broadcastId = " + broadcastId); updateSwitch(); + AudioSharingUtils.toastMessage( + mContext, mContext.getString(R.string.audio_sharing_sharing_label)); mListener.onAudioSharingStateChanged(); } @@ -161,6 +164,9 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + ", broadcastId = " + broadcastId); updateSwitch(); + AudioSharingUtils.toastMessage( + mContext, + mContext.getString(R.string.audio_sharing_sharing_stopped_label)); mListener.onAudioSharingStateChanged(); } diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 7bbb06a2d30..e922f7058be 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -278,16 +278,17 @@ public class AdvancedPowerUsageDetail extends DashboardFragment super.onPause(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); + final Context applicationContext = requireContext().getApplicationContext(); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); logMetricCategory(currentOptimizeMode); mExecutor.execute( () -> { if (currentOptimizeMode != mOptimizationMode) { AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid( - getContext(), mBatteryOptimizeUtils.getUid()); + applicationContext, mBatteryOptimizeUtils.getUid()); } BatteryOptimizeLogUtils.writeLog( - getContext().getApplicationContext(), + applicationContext, Action.LEAVE, BatteryOptimizeLogUtils.getPackageNameWithUserId( mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java index 2d2c838bc36..e59cc4add46 100644 --- a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java @@ -117,17 +117,17 @@ public class PowerBackgroundUsageDetail extends DashboardFragment super.onPause(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); + final Context applicationContext = requireContext().getApplicationContext(); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); logMetricCategory(currentOptimizeMode); - mExecutor.execute( () -> { if (currentOptimizeMode != mOptimizationMode) { AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid( - getContext(), mBatteryOptimizeUtils.getUid()); + applicationContext, mBatteryOptimizeUtils.getUid()); } BatteryOptimizeLogUtils.writeLog( - getContext().getApplicationContext(), + applicationContext, Action.LEAVE, BatteryOptimizeLogUtils.getPackageNameWithUserId( mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), diff --git a/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java b/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java index 44c45190c54..a43fda04626 100644 --- a/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java +++ b/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java @@ -56,7 +56,7 @@ public class ImsQueryProvisioningStat implements ImsQuery { final ProvisioningManager privisionManager = ProvisioningManager.createForSubscriptionId(mSubId); return privisionManager.getProvisioningStatusForCapability(mCapability, mTech); - } catch (IllegalArgumentException exception) { + } catch (UnsupportedOperationException exception) { Log.w(LOG_TAG, "fail to get Provisioning stat. subId=" + mSubId, exception); } return false; diff --git a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt index 3ec529dbe82..3f5c06efb93 100644 --- a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt +++ b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt @@ -199,7 +199,7 @@ class CarrierConfigRepository(private val context: Context) { } @VisibleForTesting - fun setStringForTest(subId: Int, key: String, value: String) { + fun setStringForTest(subId: Int, key: String, value: String?) { check(key.endsWith("_string")) { "String key should ends with _string" } getPerSubCache(subId)[key] = StringConfigValue(value) } diff --git a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java deleted file mode 100644 index 575d19ce35b..00000000000 --- a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2019 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.network.telephony; - -import android.content.Context; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; -import android.text.TextUtils; - -import com.android.settings.core.BasePreferenceController; -import com.android.settings.network.CarrierConfigCache; - -public class CarrierSettingsVersionPreferenceController extends BasePreferenceController { - - private int mSubscriptionId; - private CarrierConfigCache mCarrierConfigCache; - - public CarrierSettingsVersionPreferenceController(Context context, String preferenceKey) { - super(context, preferenceKey); - mCarrierConfigCache = CarrierConfigCache.getInstance(context); - mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - } - - public void init(int subscriptionId) { - mSubscriptionId = subscriptionId; - } - - @Override - public CharSequence getSummary() { - final PersistableBundle config = mCarrierConfigCache.getConfigForSubId(mSubscriptionId); - if (config == null) { - return null; - } - return config.getString(CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING); - } - - @Override - public int getAvailabilityStatus() { - return TextUtils.isEmpty(getSummary()) ? UNSUPPORTED_ON_DEVICE : AVAILABLE; - } -} diff --git a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt new file mode 100644 index 00000000000..f949ab8a25d --- /dev/null +++ b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt @@ -0,0 +1,62 @@ +/* + * 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.network.telephony + +import android.content.Context +import android.telephony.CarrierConfigManager +import android.telephony.SubscriptionManager +import com.android.settings.R +import com.android.settings.core.BasePreferenceController +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult + +class CarrierSettingsVersionPreferenceController(context: Context, preferenceKey: String) : + BasePreferenceController(context, preferenceKey) { + + private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID + private val searchItem = CarrierSettingsVersionSearchItem(context) + + fun init(subId: Int) { + this.subId = subId + } + + override fun getSummary() = searchItem.getSummary(subId) + + override fun getAvailabilityStatus() = + if (searchItem.isAvailable(subId)) AVAILABLE else CONDITIONALLY_UNAVAILABLE + + companion object { + class CarrierSettingsVersionSearchItem(private val context: Context) : + MobileNetworkSettingsSearchItem { + private val carrierConfigRepository = CarrierConfigRepository(context) + + fun getSummary(subId: Int): String? = + carrierConfigRepository.getString( + subId, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING) + + fun isAvailable(subId: Int): Boolean = !getSummary(subId).isNullOrEmpty() + + override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { + if (!isAvailable(subId)) return null + return MobileNetworkSettingsSearchResult( + key = "carrier_settings_version_key", + title = context.getString(R.string.carrier_settings_version), + ) + } + } + } +} diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt index 55cb1fad8f3..c63e7d23bf1 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt @@ -21,6 +21,7 @@ import android.provider.Settings import android.telephony.SubscriptionInfo import com.android.settings.R import com.android.settings.network.SubscriptionUtil +import com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController.Companion.CarrierSettingsVersionSearchItem import com.android.settings.network.telephony.DataUsagePreferenceController.Companion.DataUsageSearchItem import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem @@ -115,6 +116,7 @@ class MobileNetworkSettingsSearchIndex( fun createSearchItems(context: Context): List = listOf( + CarrierSettingsVersionSearchItem(context), DataUsageSearchItem(context), MmsMessageSearchItem(context), NrAdvancedCallingSearchItem(context), diff --git a/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt new file mode 100644 index 00000000000..ba332574819 --- /dev/null +++ b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt @@ -0,0 +1,61 @@ +/* + * 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.network.telephony.ims + +import android.content.Context +import android.telephony.AccessNetworkConstants.TransportType +import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability +import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech +import com.android.settings.network.telephony.subscriptionsChangedFlow +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest + +/** + * A repository for the IMS feature. + * + * @throws IllegalArgumentException if the [subId] is invalid. + */ +@OptIn(ExperimentalCoroutinesApi::class) +class ImsFeatureRepository( + private val context: Context, + private val subId: Int, + private val provisioningRepository: ProvisioningRepository = ProvisioningRepository(context), + private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId) +) { + /** + * A cold flow that determines the provisioning status for the specified IMS MmTel capability, + * and whether or not the requested MmTel capability is supported by the carrier on the + * specified network transport. + * + * @return true if the feature is provisioned and supported, false otherwise. + */ + fun isReadyFlow( + @MmTelCapability capability: Int, + @ImsRegistrationTech tech: Int, + @TransportType transportType: Int, + ): Flow = + context.subscriptionsChangedFlow().flatMapLatest { + combine( + provisioningRepository.imsFeatureProvisionedFlow(subId, capability, tech), + imsMmTelRepository.isSupportedFlow(capability, transportType), + ) { imsFeatureProvisioned, isSupported -> + imsFeatureProvisioned && isSupported + } + } +} diff --git a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt index 9bc10e555e1..c5d1200a1d4 100644 --- a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt +++ b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt @@ -36,6 +36,7 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext @@ -47,6 +48,11 @@ interface ImsMmTelRepository { fun imsReadyFlow(): Flow + fun isSupportedFlow( + @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, + @AccessNetworkConstants.TransportType transportType: Int, + ): Flow + suspend fun isSupported( @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @AccessNetworkConstants.TransportType transportType: Int, @@ -55,6 +61,11 @@ interface ImsMmTelRepository { suspend fun setCrossSimCallingEnabled(enabled: Boolean) } +/** + * A repository for the IMS MMTel. + * + * @throws IllegalArgumentException if the [subId] is invalid. + */ class ImsMmTelRepositoryImpl( context: Context, private val subId: Int, @@ -126,8 +137,12 @@ class ImsMmTelRepositoryImpl( awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) } }.catch { e -> Log.w(TAG, "[$subId] error while imsReadyFlow", e) + emit(false) }.conflate().flowOn(Dispatchers.Default) + override fun isSupportedFlow(capability: Int, transportType: Int): Flow = + imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) } + override suspend fun isSupported( @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @AccessNetworkConstants.TransportType transportType: Int, diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt index 04e687c2d7f..6af0559adb1 100644 --- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt +++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt @@ -20,24 +20,17 @@ import android.content.Context import android.telephony.AccessNetworkConstants import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL -import android.telephony.SubscriptionManager import android.telephony.ims.ImsMmTelManager.WiFiCallingMode import android.telephony.ims.feature.MmTelFeature import android.telephony.ims.stub.ImsRegistrationImplBase import androidx.lifecycle.LifecycleOwner +import com.android.settings.network.telephony.ims.ImsFeatureRepository import com.android.settings.network.telephony.ims.ImsMmTelRepository import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl -import com.android.settings.network.telephony.ims.ProvisioningRepository -import com.android.settings.network.telephony.subscriptionsChangedFlow import com.android.settings.network.telephony.telephonyManager import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext interface IWifiCallingRepository { @@ -50,11 +43,11 @@ class WifiCallingRepository constructor( private val context: Context, private val subId: Int, - private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId) + private val imsFeatureRepository: ImsFeatureRepository = ImsFeatureRepository(context, subId), + private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId), ) : IWifiCallingRepository { private val telephonyManager = context.telephonyManager(subId) - private val provisioningRepository = ProvisioningRepository(context) private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!! @WiFiCallingMode @@ -76,28 +69,12 @@ constructor( wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action) } - @OptIn(ExperimentalCoroutinesApi::class) - fun wifiCallingReadyFlow(): Flow { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) - return context.subscriptionsChangedFlow().flatMapLatest { - combine( - provisioningRepository.imsFeatureProvisionedFlow( - subId = subId, - capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, - tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, - ), - isWifiCallingSupportedFlow(), - ) { imsFeatureProvisioned, isWifiCallingSupported -> - imsFeatureProvisioned && isWifiCallingSupported - } - } - } - - private fun isWifiCallingSupportedFlow(): Flow { - return imsMmTelRepository.imsReadyFlow().map { imsReady -> - imsReady && isWifiCallingSupported() - } - } + fun wifiCallingReadyFlow(): Flow = + imsFeatureRepository.isReadyFlow( + capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, + tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, + transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + ) suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) { imsMmTelRepository.isSupported( diff --git a/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java index 9d4a1725bcc..d69b3173fef 100644 --- a/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java +++ b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java @@ -45,20 +45,21 @@ class InterruptionFilterPreferenceController extends AbstractZenModePreferenceCo @Override public void updateState(Preference preference, @NonNull ZenMode zenMode) { preference.setEnabled(zenMode.isEnabled()); - boolean filteringNotifications = zenMode.getRule().getInterruptionFilter() - != INTERRUPTION_FILTER_ALL; - ((TwoStatePreference) preference).setChecked(filteringNotifications); - preference.setSummary(filteringNotifications ? "" : - mContext.getResources().getString(R.string.mode_no_notification_filter)); + boolean allowingAll = zenMode.getRule().getInterruptionFilter() == INTERRUPTION_FILTER_ALL; + + ((TwoStatePreference) preference).setChecked(allowingAll); + preference.setSummary(allowingAll + ? mContext.getString(R.string.zen_mode_all_notifications_allowed) + : ""); } @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final boolean filterNotifications = ((Boolean) newValue); + public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { + final boolean allowAll = ((Boolean) newValue); return saveMode(zenMode -> { - zenMode.getRule().setInterruptionFilter(filterNotifications - ? INTERRUPTION_FILTER_PRIORITY - : INTERRUPTION_FILTER_ALL); + zenMode.getRule().setInterruptionFilter(allowAll + ? INTERRUPTION_FILTER_ALL + : INTERRUPTION_FILTER_PRIORITY); return zenMode; }); } diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java index 37772990152..6889cac2e60 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeFragment.java @@ -77,7 +77,7 @@ public class ZenModeFragment extends ZenModeFragmentBase { new ZenModeTriggerAddPreferenceController(context, "zen_add_automatic_trigger", this, mBackend)); prefControllers.add(new InterruptionFilterPreferenceController( - context, "allow_filtering", mBackend)); + context, "allow_all", mBackend)); prefControllers.add(new ManualDurationPreferenceController( context, "mode_manual_duration", this, mBackend)); return prefControllers; @@ -110,9 +110,10 @@ public class ZenModeFragment extends ZenModeFragmentBase { if (mode == null || mode.getStatus() != DISABLED_BY_OTHER) { return false; } + + mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode)); // don't come back here from the interstitial finish(); - mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode)); return true; } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java index 19d0eddd3a4..c84d42c1618 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java @@ -50,6 +50,7 @@ import androidx.fragment.app.FragmentTransaction; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.bluetooth.ui.model.FragmentTypeModel; import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -117,7 +118,9 @@ public class BluetoothDeviceDetailsFragmentTest { FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); when(fakeFeatureFactory.mBluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(any(), any(), any(), eq(mCachedDevice))).thenReturn(mFormatter); - when(mFormatter.getVisiblePreferenceKeysForMainPage()).thenReturn(null); + when(mFormatter.getVisiblePreferenceKeys( + FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE)) + .thenReturn(null); mFragment = setupFragment(); mFragment.onAttach(mContext); diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt index 609d7679f16..251b814f972 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt +++ b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt @@ -26,6 +26,7 @@ import androidx.preference.PreferenceManager import androidx.preference.PreferenceScreen import androidx.test.core.app.ApplicationProvider import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor +import com.android.settings.bluetooth.ui.model.FragmentTypeModel import com.android.settings.dashboard.DashboardFragment import com.android.settings.testutils.FakeFeatureFactory import com.android.settingslib.bluetooth.CachedBluetoothDevice @@ -45,7 +46,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.any @@ -111,10 +111,9 @@ class DeviceDetailsFragmentFormatterTest { DeviceSettingConfigItemModel.BuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons"), ), - listOf(), - "footer")) + listOf())) - val keys = underTest.getVisiblePreferenceKeysForMainPage() + val keys = underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment) assertThat(keys).containsExactly("bluetooth_device_header", "action_buttons") } @@ -125,7 +124,7 @@ class DeviceDetailsFragmentFormatterTest { testScope.runTest { `when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null) - val keys = underTest.getVisiblePreferenceKeysForMainPage() + val keys = underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment) assertThat(keys).isNull() } @@ -136,9 +135,9 @@ class DeviceDetailsFragmentFormatterTest { testScope.runTest { `when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null) - underTest.updateLayout() + underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment) - assertThat(getDisplayedPreferences().map { it.key }) + assertThat(getDisplayedPreferences().mapNotNull { it.key }) .containsExactly("bluetooth_device_header", "action_buttons", "keyboard_settings") } } @@ -157,12 +156,11 @@ class DeviceDetailsFragmentFormatterTest { DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS, "keyboard_settings"), ), - listOf(), - "footer")) + listOf())) - underTest.updateLayout() + underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment) - assertThat(getDisplayedPreferences().map { it.key }) + assertThat(getDisplayedPreferences().mapNotNull { it.key }) .containsExactly("bluetooth_device_header", "keyboard_settings") } } @@ -183,8 +181,7 @@ class DeviceDetailsFragmentFormatterTest { DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS, "keyboard_settings"), ), - listOf(), - "footer")) + listOf())) `when`(repository.getDeviceSetting(cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_ANC)) .thenReturn( flowOf( @@ -209,9 +206,9 @@ class DeviceDetailsFragmentFormatterTest { isAllowedChangingState = true, updateState = {}))) - underTest.updateLayout() + underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment) - assertThat(getDisplayedPreferences().map { it.key }) + assertThat(getDisplayedPreferences().mapNotNull { it.key }) .containsExactly( "bluetooth_device_header", "DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}", diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt index a1fadb8b354..378f363f0dd 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt +++ b/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt @@ -16,12 +16,14 @@ package com.android.settings.bluetooth.ui.viewmodel +import android.app.Application import android.bluetooth.BluetoothAdapter -import android.content.Context import android.graphics.Bitmap import androidx.test.core.app.ApplicationProvider import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout +import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel +import com.android.settings.bluetooth.ui.model.FragmentTypeModel import com.android.settings.testutils.FakeFeatureFactory import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId @@ -44,8 +46,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -73,26 +73,23 @@ class BluetoothDeviceDetailsViewModelTest { @Before fun setUp() { - val context = ApplicationProvider.getApplicationContext() + val application = ApplicationProvider.getApplicationContext() featureFactory = FakeFeatureFactory.setupForTest() - `when`( - featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository( - eq(context), eq(bluetoothAdapter), any())) - .thenReturn(repository) underTest = - BluetoothDeviceDetailsViewModel(repository, spatialAudioInteractor, cachedDevice) + BluetoothDeviceDetailsViewModel( + application, repository, spatialAudioInteractor, cachedDevice) } @Test - fun getItems_returnConfigMainItems() { + fun getItems_returnConfigMainMainItems() { testScope.runTest { `when`(repository.getDeviceSettingsConfig(cachedDevice)) .thenReturn( DeviceSettingConfigModel( - listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), "footer")) + listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf())) - val keys = underTest.getItems() + val keys = underTest.getItems(FragmentTypeModel.DeviceDetailsMainFragment) assertThat(keys).containsExactly(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2) } @@ -110,19 +107,18 @@ class BluetoothDeviceDetailsViewModelTest { BUILTIN_SETTING_ITEM_1, buildRemoteSettingItem(remoteSettingId1), ), - listOf(), - "footer")) + listOf())) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1)) .thenReturn(flowOf(pref)) - var deviceSetting: DeviceSettingModel? = null + var deviceSettingPreference: DeviceSettingPreferenceModel? = null underTest .getDeviceSetting(cachedDevice, remoteSettingId1) - .onEach { deviceSetting = it } + .onEach { deviceSettingPreference = it } .launchIn(testScope.backgroundScope) runCurrent() - assertThat(deviceSetting).isSameInstanceAs(pref) + assertThat(deviceSettingPreference?.id).isEqualTo(pref.id) verify(repository, times(1)).getDeviceSetting(cachedDevice, remoteSettingId1) } } @@ -141,19 +137,18 @@ class BluetoothDeviceDetailsViewModelTest { buildRemoteSettingItem( DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE), ), - listOf(), - "footer")) + listOf())) `when`(spatialAudioInteractor.getDeviceSetting(cachedDevice)).thenReturn(flowOf(pref)) - var deviceSetting: DeviceSettingModel? = null + var deviceSettingPreference: DeviceSettingPreferenceModel? = null underTest .getDeviceSetting( cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE) - .onEach { deviceSetting = it } + .onEach { deviceSettingPreference = it } .launchIn(testScope.backgroundScope) runCurrent() - assertThat(deviceSetting).isSameInstanceAs(pref) + assertThat(deviceSettingPreference?.id).isEqualTo(pref.id) verify(spatialAudioInteractor, times(1)).getDeviceSetting(cachedDevice) } } @@ -164,9 +159,9 @@ class BluetoothDeviceDetailsViewModelTest { `when`(repository.getDeviceSettingsConfig(cachedDevice)) .thenReturn( DeviceSettingConfigModel( - listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), "footer")) + listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf())) - val layout = underTest.getLayout()!! + val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!! assertThat(getLatestLayout(layout)) .isEqualTo( @@ -191,8 +186,7 @@ class BluetoothDeviceDetailsViewModelTest { buildRemoteSettingItem(remoteSettingId2), buildRemoteSettingItem(remoteSettingId3), ), - listOf(), - "footer")) + listOf())) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1)) .thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId1))) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId2)) @@ -200,7 +194,7 @@ class BluetoothDeviceDetailsViewModelTest { `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId3)) .thenReturn(flowOf(buildActionSwitchPreference(remoteSettingId3))) - val layout = underTest.getLayout()!! + val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!! assertThat(getLatestLayout(layout)) .isEqualTo( diff --git a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java index 0c3f8e1815d..777d213142f 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java @@ -87,7 +87,7 @@ public final class InterruptionFilterPreferenceControllerTest { .build(); mController.updateZenMode(preference, zenMode); - verify(preference).setChecked(false); + verify(preference).setChecked(true); } @Test @@ -99,7 +99,7 @@ public final class InterruptionFilterPreferenceControllerTest { mController.updateZenMode(preference, zenMode); - mController.onPreferenceChange(preference, true); + mController.onPreferenceChange(preference, false); ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); @@ -118,7 +118,7 @@ public final class InterruptionFilterPreferenceControllerTest { .build(); mController.updateZenMode(preference, zenMode); - verify(preference).setChecked(true); + verify(preference).setChecked(false); } @Test @@ -131,7 +131,7 @@ public final class InterruptionFilterPreferenceControllerTest { mController.updateZenMode(preference, zenMode); - mController.onPreferenceChange(preference, false); + mController.onPreferenceChange(preference, true); ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ManualDurationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ManualDurationPreferenceControllerTest.java index 29fdfdd6bd3..4edb0d510e1 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ManualDurationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ManualDurationPreferenceControllerTest.java @@ -18,11 +18,9 @@ package com.android.settings.notification.modes; import static com.google.common.truth.Truth.assertThat; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.ContentResolver; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; @@ -31,7 +29,6 @@ import androidx.fragment.app.Fragment; import androidx.preference.Preference; import com.android.settingslib.notification.modes.TestModeBuilder; -import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -73,10 +70,8 @@ public class ManualDurationPreferenceControllerTest { @Test public void testIsAvailable_onlyForManualDnd() { assertThat(mPrefController.isAvailable(TestModeBuilder.EXAMPLE)).isFalse(); - - ZenMode manualDnd = ZenMode.manualDndMode( - new AutomaticZenRule.Builder("id", Uri.EMPTY).build(), false); - assertThat(mPrefController.isAvailable(manualDnd)).isTrue(); + assertThat(mPrefController.isAvailable(TestModeBuilder.MANUAL_DND_ACTIVE)).isTrue(); + assertThat(mPrefController.isAvailable(TestModeBuilder.MANUAL_DND_INACTIVE)).isTrue(); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java index 47078b08697..159dadae273 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java @@ -23,11 +23,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.ContentResolver; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; @@ -192,8 +190,7 @@ public final class ZenModeButtonPreferenceControllerTest { Button button = new Button(mContext); LayoutPreference pref = mock(LayoutPreference.class); when(pref.findViewById(anyInt())).thenReturn(button); - ZenMode zenMode = ZenMode.manualDndMode( - new AutomaticZenRule.Builder("manual", Uri.EMPTY).build(), false); + ZenMode zenMode = TestModeBuilder.MANUAL_DND_INACTIVE; mController.updateZenMode(pref, zenMode); button.callOnClick(); @@ -207,8 +204,7 @@ public final class ZenModeButtonPreferenceControllerTest { Button button = new Button(mContext); LayoutPreference pref = mock(LayoutPreference.class); when(pref.findViewById(anyInt())).thenReturn(button); - ZenMode zenMode = ZenMode.manualDndMode( - new AutomaticZenRule.Builder("manual", Uri.EMPTY).build(), false); + ZenMode zenMode = TestModeBuilder.MANUAL_DND_INACTIVE; mController.updateZenMode(pref, zenMode); button.callOnClick(); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java index a56e7230bde..0d20b190844 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java @@ -19,7 +19,6 @@ package com.android.settings.notification.modes; import static android.app.AutomaticZenRule.TYPE_OTHER; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.android.settings.notification.modes.CharSequenceTruth.assertThat; @@ -28,7 +27,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.net.Uri; @@ -125,12 +123,7 @@ public class ZenModeTriggerAddPreferenceControllerTest { @Test public void isAvailable_manualDND_false() { - ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", - Uri.parse("manual")) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(), /* isActive= */ false); - - mController.setZenMode(manualMode); + mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE); assertThat(mController.isAvailable()).isFalse(); } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java index 4510e2048f5..bcafe47f9c7 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java @@ -18,15 +18,12 @@ package com.android.settings.notification.modes; import static android.app.AutomaticZenRule.TYPE_OTHER; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.google.common.truth.Truth.assertThat; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.SystemZenRules; @@ -116,12 +113,7 @@ public class ZenModeTriggerCategoryPreferenceControllerTest { @Test public void isAvailable_manualDND_false() { - ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", - Uri.parse("manual")) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(), /* isActive= */ false); - - mController.setZenMode(manualMode); + mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE); assertThat(mController.isAvailable()).isFalse(); } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java index 80d314c3dc0..b7af71b50fd 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java @@ -19,7 +19,6 @@ package com.android.settings.notification.modes; import static android.app.AutomaticZenRule.TYPE_OTHER; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.android.settings.notification.modes.CharSequenceTruth.assertThat; @@ -35,13 +34,11 @@ import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.app.AlertDialog; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Looper; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -149,12 +146,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { @Test public void isAvailable_manualDND_false() { - ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", - Uri.parse("manual")) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(), /* isActive= */ false); - - mController.setZenMode(manualMode); + mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE); assertThat(mController.isAvailable()).isFalse(); } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java index 4c16f2616bf..4fa8b8ac6c4 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java @@ -18,6 +18,8 @@ package com.android.settings.notification.modes; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; +import static com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND_INACTIVE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; @@ -71,13 +73,6 @@ public class ZenModesListPreferenceControllerTest { .build()) .build(); - private static final ZenMode TEST_MANUAL_MODE = ZenMode.manualDndMode( - new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), - false); - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); @@ -152,7 +147,7 @@ public class ZenModesListPreferenceControllerTest { @DisableFlags(Flags.FLAG_MODES_UI) public void testModesUiOff_notAvailableAndNoSearchData() { // There exist modes - when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE, TEST_MODE)); + when(mBackend.getModes()).thenReturn(List.of(MANUAL_DND_INACTIVE, TEST_MODE)); assertThat(mPrefController.isAvailable()).isFalse(); List data = new ArrayList<>(); @@ -187,20 +182,20 @@ public class ZenModesListPreferenceControllerTest { // Changing mode data so there's a different one mode doesn't keep any previous data // (and setting that state up in the caller) - when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE)); + when(mBackend.getModes()).thenReturn(List.of(MANUAL_DND_INACTIVE)); List newData = new ArrayList<>(); mPrefController.updateDynamicRawDataToIndex(newData); assertThat(newData).hasSize(1); SearchIndexableRaw newItem = newData.get(0); - assertThat(newItem.key).isEqualTo(TEST_MANUAL_MODE.getId()); + assertThat(newItem.key).isEqualTo(MANUAL_DND_INACTIVE.getId()); assertThat(newItem.title).isEqualTo("Do Not Disturb"); // set above } @Test @EnableFlags(Flags.FLAG_MODES_UI) public void testUpdateDynamicRawDataToIndex_multipleModes() { - when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE, TEST_MODE)); + when(mBackend.getModes()).thenReturn(List.of(MANUAL_DND_INACTIVE, TEST_MODE)); List data = new ArrayList<>(); mPrefController.updateDynamicRawDataToIndex(data); @@ -208,7 +203,7 @@ public class ZenModesListPreferenceControllerTest { // Should keep the order presented by getModes() SearchIndexableRaw item0 = data.get(0); - assertThat(item0.key).isEqualTo(TEST_MANUAL_MODE.getId()); + assertThat(item0.key).isEqualTo(MANUAL_DND_INACTIVE.getId()); assertThat(item0.title).isEqualTo("Do Not Disturb"); // set above SearchIndexableRaw item1 = data.get(1); diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt new file mode 100644 index 00000000000..ed6c02711e3 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt @@ -0,0 +1,70 @@ +/* + * 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.network.telephony + +import android.content.Context +import android.telephony.CarrierConfigManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class CarrierSettingsVersionPreferenceControllerTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + + private val controller = + CarrierSettingsVersionPreferenceController(context, TEST_KEY).apply { init(SUB_ID) } + + @Before + fun setUp() { + CarrierConfigRepository.resetForTest() + } + + @Test + fun getSummary_nullConfig_noCrash() { + controller.getSummary() + } + + @Test + fun getSummary_nullVersionString_returnNull() { + CarrierConfigRepository.setStringForTest( + SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING, null) + + val summary = controller.getSummary() + + assertThat(summary).isNull() + } + + @Test + fun getSummary_hasVersionString_returnCorrectSummary() { + CarrierConfigRepository.setStringForTest( + SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING, "test_version_123") + + val summary = controller.getSummary() + + assertThat(summary).isEqualTo("test_version_123") + } + + private companion object { + const val TEST_KEY = "test_key" + const val SUB_ID = 10 + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt new file mode 100644 index 00000000000..3f72b2c0dc4 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt @@ -0,0 +1,108 @@ +/* + * 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.network.telephony.ims + +import android.content.Context +import android.telephony.AccessNetworkConstants +import android.telephony.ims.feature.MmTelFeature +import android.telephony.ims.stub.ImsRegistrationImplBase +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub + +@RunWith(AndroidJUnit4::class) +class ImsFeatureRepositoryTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + + private val mockProvisioningRepository = mock() + private val mockImsMmTelRepository = mock() + + @Test + fun isReadyFlow_notProvisioned_returnFalse() = runBlocking { + mockProvisioningRepository.stub { + onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn + flowOf(false) + } + + val repository = + ImsFeatureRepository( + context = context, + subId = SUB_ID, + provisioningRepository = mockProvisioningRepository, + ) + + val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first() + + assertThat(isReady).isFalse() + } + + @Test + fun isReadyFlow_notSupported_returnFalse() = runBlocking { + mockImsMmTelRepository.stub { + onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(false) + } + + val repository = + ImsFeatureRepository( + context = context, + subId = SUB_ID, + imsMmTelRepository = mockImsMmTelRepository, + ) + + val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first() + + assertThat(isReady).isFalse() + } + + @Test + fun isReadyFlow_provisionedAndSupported_returnFalse() = runBlocking { + mockProvisioningRepository.stub { + onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn flowOf(true) + } + mockImsMmTelRepository.stub { + onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(true) + } + + val repository = + ImsFeatureRepository( + context = context, + subId = SUB_ID, + provisioningRepository = mockProvisioningRepository, + imsMmTelRepository = mockImsMmTelRepository, + ) + + val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first() + + assertThat(isReady).isTrue() + } + + private companion object { + const val SUB_ID = 10 + const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE + const val TECH = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN + const val TRANSPORT_TYPE = AccessNetworkConstants.TRANSPORT_TYPE_WLAN + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt index 0144f6669cb..f0a23eb92c9 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt @@ -55,7 +55,8 @@ class WifiCallingRepositoryTest { on { getWiFiCallingMode(any()) } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN } - private val repository = WifiCallingRepository(context, SUB_ID, mockImsMmTelRepository) + private val repository = + WifiCallingRepository(context, SUB_ID, imsMmTelRepository = mockImsMmTelRepository) @Test fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() { diff --git a/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java deleted file mode 100644 index 40be07f1717..00000000000 --- a/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2020 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.network.telephony; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; - -import android.content.Context; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settings.network.CarrierConfigCache; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -public class CarrierSettingsVersionPreferenceControllerTest { - @Mock - private CarrierConfigCache mCarrierConfigCache; - - private CarrierSettingsVersionPreferenceController mController; - private int mSubscriptionId = 1234; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - Context context = spy(ApplicationProvider.getApplicationContext()); - CarrierConfigCache.setTestInstance(context, mCarrierConfigCache); - mController = new CarrierSettingsVersionPreferenceController(context, "mock_key"); - mController.init(mSubscriptionId); - } - - @Test - public void getSummary_nullConfig_noCrash() { - doReturn(null).when(mCarrierConfigCache).getConfigForSubId(mSubscriptionId); - - assertThat(mController.getSummary()).isNull(); - } - - @Test - public void getSummary_nullVersionString_noCrash() { - doReturn(new PersistableBundle()).when(mCarrierConfigCache) - .getConfigForSubId(mSubscriptionId); - assertThat(mController.getSummary()).isNull(); - } - - @Test - public void getSummary_hasVersionString_correctSummary() { - final PersistableBundle bundle = new PersistableBundle(); - bundle.putString(CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING, - "test_version_123"); - doReturn(bundle).when(mCarrierConfigCache).getConfigForSubId(mSubscriptionId); - - assertThat(mController.getSummary()).isEqualTo("test_version_123"); - } -}