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" style="@style/TextAppearance.SearchBar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="8dp" android:layout_marginStart="8dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:text="@string/homepage_search"/> android:text="@string/homepage_search"/>
</LinearLayout> </LinearLayout>

View File

@@ -69,7 +69,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingVertical="8dp" android:paddingTop="8dp"
android:paddingBottom="16dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">

View File

@@ -1864,6 +1864,10 @@
<string name="device_details_title">Device details</string> <string name="device_details_title">Device details</string>
<!-- Title for keyboard settings preferences. [CHAR LIMIT=50] --> <!-- Title for keyboard settings preferences. [CHAR LIMIT=50] -->
<string name="bluetooth_device_keyboard_settings_preference_title">Keyboard settings</string> <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 --> <!-- 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> <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]--> <!-- 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] --> <!-- 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> <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 --> <!-- Bluetooth developer settings: Maximum number of connected audio devices -->
<string name="bluetooth_max_connected_audio_devices_string">Maximum connected Bluetooth audio devices</string> <string name="bluetooth_max_connected_audio_devices_string">Maximum connected Bluetooth audio devices</string>
<!-- Bluetooth developer settings: Maximum number of connected audio devices --> <!-- Bluetooth developer settings: Maximum number of connected audio devices -->
@@ -4493,10 +4500,10 @@
<string name="trackpad_reverse_scrolling_title">Reverse scrolling</string> <string name="trackpad_reverse_scrolling_title">Reverse scrolling</string>
<!-- Summary text for 'Reverse scrolling' [CHAR LIMIT=60] --> <!-- Summary text for 'Reverse scrolling' [CHAR LIMIT=60] -->
<string name="trackpad_reverse_scrolling_summary">Content moves up when you scroll down</string> <string name="trackpad_reverse_scrolling_summary">Content moves up when you scroll down</string>
<!-- Title text for 'Bottom-right tap' [CHAR LIMIT=35] --> <!-- 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 tap</string> <string name="trackpad_bottom_right_tap_title">Bottom-right click</string>
<!-- Summary text for 'Bottom-right tap' [CHAR LIMIT=60] --> <!-- 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">Tap the bottom right corner of the touchpad for more options</string> <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] --> <!-- Title text for 'Pointer speed'. [CHAR LIMIT=35] -->
<string name="trackpad_pointer_speed">Pointer speed</string> <string name="trackpad_pointer_speed">Pointer speed</string>
<!-- Title text for mouse pointer fill style. [CHAR LIMIT=35] --> <!-- Title text for mouse pointer fill style. [CHAR LIMIT=35] -->
@@ -8184,10 +8191,10 @@
other {{effect_1}, {effect_2}, and # more} other {{effect_1}, {effect_2}, and # more}
} }
</string> </string>
<!-- Modes: setting for whether the mode should filter (silence/hide) notifications/volume streams --> <!-- Modes: setting for a mode to allow all notifications and sounds through -->
<string name="mode_notification_filter_title">Limit what can notify you</string> <string name="zen_mode_allow_all_notifications">Allow all notifications</string>
<!-- Modes: subtext when a mode is not filtering (silence/hide) notifications/volume streams --> <!-- Modes: subtext when a mode is allowing all notifications and sounds (i.e. no filtering) -->
<string name="mode_no_notification_filter">No interruptions are filtered</string> <string name="zen_mode_all_notifications_allowed">People, apps, and sounds can interrupt</string>
<!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] --> <!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] -->
<string name="zen_mode_restrict_notifications_title">Display options for filtered <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:searchable="false"
settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/> settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/>
<!-- Settings search is handled by CarrierSettingsVersionSearchItem. -->
<Preference <Preference
android:key="carrier_settings_version_key" android:key="carrier_settings_version_key"
android:title="@string/carrier_settings_version" android:title="@string/carrier_settings_version"
android:enabled="false" settings:searchable="false"
android:shouldDisableView="false"
android:selectable="false"
settings:controller="com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController" settings:controller="com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController"
settings:enableCopying="true"/> settings:enableCopying="true"/>

View File

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

View File

