Snap for 12253386 from 8cd5a449dd to 24Q4-release

Change-Id: I5b47fd5716b18fbd495d8155c37d15274e3e1036
This commit is contained in:
Android Build Coastguard Worker
2024-08-20 23:21:33 +00:00
40 changed files with 829 additions and 377 deletions

View File

@@ -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"/>
</LinearLayout>

View File

@@ -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">

View File

@@ -1864,6 +1864,10 @@
<string name="device_details_title">Device details</string>
<!-- Title for keyboard settings preferences. [CHAR LIMIT=50] -->
<string name="bluetooth_device_keyboard_settings_preference_title">Keyboard settings</string>
<!-- Title for more settings preferences. [CHAR LIMIT=50] -->
<string name="bluetooth_device_more_settings_preference_title">More settings</string>
<!-- Title for more settings summary. [CHAR LIMIT=50] -->
<string name="bluetooth_device_more_settings_preference_summary">Firmware updates, about, and more</string>
<!-- Title of the item to show device MAC address -->
<string name="bluetooth_device_mac_address">Device\'s Bluetooth address: <xliff:g id="address">%1$s</xliff:g></string>
<!-- Title of the items to show multuple devices MAC address [CHAR LIMIT=NONE]-->
@@ -1884,6 +1888,9 @@
<!-- Bluetooth device details companion apps. In the confirmation dialog for removing an associated app, this is the label on the button that will complete the disassociate action. [CHAR LIMIT=80] -->
<string name = "bluetooth_companion_app_remove_association_confirm_button">Disconnect app</string>
<!-- Title of device details screen [CHAR LIMIT=28]-->
<string name="device_details_more_settings">More settings</string>
<!-- Bluetooth developer settings: Maximum number of connected audio devices -->
<string name="bluetooth_max_connected_audio_devices_string">Maximum connected Bluetooth audio devices</string>
<!-- Bluetooth developer settings: Maximum number of connected audio devices -->
@@ -4493,10 +4500,10 @@
<string name="trackpad_reverse_scrolling_title">Reverse scrolling</string>
<!-- Summary text for 'Reverse scrolling' [CHAR LIMIT=60] -->
<string name="trackpad_reverse_scrolling_summary">Content moves up when you scroll down</string>
<!-- Title text for 'Bottom-right tap' [CHAR LIMIT=35] -->
<string name="trackpad_bottom_right_tap_title">Bottom-right tap</string>
<!-- Summary text for 'Bottom-right tap' [CHAR LIMIT=60] -->
<string name="trackpad_bottom_right_tap_summary">Tap the bottom right corner of the touchpad for more options</string>
<!-- Title text for 'Bottom-right click', a setting that allows the user to right-click by pressing in the bottom-right corner of a touchpad. [CHAR LIMIT=35] -->
<string name="trackpad_bottom_right_tap_title">Bottom-right click</string>
<!-- Summary text for 'Bottom-right click', a setting that allows the user to right-click by pressing in the bottom-right corner of a touchpad [CHAR LIMIT=60] -->
<string name="trackpad_bottom_right_tap_summary">Click in the bottom right corner of the touchpad for more options</string>
<!-- Title text for 'Pointer speed'. [CHAR LIMIT=35] -->
<string name="trackpad_pointer_speed">Pointer speed</string>
<!-- Title text for mouse pointer fill style. [CHAR LIMIT=35] -->
@@ -8184,10 +8191,10 @@
other {{effect_1}, {effect_2}, and # more}
}
</string>
<!-- Modes: setting for whether the mode should filter (silence/hide) notifications/volume streams -->
<string name="mode_notification_filter_title">Limit what can notify you</string>
<!-- Modes: subtext when a mode is not filtering (silence/hide) notifications/volume streams -->
<string name="mode_no_notification_filter">No interruptions are filtered</string>
<!-- Modes: setting for a mode to allow all notifications and sounds through -->
<string name="zen_mode_allow_all_notifications">Allow all notifications</string>
<!-- Modes: subtext when a mode is allowing all notifications and sounds (i.e. no filtering) -->
<string name="zen_mode_all_notifications_allowed">People, apps, and sounds can interrupt</string>
<!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] -->
<string name="zen_mode_restrict_notifications_title">Display options for filtered

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="bluetooth_device_more_settings_screen"
android:title="@string/device_details_more_settings">
<PreferenceCategory
android:key="bluetooth_profiles"/>
</PreferenceScreen>

View File

@@ -176,12 +176,11 @@
settings:searchable="false"
settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/>
<!-- Settings search is handled by CarrierSettingsVersionSearchItem. -->
<Preference
android:key="carrier_settings_version_key"
android:title="@string/carrier_settings_version"
android:enabled="false"
android:shouldDisableView="false"
android:selectable="false"
settings:searchable="false"
settings:controller="com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController"
settings:enableCopying="true"/>

View File

@@ -59,8 +59,8 @@
android:key="modes_filters">
<SwitchPreferenceCompat
android:key="allow_filtering"
android:title="@string/mode_notification_filter_title"/>
android:key="allow_all"
android:title="@string/zen_mode_allow_all_notifications"/>
<com.android.settings.notification.modes.CircularIconsPreference
android:key="zen_mode_people"

View File

@@ -48,6 +48,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
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.connecteddevice.stylus.StylusDevicesController;
import com.android.settings.core.SettingsUIDeviceConfig;
@@ -255,8 +256,17 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
public void onDetach() {
super.onDetach();
mManager.getEventManager().unregisterCallback(mBluetoothCallback);
mBluetoothAdapter.removeOnMetadataChangedListener(
mCachedDevice.getDevice(), mExtraControlMetadataListener);
BluetoothDevice device = mCachedDevice.getDevice();
try {
mBluetoothAdapter.removeOnMetadataChangedListener(
device, mExtraControlMetadataListener);
} catch (IllegalArgumentException e) {
Log.w(
TAG,
"Unable to unregister metadata change callback for "
+ mCachedDevice,
e);
}
}
private void updateExtraControlUri(int viewWidth) {
@@ -343,7 +353,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
public void onCreatePreferences(@NonNull Bundle savedInstanceState, @NonNull String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
if (Flags.enableBluetoothDeviceDetailsPolish()) {
mFormatter.updateLayout();
mFormatter.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE);
}
}
@@ -400,7 +410,9 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
@Override
protected void addPreferenceController(AbstractPreferenceController controller) {
if (Flags.enableBluetoothDeviceDetailsPolish()) {
List<String> keys = mFormatter.getVisiblePreferenceKeysForMainPage();
List<String> keys =
mFormatter.getVisiblePreferenceKeys(
FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE);
Lifecycle lifecycle = getSettingsLifecycle();
if (keys == null || keys.contains(controller.getPreferenceKey())) {
super.addPreferenceController(controller);

View File

@@ -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<DeviceSettingModel.MultiTogglePreference>,
preferenceModels: List<DeviceSettingPreferenceModel.MultiTogglePreference>,
) {
var settingIdForPopUp by remember { mutableStateOf<Int?>(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 =

View File

@@ -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<ToggleModel>,
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
}

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.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
}

View File

@@ -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<String>?
fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List<String>?
/** 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<String>? = runBlocking {
viewModel
.getItems()
?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()
?.mapNotNull { it.preferenceKey }
}
override fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List<String>? =
runBlocking {
viewModel
.getItems(fragmentType)
?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()
?.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<DeviceSettingConfigItemModel.BuiltinItem>()
@@ -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<DeviceSettingModel>())
flowOf(emptyList<DeviceSettingPreferenceModel>())
} 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<DeviceSettingModel.MultiTogglePreference>())
settings.filterIsInstance<DeviceSettingPreferenceModel.MultiTogglePreference>())
}
}
}
@Composable
private fun buildMultiTogglePreference(prefs: List<DeviceSettingModel.MultiTogglePreference>) {
private fun buildMultiTogglePreference(
prefs: List<DeviceSettingPreferenceModel.MultiTogglePreference>
) {
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}"

View File

@@ -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<String>? =
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<AbstractPreferenceController> {
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"
}
}

View File

@@ -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<DeviceSettingConfigItemModel>? = items.await()?.mainItems
suspend fun getItems(fragment: FragmentTypeModel): List<DeviceSettingConfigItemModel>? =
when (fragment) {
is FragmentTypeModel.DeviceDetailsMainFragment -> items.await()?.mainItems
is FragmentTypeModel.DeviceDetailsMoreSettingsFragment ->
items.await()?.moreSettingsItems
}
fun getDeviceSetting(
cachedDevice: CachedBluetoothDevice,
@DeviceSettingId settingId: Int
): Flow<DeviceSettingModel?> {
): Flow<DeviceSettingPreferenceModel?> {
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<DeviceSettingConfigItemModel.AppProvidedItem>()
@@ -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 <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return BluetoothDeviceDetailsViewModel(
deviceSettingRepository, spatialAudioInteractor, cachedDevice)
application, deviceSettingRepository, spatialAudioInteractor, cachedDevice)
as T
}
}

View File

@@ -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();
}

View File

@@ -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()),

View File

@@ -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()),

View File

@@ -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;

View File

@@ -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)
}

View File

@@ -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;
}
}

View File

@@ -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),
)
}
}
}
}

View File

@@ -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<MobileNetworkSettingsSearchItem> =
listOf(
CarrierSettingsVersionSearchItem(context),
DataUsageSearchItem(context),
MmsMessageSearchItem(context),
NrAdvancedCallingSearchItem(context),

View File

@@ -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<Boolean> =
context.subscriptionsChangedFlow().flatMapLatest {
combine(
provisioningRepository.imsFeatureProvisionedFlow(subId, capability, tech),
imsMmTelRepository.isSupportedFlow(capability, transportType),
) { imsFeatureProvisioned, isSupported ->
imsFeatureProvisioned && isSupported
}
}
}

View File

@@ -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<Boolean>
fun isSupportedFlow(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
): Flow<Boolean>
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<Boolean> =
imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) }
override suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,

View File

@@ -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<Boolean> {
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<Boolean> {
return imsMmTelRepository.imsReadyFlow().map { imsReady ->
imsReady && isWifiCallingSupported()
}
}
fun wifiCallingReadyFlow(): Flow<Boolean> =
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(

View File

@@ -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;
});
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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}",

View File

@@ -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<Context>()
val application = ApplicationProvider.getApplicationContext<Application>()
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(

View File

@@ -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<ZenMode> 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<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());

View File

@@ -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

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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<SearchIndexableRaw> 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<SearchIndexableRaw> 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<SearchIndexableRaw> 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);

View File

@@ -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
}
}

View File

@@ -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<ProvisioningRepository>()
private val mockImsMmTelRepository = mock<ImsMmTelRepository>()
@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
}
}

View File

@@ -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() {

View File

@@ -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");
}
}