@@ -48,6 +48,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.R; 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.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
import com.android.settings.connecteddevice.stylus.StylusDevicesController; import com.android.settings.connecteddevice.stylus.StylusDevicesController;
import com.android.settings.core.SettingsUIDeviceConfig; import com.android.settings.core.SettingsUIDeviceConfig;
@@ -255,8 +256,17 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
public void onDetach() { public void onDetach() {
super.onDetach(); super.onDetach();
mManager.getEventManager().unregisterCallback(mBluetoothCallback); mManager.getEventManager().unregisterCallback(mBluetoothCallback);
mBluetoothAdapter.removeOnMetadataChangedListener( BluetoothDevice device = mCachedDevice.getDevice();
mCachedDevice.getDevice(), mExtraControlMetadataListener); 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) { private void updateExtraControlUri(int viewWidth) {
@@ -343,7 +353,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
public void onCreatePreferences(@NonNull Bundle savedInstanceState, @NonNull String rootKey) { public void onCreatePreferences(@NonNull Bundle savedInstanceState, @NonNull String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey); super.onCreatePreferences(savedInstanceState, rootKey);
if (Flags.enableBluetoothDeviceDetailsPolish()) { if (Flags.enableBluetoothDeviceDetailsPolish()) {
mFormatter.updateLayout(); mFormatter.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE);
} }
} }
@@ -400,7 +410,9 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
@Override @Override
protected void addPreferenceController(AbstractPreferenceController controller) { protected void addPreferenceController(AbstractPreferenceController controller) {
if (Flags.enableBluetoothDeviceDetailsPolish()) { if (Flags.enableBluetoothDeviceDetailsPolish()) {
List<String> keys = mFormatter.getVisiblePreferenceKeysForMainPage(); List<String> keys =
mFormatter.getVisiblePreferenceKeys(
FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE);
Lifecycle lifecycle = getSettingsLifecycle(); Lifecycle lifecycle = getSettingsLifecycle();
if (keys == null || keys.contains(controller.getPreferenceKey())) { if (keys == null || keys.contains(controller.getPreferenceKey())) {
super.addPreferenceController(controller); 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.unit.sp
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import com.android.settings.R 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.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.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.dialog.getDialogWidth import com.android.settingslib.spa.widget.dialog.getDialogWidth
@Composable @Composable
fun MultiTogglePreferenceGroup( fun MultiTogglePreferenceGroup(
preferenceModels: List<DeviceSettingModel.MultiTogglePreference>, preferenceModels: List<DeviceSettingPreferenceModel.MultiTogglePreference>,
) { ) {
var settingIdForPopUp by remember { mutableStateOf<Int?>(null) } var settingIdForPopUp by remember { mutableStateOf<Int?>(null) }
@@ -115,7 +114,7 @@ fun MultiTogglePreferenceGroup(
colors = getButtonColors(preferenceModel.isActive), colors = getButtonColors(preferenceModel.isActive),
contentPadding = PaddingValues(0.dp)) { contentPadding = PaddingValues(0.dp)) {
DeviceSettingComposeIcon( DeviceSettingComposeIcon(
preferenceModel.toggles[preferenceModel.state.selectedIndex] preferenceModel.toggles[preferenceModel.selectedIndex]
.icon, .icon,
modifier = Modifier.size(24.dp)) modifier = Modifier.size(24.dp))
} }
@@ -144,7 +143,7 @@ private fun getButtonColors(isActive: Boolean) =
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun dialog( private fun dialog(
multiTogglePreference: DeviceSettingModel.MultiTogglePreference, multiTogglePreference: DeviceSettingPreferenceModel.MultiTogglePreference,
onDismiss: () -> Unit onDismiss: () -> Unit
) { ) {
BasicAlertDialog( BasicAlertDialog(
@@ -179,7 +178,7 @@ private fun dialog(
} }
@Composable @Composable
private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiTogglePreference) { private fun dialogContent(multiTogglePreference: DeviceSettingPreferenceModel.MultiTogglePreference) {
Column { Column {
Row( Row(
modifier = Modifier.fillMaxWidth().height(24.dp), modifier = Modifier.fillMaxWidth().height(24.dp),
@@ -219,7 +218,7 @@ private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiToggleP
} }
Row { Row {
for ((idx, toggle) in multiTogglePreference.toggles.withIndex()) { for ((idx, toggle) in multiTogglePreference.toggles.withIndex()) {
val selected = idx == multiTogglePreference.state.selectedIndex val selected = idx == multiTogglePreference.selectedIndex
Column( Column(
modifier = modifier =
Modifier.weight(1f) Modifier.weight(1f)
@@ -237,8 +236,7 @@ private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiToggleP
) { ) {
Button( Button(
onClick = { onClick = {
multiTogglePreference.updateState( multiTogglePreference.onSelectedChange(idx)
DeviceSettingStateModel.MultiTogglePreferenceState(idx))
}, },
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
colors = 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.bluetooth.BluetoothAdapter
import android.content.Context import android.content.Context
import android.media.AudioManager import android.media.AudioManager
import android.util.Log import android.os.Bundle
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import com.android.settings.R
import com.android.settings.SettingsPreferenceFragment import com.android.settings.SettingsPreferenceFragment
import com.android.settings.bluetooth.ui.composable.Icon import com.android.settings.bluetooth.ui.composable.Icon
import com.android.settings.bluetooth.ui.composable.MultiTogglePreferenceGroup import com.android.settings.bluetooth.ui.composable.MultiTogglePreferenceGroup
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout 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.bluetooth.ui.viewmodel.BluetoothDeviceDetailsViewModel
import com.android.settings.core.SubSettingLauncher
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settings.spa.preference.ComposePreference import com.android.settings.spa.preference.ComposePreference
import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel 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.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
import com.android.settingslib.spa.framework.theme.SettingsDimension 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.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import com.android.settingslib.spa.widget.ui.Footer
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import com.android.settingslib.spa.widget.preference.Preference as SpaPreference
/** Handles device details fragment layout according to config. */ /** Handles device details fragment layout according to config. */
interface DeviceDetailsFragmentFormatter { interface DeviceDetailsFragmentFormatter {
/** Gets keys of visible preferences in built-in preference in xml. */ /** 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. */ /** Updates device details fragment layout. */
fun updateLayout() fun updateLayout(fragmentType: FragmentTypeModel)
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@@ -79,23 +84,25 @@ class DeviceDetailsFragmentFormatterImpl(
ViewModelProvider( ViewModelProvider(
fragment, fragment,
BluetoothDeviceDetailsViewModel.Factory( BluetoothDeviceDetailsViewModel.Factory(
fragment.requireActivity().application,
repository, repository,
spatialAudioInteractor, spatialAudioInteractor,
cachedDevice, cachedDevice,
)) ))
.get(BluetoothDeviceDetailsViewModel::class.java) .get(BluetoothDeviceDetailsViewModel::class.java)
override fun getVisiblePreferenceKeysForMainPage(): List<String>? = runBlocking { override fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List<String>? =
viewModel runBlocking {
.getItems() viewModel
?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>() .getItems(fragmentType)
?.mapNotNull { it.preferenceKey } ?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()
} ?.mapNotNull { it.preferenceKey }
}
/** Updates bluetooth device details fragment layout. */ /** Updates bluetooth device details fragment layout. */
override fun updateLayout() = runBlocking { override fun updateLayout(fragmentType: FragmentTypeModel) = runBlocking {
val items = viewModel.getItems() ?: return@runBlocking val items = viewModel.getItems(fragmentType) ?: return@runBlocking
val layout = viewModel.getLayout() ?: return@runBlocking val layout = viewModel.getLayout(fragmentType) ?: return@runBlocking
val prefKeyToSettingId = val prefKeyToSettingId =
items items
.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>() .filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()
@@ -124,6 +131,8 @@ class DeviceDetailsFragmentFormatterImpl(
fragment.preferenceScreen.addPreference(pref) fragment.preferenceScreen.addPreference(pref)
} }
} }
// TODO(b/343317785): figure out how to remove the foot preference.
fragment.preferenceScreen.addPreference(Preference(context).apply { order = 10000 })
} }
@Composable @Composable
@@ -132,7 +141,7 @@ class DeviceDetailsFragmentFormatterImpl(
remember(row) { remember(row) {
layout.rows[row].settingIds.flatMapLatest { settingIds -> layout.rows[row].settingIds.flatMapLatest { settingIds ->
if (settingIds.isEmpty()) { if (settingIds.isEmpty()) {
flowOf(emptyList<DeviceSettingModel>()) flowOf(emptyList<DeviceSettingPreferenceModel>())
} else { } else {
combine( combine(
settingIds.map { settingId -> settingIds.map { settingId ->
@@ -150,72 +159,104 @@ class DeviceDetailsFragmentFormatterImpl(
0 -> {} 0 -> {}
1 -> { 1 -> {
when (val setting = settings[0]) { when (val setting = settings[0]) {
is DeviceSettingModel.ActionSwitchPreference -> { is DeviceSettingPreferenceModel.PlainPreference -> {
buildActionSwitchPreference(setting) buildPlainPreference(setting)
} }
is DeviceSettingModel.MultiTogglePreference -> { is DeviceSettingPreferenceModel.SwitchPreference -> {
buildSwitchPreference(setting)
}
is DeviceSettingPreferenceModel.MultiTogglePreference -> {
buildMultiTogglePreference(listOf(setting)) buildMultiTogglePreference(listOf(setting))
} }
null -> {} is DeviceSettingPreferenceModel.FooterPreference -> {
else -> { buildFooterPreference(setting)
Log.w(TAG, "Unknown preference type ${setting.id}, skip.")
} }
is DeviceSettingPreferenceModel.MoreSettingsPreference -> {
buildMoreSettingsPreference()
}
null -> {}
} }
} }
else -> { else -> {
if (!settings.all { it is DeviceSettingModel.MultiTogglePreference }) { if (!settings.all { it is DeviceSettingPreferenceModel.MultiTogglePreference }) {
return return
} }
buildMultiTogglePreference( buildMultiTogglePreference(
settings.filterIsInstance<DeviceSettingModel.MultiTogglePreference>()) settings.filterIsInstance<DeviceSettingPreferenceModel.MultiTogglePreference>())
} }
} }
} }
@Composable @Composable
private fun buildMultiTogglePreference(prefs: List<DeviceSettingModel.MultiTogglePreference>) { private fun buildMultiTogglePreference(
prefs: List<DeviceSettingPreferenceModel.MultiTogglePreference>
) {
MultiTogglePreferenceGroup(prefs) MultiTogglePreferenceGroup(prefs)
} }
@Composable @Composable
private fun buildActionSwitchPreference(model: DeviceSettingModel.ActionSwitchPreference) { private fun buildSwitchPreference(model: DeviceSettingPreferenceModel.SwitchPreference) {
if (model.switchState != null) { val switchPrefModel =
val switchPrefModel = object : SwitchPreferenceModel {
object : SwitchPreferenceModel { override val title = model.title
override val title = model.title override val summary = { model.summary ?: "" }
override val summary = { model.summary ?: "" } override val checked = { model.checked }
override val checked = { model.switchState?.checked } override val onCheckedChange = { newChecked: Boolean ->
override val onCheckedChange = { newChecked: Boolean -> model.onCheckedChange(newChecked)
model.updateState?.invoke(
DeviceSettingStateModel.ActionSwitchPreferenceState(newChecked))
Unit
}
override val icon = @Composable { deviceSettingIcon(model) }
} }
if (model.intent != null) { override val icon = @Composable { deviceSettingIcon(model.icon) }
TwoTargetSwitchPreference(switchPrefModel) { context.startActivity(model.intent) }
} else {
SwitchPreference(switchPrefModel)
} }
if (model.onPrimaryClick != null) {
TwoTargetSwitchPreference(
switchPrefModel, primaryOnClick = model.onPrimaryClick::invoke)
} else { } else {
SpaPreference( SwitchPreference(switchPrefModel)
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) }
})
} }
} }
@Composable @Composable
private fun deviceSettingIcon(model: DeviceSettingModel.ActionSwitchPreference) { private fun buildPlainPreference(model: DeviceSettingPreferenceModel.PlainPreference) {
model.icon?.let { icon -> SpaPreference(
Icon(icon, modifier = Modifier.size(SettingsDimension.itemIconSize)) 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}" 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 package com.android.settings.bluetooth.ui.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow 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.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository 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.DeviceSettingConfigItemModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel 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.CoroutineStart
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
@@ -38,30 +43,81 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
class BluetoothDeviceDetailsViewModel( class BluetoothDeviceDetailsViewModel(
private val application: Application,
private val deviceSettingRepository: DeviceSettingRepository, private val deviceSettingRepository: DeviceSettingRepository,
private val spatialAudioInteractor: SpatialAudioInteractor, private val spatialAudioInteractor: SpatialAudioInteractor,
private val cachedDevice: CachedBluetoothDevice, private val cachedDevice: CachedBluetoothDevice,
) : ViewModel() { ) : AndroidViewModel(application){
private val items = private val items =
viewModelScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) { viewModelScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
deviceSettingRepository.getDeviceSettingsConfig(cachedDevice) 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( fun getDeviceSetting(
cachedDevice: CachedBluetoothDevice, cachedDevice: CachedBluetoothDevice,
@DeviceSettingId settingId: Int @DeviceSettingId settingId: Int
): Flow<DeviceSettingModel?> { ): Flow<DeviceSettingPreferenceModel?> {
if (settingId == DeviceSettingId.DEVICE_SETTING_ID_MORE_SETTINGS) {
return flowOf(DeviceSettingPreferenceModel.MoreSettingsPreference(settingId))
}
return when (settingId) { return when (settingId) {
DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE -> DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE ->
spatialAudioInteractor.getDeviceSetting(cachedDevice) spatialAudioInteractor.getDeviceSetting(cachedDevice)
else -> deviceSettingRepository.getDeviceSetting(cachedDevice, settingId) 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? { suspend fun getLayout(fragment: FragmentTypeModel): DeviceSettingLayout? {
val configItems = getItems() ?: return null val configItems = getItems(fragment) ?: return null
val idToDeviceSetting = val idToDeviceSetting =
configItems configItems
.filterIsInstance<DeviceSettingConfigItemModel.AppProvidedItem>() .filterIsInstance<DeviceSettingConfigItemModel.AppProvidedItem>()
@@ -80,7 +136,7 @@ class BluetoothDeviceDetailsViewModel(
if (!isXmlPreference && setting == null) { if (!isXmlPreference && setting == null) {
continue continue
} }
if (setting !is DeviceSettingModel.MultiTogglePreference) { if (setting !is DeviceSettingPreferenceModel.MultiTogglePreference) {
multiToggleSettingIds = null multiToggleSettingIds = null
positionMapping[i] = listOf(configItem.settingId) positionMapping[i] = listOf(configItem.settingId)
continue continue
@@ -103,6 +159,7 @@ class BluetoothDeviceDetailsViewModel(
} }
class Factory( class Factory(
private val application: Application,
private val deviceSettingRepository: DeviceSettingRepository, private val deviceSettingRepository: DeviceSettingRepository,
private val spatialAudioInteractor: SpatialAudioInteractor, private val spatialAudioInteractor: SpatialAudioInteractor,
private val cachedDevice: CachedBluetoothDevice, private val cachedDevice: CachedBluetoothDevice,
@@ -110,7 +167,7 @@ class BluetoothDeviceDetailsViewModel(
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return BluetoothDeviceDetailsViewModel( return BluetoothDeviceDetailsViewModel(
deviceSettingRepository, spatialAudioInteractor, cachedDevice) application, deviceSettingRepository, spatialAudioInteractor, cachedDevice)
as T as T
} }
} }

View File

@@ -43,6 +43,7 @@ import androidx.fragment.app.Fragment;
import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import com.android.settings.R;
import com.android.settings.bluetooth.Utils; import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
@@ -128,6 +129,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
+ ", broadcastId = " + ", broadcastId = "
+ broadcastId); + broadcastId);
updateSwitch(); updateSwitch();
AudioSharingUtils.toastMessage(
mContext, mContext.getString(R.string.audio_sharing_sharing_label));
mListener.onAudioSharingStateChanged(); mListener.onAudioSharingStateChanged();
} }
@@ -161,6 +164,9 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
+ ", broadcastId = " + ", broadcastId = "
+ broadcastId); + broadcastId);
updateSwitch(); updateSwitch();
AudioSharingUtils.toastMessage(
mContext,
mContext.getString(R.string.audio_sharing_sharing_stopped_label));
mListener.onAudioSharingStateChanged(); mListener.onAudioSharingStateChanged();
} }

View File

@@ -278,16 +278,17 @@ public class AdvancedPowerUsageDetail extends DashboardFragment
super.onPause(); super.onPause();
final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode();
final Context applicationContext = requireContext().getApplicationContext();
mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode);
logMetricCategory(currentOptimizeMode); logMetricCategory(currentOptimizeMode);
mExecutor.execute( mExecutor.execute(
() -> { () -> {
if (currentOptimizeMode != mOptimizationMode) { if (currentOptimizeMode != mOptimizationMode) {
AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid( AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(
getContext(), mBatteryOptimizeUtils.getUid()); applicationContext, mBatteryOptimizeUtils.getUid());
} }
BatteryOptimizeLogUtils.writeLog( BatteryOptimizeLogUtils.writeLog(
getContext().getApplicationContext(), applicationContext,
Action.LEAVE, Action.LEAVE,
BatteryOptimizeLogUtils.getPackageNameWithUserId( BatteryOptimizeLogUtils.getPackageNameWithUserId(
mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),

View File

@@ -117,17 +117,17 @@ public class PowerBackgroundUsageDetail extends DashboardFragment
super.onPause(); super.onPause();
final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode();
final Context applicationContext = requireContext().getApplicationContext();
mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode);
logMetricCategory(currentOptimizeMode); logMetricCategory(currentOptimizeMode);
mExecutor.execute( mExecutor.execute(
() -> { () -> {
if (currentOptimizeMode != mOptimizationMode) { if (currentOptimizeMode != mOptimizationMode) {
AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid( AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(
getContext(), mBatteryOptimizeUtils.getUid()); applicationContext, mBatteryOptimizeUtils.getUid());
} }
BatteryOptimizeLogUtils.writeLog( BatteryOptimizeLogUtils.writeLog(
getContext().getApplicationContext(), applicationContext,
Action.LEAVE, Action.LEAVE,
BatteryOptimizeLogUtils.getPackageNameWithUserId( BatteryOptimizeLogUtils.getPackageNameWithUserId(
mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),

View File

@@ -56,7 +56,7 @@ public class ImsQueryProvisioningStat implements ImsQuery {
final ProvisioningManager privisionManager = final ProvisioningManager privisionManager =
ProvisioningManager.createForSubscriptionId(mSubId); ProvisioningManager.createForSubscriptionId(mSubId);
return privisionManager.getProvisioningStatusForCapability(mCapability, mTech); return privisionManager.getProvisioningStatusForCapability(mCapability, mTech);
} catch (IllegalArgumentException exception) { } catch (UnsupportedOperationException exception) {
Log.w(LOG_TAG, "fail to get Provisioning stat. subId=" + mSubId, exception); Log.w(LOG_TAG, "fail to get Provisioning stat. subId=" + mSubId, exception);
} }
return false; return false;

View File

@@ -199,7 +199,7 @@ class CarrierConfigRepository(private val context: Context) {
} }
@VisibleForTesting @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" } check(key.endsWith("_string")) { "String key should ends with _string" }
getPerSubCache(subId)[key] = StringConfigValue(value) 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 android.telephony.SubscriptionInfo
import com.android.settings.R import com.android.settings.R
import com.android.settings.network.SubscriptionUtil 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.DataUsagePreferenceController.Companion.DataUsageSearchItem
import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem
import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem
@@ -115,6 +116,7 @@ class MobileNetworkSettingsSearchIndex(
fun createSearchItems(context: Context): List<MobileNetworkSettingsSearchItem> = fun createSearchItems(context: Context): List<MobileNetworkSettingsSearchItem> =
listOf( listOf(
CarrierSettingsVersionSearchItem(context),
DataUsageSearchItem(context), DataUsageSearchItem(context),
MmsMessageSearchItem(context), MmsMessageSearchItem(context),
NrAdvancedCallingSearchItem(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.catch
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -47,6 +48,11 @@ interface ImsMmTelRepository {
fun imsReadyFlow(): Flow<Boolean> fun imsReadyFlow(): Flow<Boolean>
fun isSupportedFlow(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
): Flow<Boolean>
suspend fun isSupported( suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int, @AccessNetworkConstants.TransportType transportType: Int,
@@ -55,6 +61,11 @@ interface ImsMmTelRepository {
suspend fun setCrossSimCallingEnabled(enabled: Boolean) suspend fun setCrossSimCallingEnabled(enabled: Boolean)
} }
/**
* A repository for the IMS MMTel.
*
* @throws IllegalArgumentException if the [subId] is invalid.
*/
class ImsMmTelRepositoryImpl( class ImsMmTelRepositoryImpl(
context: Context, context: Context,
private val subId: Int, private val subId: Int,
@@ -126,8 +137,12 @@ class ImsMmTelRepositoryImpl(
awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) } awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) }
}.catch { e -> }.catch { e ->
Log.w(TAG, "[$subId] error while imsReadyFlow", e) Log.w(TAG, "[$subId] error while imsReadyFlow", e)
emit(false)
}.conflate().flowOn(Dispatchers.Default) }.conflate().flowOn(Dispatchers.Default)
override fun isSupportedFlow(capability: Int, transportType: Int): Flow<Boolean> =
imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) }
override suspend fun isSupported( override suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int, @AccessNetworkConstants.TransportType transportType: Int,

View File

@@ -20,24 +20,17 @@ import android.content.Context
import android.telephony.AccessNetworkConstants import android.telephony.AccessNetworkConstants
import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL 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.ImsMmTelManager.WiFiCallingMode
import android.telephony.ims.feature.MmTelFeature import android.telephony.ims.feature.MmTelFeature
import android.telephony.ims.stub.ImsRegistrationImplBase import android.telephony.ims.stub.ImsRegistrationImplBase
import androidx.lifecycle.LifecycleOwner 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.ImsMmTelRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl 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.settings.network.telephony.telephonyManager
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow 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 import kotlinx.coroutines.withContext
interface IWifiCallingRepository { interface IWifiCallingRepository {
@@ -50,11 +43,11 @@ class WifiCallingRepository
constructor( constructor(
private val context: Context, private val context: Context,
private val subId: Int, 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 { ) : IWifiCallingRepository {
private val telephonyManager = context.telephonyManager(subId) private val telephonyManager = context.telephonyManager(subId)
private val provisioningRepository = ProvisioningRepository(context)
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!! private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
@WiFiCallingMode @WiFiCallingMode
@@ -76,28 +69,12 @@ constructor(
wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action) wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action)
} }
@OptIn(ExperimentalCoroutinesApi::class) fun wifiCallingReadyFlow(): Flow<Boolean> =
fun wifiCallingReadyFlow(): Flow<Boolean> { imsFeatureRepository.isReadyFlow(
if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
return context.subscriptionsChangedFlow().flatMapLatest { tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
combine( transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
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()
}
}
suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) { suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) {
imsMmTelRepository.isSupported( imsMmTelRepository.isSupported(

View File

@@ -45,20 +45,21 @@ class InterruptionFilterPreferenceController extends AbstractZenModePreferenceCo
@Override @Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) { public void updateState(Preference preference, @NonNull ZenMode zenMode) {
preference.setEnabled(zenMode.isEnabled()); preference.setEnabled(zenMode.isEnabled());
boolean filteringNotifications = zenMode.getRule().getInterruptionFilter() boolean allowingAll = zenMode.getRule().getInterruptionFilter() == INTERRUPTION_FILTER_ALL;
!= INTERRUPTION_FILTER_ALL;
((TwoStatePreference) preference).setChecked(filteringNotifications); ((TwoStatePreference) preference).setChecked(allowingAll);
preference.setSummary(filteringNotifications ? "" : preference.setSummary(allowingAll
mContext.getResources().getString(R.string.mode_no_notification_filter)); ? mContext.getString(R.string.zen_mode_all_notifications_allowed)
: "");
} }
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
final boolean filterNotifications = ((Boolean) newValue); final boolean allowAll = ((Boolean) newValue);
return saveMode(zenMode -> { return saveMode(zenMode -> {
zenMode.getRule().setInterruptionFilter(filterNotifications zenMode.getRule().setInterruptionFilter(allowAll
? INTERRUPTION_FILTER_PRIORITY ? INTERRUPTION_FILTER_ALL
: INTERRUPTION_FILTER_ALL); : INTERRUPTION_FILTER_PRIORITY);
return zenMode; return zenMode;
}); });
} }

View File

@@ -77,7 +77,7 @@ public class ZenModeFragment extends ZenModeFragmentBase {
new ZenModeTriggerAddPreferenceController(context, "zen_add_automatic_trigger", new ZenModeTriggerAddPreferenceController(context, "zen_add_automatic_trigger",
this, mBackend)); this, mBackend));
prefControllers.add(new InterruptionFilterPreferenceController( prefControllers.add(new InterruptionFilterPreferenceController(
context, "allow_filtering", mBackend)); context, "allow_all", mBackend));
prefControllers.add(new ManualDurationPreferenceController( prefControllers.add(new ManualDurationPreferenceController(
context, "mode_manual_duration", this, mBackend)); context, "mode_manual_duration", this, mBackend));
return prefControllers; return prefControllers;
@@ -110,9 +110,10 @@ public class ZenModeFragment extends ZenModeFragmentBase {
if (mode == null || mode.getStatus() != DISABLED_BY_OTHER) { if (mode == null || mode.getStatus() != DISABLED_BY_OTHER) {
return false; return false;
} }
mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode));
// don't come back here from the interstitial // don't come back here from the interstitial
finish(); finish();
mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode));
return true; return true;
} }

View File

@@ -50,6 +50,7 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.R; 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.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -117,7 +118,9 @@ public class BluetoothDeviceDetailsFragmentTest {
FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
when(fakeFeatureFactory.mBluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(any(), when(fakeFeatureFactory.mBluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(any(),
any(), any(), eq(mCachedDevice))).thenReturn(mFormatter); any(), any(), eq(mCachedDevice))).thenReturn(mFormatter);
when(mFormatter.getVisiblePreferenceKeysForMainPage()).thenReturn(null); when(mFormatter.getVisiblePreferenceKeys(
FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE))
.thenReturn(null);
mFragment = setupFragment(); mFragment = setupFragment();
mFragment.onAttach(mContext); mFragment.onAttach(mContext);

View File

@@ -26,6 +26,7 @@ import androidx.preference.PreferenceManager
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor 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.dashboard.DashboardFragment
import com.android.settings.testutils.FakeFeatureFactory import com.android.settings.testutils.FakeFeatureFactory
import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -45,7 +46,6 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.any import org.mockito.Mockito.any
@@ -111,10 +111,9 @@ class DeviceDetailsFragmentFormatterTest {
DeviceSettingConfigItemModel.BuiltinItem( DeviceSettingConfigItemModel.BuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons"), DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons"),
), ),
listOf(), listOf()))
"footer"))
val keys = underTest.getVisiblePreferenceKeysForMainPage() val keys = underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(keys).containsExactly("bluetooth_device_header", "action_buttons") assertThat(keys).containsExactly("bluetooth_device_header", "action_buttons")
} }
@@ -125,7 +124,7 @@ class DeviceDetailsFragmentFormatterTest {
testScope.runTest { testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null) `when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null)
val keys = underTest.getVisiblePreferenceKeysForMainPage() val keys = underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(keys).isNull() assertThat(keys).isNull()
} }
@@ -136,9 +135,9 @@ class DeviceDetailsFragmentFormatterTest {
testScope.runTest { testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null) `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") .containsExactly("bluetooth_device_header", "action_buttons", "keyboard_settings")
} }
} }
@@ -157,12 +156,11 @@ class DeviceDetailsFragmentFormatterTest {
DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS, DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
"keyboard_settings"), "keyboard_settings"),
), ),
listOf(), listOf()))
"footer"))
underTest.updateLayout() underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(getDisplayedPreferences().map { it.key }) assertThat(getDisplayedPreferences().mapNotNull { it.key })
.containsExactly("bluetooth_device_header", "keyboard_settings") .containsExactly("bluetooth_device_header", "keyboard_settings")
} }
} }
@@ -183,8 +181,7 @@ class DeviceDetailsFragmentFormatterTest {
DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS, DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
"keyboard_settings"), "keyboard_settings"),
), ),
listOf(), listOf()))
"footer"))
`when`(repository.getDeviceSetting(cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_ANC)) `when`(repository.getDeviceSetting(cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_ANC))
.thenReturn( .thenReturn(
flowOf( flowOf(
@@ -209,9 +206,9 @@ class DeviceDetailsFragmentFormatterTest {
isAllowedChangingState = true, isAllowedChangingState = true,
updateState = {}))) updateState = {})))
underTest.updateLayout() underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(getDisplayedPreferences().map { it.key }) assertThat(getDisplayedPreferences().mapNotNull { it.key })
.containsExactly( .containsExactly(
"bluetooth_device_header", "bluetooth_device_header",
"DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}", "DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}",

View File

@@ -16,12 +16,14 @@
package com.android.settings.bluetooth.ui.viewmodel package com.android.settings.bluetooth.ui.viewmodel
import android.app.Application
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout 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.settings.testutils.FakeFeatureFactory
import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
@@ -44,8 +46,6 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.times import org.mockito.Mockito.times
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
@@ -73,26 +73,23 @@ class BluetoothDeviceDetailsViewModelTest {
@Before @Before
fun setUp() { fun setUp() {
val context = ApplicationProvider.getApplicationContext<Context>() val application = ApplicationProvider.getApplicationContext<Application>()
featureFactory = FakeFeatureFactory.setupForTest() featureFactory = FakeFeatureFactory.setupForTest()
`when`(
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
eq(context), eq(bluetoothAdapter), any()))
.thenReturn(repository)
underTest = underTest =
BluetoothDeviceDetailsViewModel(repository, spatialAudioInteractor, cachedDevice) BluetoothDeviceDetailsViewModel(
application, repository, spatialAudioInteractor, cachedDevice)
} }
@Test @Test
fun getItems_returnConfigMainItems() { fun getItems_returnConfigMainMainItems() {
testScope.runTest { testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice)) `when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn( .thenReturn(
DeviceSettingConfigModel( 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) assertThat(keys).containsExactly(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2)
} }
@@ -110,19 +107,18 @@ class BluetoothDeviceDetailsViewModelTest {
BUILTIN_SETTING_ITEM_1, BUILTIN_SETTING_ITEM_1,
buildRemoteSettingItem(remoteSettingId1), buildRemoteSettingItem(remoteSettingId1),
), ),
listOf(), listOf()))
"footer"))
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1)) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1))
.thenReturn(flowOf(pref)) .thenReturn(flowOf(pref))
var deviceSetting: DeviceSettingModel? = null var deviceSettingPreference: DeviceSettingPreferenceModel? = null
underTest underTest
.getDeviceSetting(cachedDevice, remoteSettingId1) .getDeviceSetting(cachedDevice, remoteSettingId1)
.onEach { deviceSetting = it } .onEach { deviceSettingPreference = it }
.launchIn(testScope.backgroundScope) .launchIn(testScope.backgroundScope)
runCurrent() runCurrent()
assertThat(deviceSetting).isSameInstanceAs(pref) assertThat(deviceSettingPreference?.id).isEqualTo(pref.id)
verify(repository, times(1)).getDeviceSetting(cachedDevice, remoteSettingId1) verify(repository, times(1)).getDeviceSetting(cachedDevice, remoteSettingId1)
} }
} }
@@ -141,19 +137,18 @@ class BluetoothDeviceDetailsViewModelTest {
buildRemoteSettingItem( buildRemoteSettingItem(
DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE), DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE),
), ),
listOf(), listOf()))
"footer"))
`when`(spatialAudioInteractor.getDeviceSetting(cachedDevice)).thenReturn(flowOf(pref)) `when`(spatialAudioInteractor.getDeviceSetting(cachedDevice)).thenReturn(flowOf(pref))
var deviceSetting: DeviceSettingModel? = null var deviceSettingPreference: DeviceSettingPreferenceModel? = null
underTest underTest
.getDeviceSetting( .getDeviceSetting(
cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE) cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE)
.onEach { deviceSetting = it } .onEach { deviceSettingPreference = it }
.launchIn(testScope.backgroundScope) .launchIn(testScope.backgroundScope)
runCurrent() runCurrent()
assertThat(deviceSetting).isSameInstanceAs(pref) assertThat(deviceSettingPreference?.id).isEqualTo(pref.id)
verify(spatialAudioInteractor, times(1)).getDeviceSetting(cachedDevice) verify(spatialAudioInteractor, times(1)).getDeviceSetting(cachedDevice)
} }
} }
@@ -164,9 +159,9 @@ class BluetoothDeviceDetailsViewModelTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice)) `when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn( .thenReturn(
DeviceSettingConfigModel( 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)) assertThat(getLatestLayout(layout))
.isEqualTo( .isEqualTo(
@@ -191,8 +186,7 @@ class BluetoothDeviceDetailsViewModelTest {
buildRemoteSettingItem(remoteSettingId2), buildRemoteSettingItem(remoteSettingId2),
buildRemoteSettingItem(remoteSettingId3), buildRemoteSettingItem(remoteSettingId3),
), ),
listOf(), listOf()))
"footer"))
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1)) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1))
.thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId1))) .thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId1)))
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId2)) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId2))
@@ -200,7 +194,7 @@ class BluetoothDeviceDetailsViewModelTest {
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId3)) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId3))
.thenReturn(flowOf(buildActionSwitchPreference(remoteSettingId3))) .thenReturn(flowOf(buildActionSwitchPreference(remoteSettingId3)))
val layout = underTest.getLayout()!! val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!!
assertThat(getLatestLayout(layout)) assertThat(getLatestLayout(layout))
.isEqualTo( .isEqualTo(

View File

@@ -87,7 +87,7 @@ public final class InterruptionFilterPreferenceControllerTest {
.build(); .build();
mController.updateZenMode(preference, zenMode); mController.updateZenMode(preference, zenMode);
verify(preference).setChecked(false); verify(preference).setChecked(true);
} }
@Test @Test
@@ -99,7 +99,7 @@ public final class InterruptionFilterPreferenceControllerTest {
mController.updateZenMode(preference, zenMode); mController.updateZenMode(preference, zenMode);
mController.onPreferenceChange(preference, true); mController.onPreferenceChange(preference, false);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class); ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture()); verify(mBackend).updateMode(captor.capture());
@@ -118,7 +118,7 @@ public final class InterruptionFilterPreferenceControllerTest {
.build(); .build();
mController.updateZenMode(preference, zenMode); mController.updateZenMode(preference, zenMode);
verify(preference).setChecked(true); verify(preference).setChecked(false);
} }
@Test @Test
@@ -131,7 +131,7 @@ public final class InterruptionFilterPreferenceControllerTest {
mController.updateZenMode(preference, zenMode); mController.updateZenMode(preference, zenMode);
mController.onPreferenceChange(preference, false); mController.onPreferenceChange(preference, true);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class); ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture()); 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 static com.google.common.truth.Truth.assertThat;
import android.app.AutomaticZenRule;
import android.app.Flags; import android.app.Flags;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings; import android.provider.Settings;
@@ -31,7 +29,6 @@ import androidx.fragment.app.Fragment;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.settingslib.notification.modes.TestModeBuilder; import com.android.settingslib.notification.modes.TestModeBuilder;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend; import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before; import org.junit.Before;
@@ -73,10 +70,8 @@ public class ManualDurationPreferenceControllerTest {
@Test @Test
public void testIsAvailable_onlyForManualDnd() { public void testIsAvailable_onlyForManualDnd() {
assertThat(mPrefController.isAvailable(TestModeBuilder.EXAMPLE)).isFalse(); assertThat(mPrefController.isAvailable(TestModeBuilder.EXAMPLE)).isFalse();
assertThat(mPrefController.isAvailable(TestModeBuilder.MANUAL_DND_ACTIVE)).isTrue();
ZenMode manualDnd = ZenMode.manualDndMode( assertThat(mPrefController.isAvailable(TestModeBuilder.MANUAL_DND_INACTIVE)).isTrue();
new AutomaticZenRule.Builder("id", Uri.EMPTY).build(), false);
assertThat(mPrefController.isAvailable(manualDnd)).isTrue();
} }
@Test @Test

View File

@@ -23,11 +23,9 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.AutomaticZenRule;
import android.app.Flags; import android.app.Flags;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings; import android.provider.Settings;
@@ -192,8 +190,7 @@ public final class ZenModeButtonPreferenceControllerTest {
Button button = new Button(mContext); Button button = new Button(mContext);
LayoutPreference pref = mock(LayoutPreference.class); LayoutPreference pref = mock(LayoutPreference.class);
when(pref.findViewById(anyInt())).thenReturn(button); when(pref.findViewById(anyInt())).thenReturn(button);
ZenMode zenMode = ZenMode.manualDndMode( ZenMode zenMode = TestModeBuilder.MANUAL_DND_INACTIVE;
new AutomaticZenRule.Builder("manual", Uri.EMPTY).build(), false);
mController.updateZenMode(pref, zenMode); mController.updateZenMode(pref, zenMode);
button.callOnClick(); button.callOnClick();
@@ -207,8 +204,7 @@ public final class ZenModeButtonPreferenceControllerTest {
Button button = new Button(mContext); Button button = new Button(mContext);
LayoutPreference pref = mock(LayoutPreference.class); LayoutPreference pref = mock(LayoutPreference.class);
when(pref.findViewById(anyInt())).thenReturn(button); when(pref.findViewById(anyInt())).thenReturn(button);
ZenMode zenMode = ZenMode.manualDndMode( ZenMode zenMode = TestModeBuilder.MANUAL_DND_INACTIVE;
new AutomaticZenRule.Builder("manual", Uri.EMPTY).build(), false);
mController.updateZenMode(pref, zenMode); mController.updateZenMode(pref, zenMode);
button.callOnClick(); 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_OTHER;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; 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 android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.settings.notification.modes.CharSequenceTruth.assertThat; 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 static org.mockito.Mockito.verify;
import android.app.AutomaticZenRule;
import android.app.Flags; import android.app.Flags;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
@@ -125,12 +123,7 @@ public class ZenModeTriggerAddPreferenceControllerTest {
@Test @Test
public void isAvailable_manualDND_false() { public void isAvailable_manualDND_false() {
ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE);
Uri.parse("manual"))
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.build(), /* isActive= */ false);
mController.setZenMode(manualMode);
assertThat(mController.isAvailable()).isFalse(); 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_OTHER;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; 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 android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.app.AutomaticZenRule;
import android.app.Flags; import android.app.Flags;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.SystemZenRules; import android.service.notification.SystemZenRules;
@@ -116,12 +113,7 @@ public class ZenModeTriggerCategoryPreferenceControllerTest {
@Test @Test
public void isAvailable_manualDND_false() { public void isAvailable_manualDND_false() {
ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE);
Uri.parse("manual"))
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.build(), /* isActive= */ false);
mController.setZenMode(manualMode);
assertThat(mController.isAvailable()).isFalse(); 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_OTHER;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; 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 android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.settings.notification.modes.CharSequenceTruth.assertThat; 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 static org.robolectric.Shadows.shadowOf;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AutomaticZenRule;
import android.app.Flags; import android.app.Flags;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Looper; import android.os.Looper;
import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule;
@@ -149,12 +146,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest {
@Test @Test
public void isAvailable_manualDND_false() { public void isAvailable_manualDND_false() {
ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE);
Uri.parse("manual"))
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.build(), /* isActive= */ false);
mController.setZenMode(manualMode);
assertThat(mController.isAvailable()).isFalse(); 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 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 com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -71,13 +73,6 @@ public class ZenModesListPreferenceControllerTest {
.build()) .build())
.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 @Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
@@ -152,7 +147,7 @@ public class ZenModesListPreferenceControllerTest {
@DisableFlags(Flags.FLAG_MODES_UI) @DisableFlags(Flags.FLAG_MODES_UI)
public void testModesUiOff_notAvailableAndNoSearchData() { public void testModesUiOff_notAvailableAndNoSearchData() {
// There exist modes // 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(); assertThat(mPrefController.isAvailable()).isFalse();
List<SearchIndexableRaw> data = new ArrayList<>(); 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 // Changing mode data so there's a different one mode doesn't keep any previous data
// (and setting that state up in the caller) // (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<>(); List<SearchIndexableRaw> newData = new ArrayList<>();
mPrefController.updateDynamicRawDataToIndex(newData); mPrefController.updateDynamicRawDataToIndex(newData);
assertThat(newData).hasSize(1); assertThat(newData).hasSize(1);
SearchIndexableRaw newItem = newData.get(0); 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 assertThat(newItem.title).isEqualTo("Do Not Disturb"); // set above
} }
@Test @Test
@EnableFlags(Flags.FLAG_MODES_UI) @EnableFlags(Flags.FLAG_MODES_UI)
public void testUpdateDynamicRawDataToIndex_multipleModes() { 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<>(); List<SearchIndexableRaw> data = new ArrayList<>();
mPrefController.updateDynamicRawDataToIndex(data); mPrefController.updateDynamicRawDataToIndex(data);
@@ -208,7 +203,7 @@ public class ZenModesListPreferenceControllerTest {
// Should keep the order presented by getModes() // Should keep the order presented by getModes()
SearchIndexableRaw item0 = data.get(0); 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 assertThat(item0.title).isEqualTo("Do Not Disturb"); // set above
SearchIndexableRaw item1 = data.get(1); 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 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 @Test
fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() { 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");
}
}