diff --git a/res/layout/reset_esim_checkbox.xml b/res/layout/reset_esim_checkbox.xml index 626ecc3b22d..59f92631af1 100644 --- a/res/layout/reset_esim_checkbox.xml +++ b/res/layout/reset_esim_checkbox.xml @@ -49,7 +49,7 @@ android:text="@string/reset_esim_title"/> diff --git a/res/xml/input_touchpad_three_finger_tap_customization.xml b/res/xml/input_touchpad_three_finger_tap_customization.xml index f0103aeda73..74510811158 100644 --- a/res/xml/input_touchpad_three_finger_tap_customization.xml +++ b/res/xml/input_touchpad_three_finger_tap_customization.xml @@ -16,7 +16,6 @@ @@ -25,5 +24,4 @@ android:key="input_touchpad_three_finger_tap_preference" android:title="@string/three_finger_tap_preference_title"/> - diff --git a/res/xml/security_lockscreen_settings.xml b/res/xml/security_lockscreen_settings.xml index 15d530357d9..fdc738afca8 100644 --- a/res/xml/security_lockscreen_settings.xml +++ b/res/xml/security_lockscreen_settings.xml @@ -17,6 +17,7 @@ } +@FlowPreview @OptIn(ExperimentalCoroutinesApi::class) class DeviceDetailsFragmentFormatterImpl( private val context: Context, @@ -101,6 +107,9 @@ class DeviceDetailsFragmentFormatterImpl( private val cachedDevice: CachedBluetoothDevice, private val backgroundCoroutineContext: CoroutineContext, ) : DeviceDetailsFragmentFormatter { + private val metricsFeatureProvider = FeatureFactory.featureFactory.metricsFeatureProvider + private val prefVisibility = mutableMapOf>() + private val prefVisibilityJobs = mutableListOf() private val viewModel: BluetoothDeviceDetailsViewModel = ViewModelProvider( @@ -147,21 +156,33 @@ class DeviceDetailsFragmentFormatterImpl( prefKeyToSettingId[pref.key]?.let { id -> settingIdToXmlPreferences[id] = pref } } fragment.preferenceScreen.removeAll() + for (job in prefVisibilityJobs) { + job.cancel() + } + prefVisibilityJobs.clear() for (row in items.indices) { val settingId = items[row].settingId if (settingIdToXmlPreferences.containsKey(settingId)) { fragment.preferenceScreen.addPreference( - settingIdToXmlPreferences[settingId]!!.apply { order = row } + settingIdToXmlPreferences[settingId]!! + .apply { order = row } + .also { logItemShown(it.key, it.isVisible) } ) } else { + val prefKey = getPreferenceKey(settingId) + prefVisibilityJobs.add( + getDevicesSettingForRow(layout, row) + .onEach { logItemShown(prefKey, it.isNotEmpty()) } + .launchIn(fragment.lifecycleScope) + ) val pref = ComposePreference(context) .apply { - key = getPreferenceKey(settingId) + key = prefKey order = row } - .also { pref -> pref.setContent { buildPreference(layout, row) } } + .also { pref -> pref.setContent { buildPreference(layout, row, prefKey) } } fragment.preferenceScreen.addPreference(pref) } } @@ -183,24 +204,28 @@ class DeviceDetailsFragmentFormatterImpl( } ?: emit(null) } - @Composable - private fun buildPreference(layout: DeviceSettingLayout, row: Int) { - val contents by - remember(row) { - layout.rows[row].columns.flatMapLatest { columns -> - if (columns.isEmpty()) { - flowOf(emptyList()) - } else { - combine( - columns.map { column -> - viewModel.getDeviceSetting(cachedDevice, column.settingId) - } - ) { - it.toList() - } - } + private fun getDevicesSettingForRow( + layout: DeviceSettingLayout, + row: Int, + ): Flow> = + layout.rows[row].columns.flatMapLatest { columns -> + if (columns.isEmpty()) { + flowOf(emptyList()) + } else { + combine( + columns.map { column -> + viewModel.getDeviceSetting(cachedDevice, column.settingId) } + ) { + it.toList().filterNotNull() } + } + } + + @Composable + private fun buildPreference(layout: DeviceSettingLayout, row: Int, prefKey: String) { + val contents by + remember(row) { getDevicesSettingForRow(layout, row) } .collectAsStateWithLifecycle(initialValue = listOf()) val highlighted by @@ -226,31 +251,31 @@ class DeviceDetailsFragmentFormatterImpl( shape = RoundedCornerShape(28.dp), ) ) {} - buildPreferences(settings) + buildPreferences(settings, prefKey) } } } @Composable - fun buildPreferences(settings: List) { + fun buildPreferences(settings: List, prefKey: String) { when (settings.size) { 0 -> {} 1 -> { when (val setting = settings[0]) { is DeviceSettingPreferenceModel.PlainPreference -> { - buildPlainPreference(setting) + buildPlainPreference(setting, prefKey) } is DeviceSettingPreferenceModel.SwitchPreference -> { - buildSwitchPreference(setting) + buildSwitchPreference(setting, prefKey) } is DeviceSettingPreferenceModel.MultiTogglePreference -> { - buildMultiTogglePreference(setting) + buildMultiTogglePreference(setting, prefKey) } is DeviceSettingPreferenceModel.FooterPreference -> { buildFooterPreference(setting) } is DeviceSettingPreferenceModel.MoreSettingsPreference -> { - buildMoreSettingsPreference() + buildMoreSettingsPreference(prefKey) } is DeviceSettingPreferenceModel.HelpPreference -> {} null -> {} @@ -262,20 +287,32 @@ class DeviceDetailsFragmentFormatterImpl( @Composable private fun buildMultiTogglePreference( - pref: DeviceSettingPreferenceModel.MultiTogglePreference + pref: DeviceSettingPreferenceModel.MultiTogglePreference, + prefKey: String, ) { - MultiTogglePreference(pref) + MultiTogglePreference( + pref.copy( + onSelectedChange = { newState -> + logItemClick(prefKey, newState) + pref.onSelectedChange(newState) + } + ) + ) } @Composable - private fun buildSwitchPreference(model: DeviceSettingPreferenceModel.SwitchPreference) { + private fun buildSwitchPreference( + model: DeviceSettingPreferenceModel.SwitchPreference, + prefKey: String, + ) { val switchPrefModel = object : SwitchPreferenceModel { override val title = model.title override val summary = { model.summary ?: "" } override val checked = { model.checked } - override val onCheckedChange = { newChecked: Boolean -> - model.onCheckedChange(newChecked) + override val onCheckedChange = { newState: Boolean -> + logItemClick(prefKey, if (newState) EVENT_SWITCH_ON else EVENT_SWITCH_OFF) + model.onCheckedChange(newState) } override val changeable = { !model.disabled } override val icon: (@Composable () -> Unit)? @@ -289,8 +326,11 @@ class DeviceDetailsFragmentFormatterImpl( if (model.action != null) { TwoTargetSwitchPreference( switchPrefModel, - primaryOnClick = { triggerAction(model.action) }, - primaryEnabled = { !model.disabled } + primaryOnClick = { + logItemClick(prefKey, EVENT_CLICK_PRIMARY) + triggerAction(model.action) + }, + primaryEnabled = { !model.disabled }, ) } else { SwitchPreference(switchPrefModel) @@ -298,12 +338,16 @@ class DeviceDetailsFragmentFormatterImpl( } @Composable - private fun buildPlainPreference(model: DeviceSettingPreferenceModel.PlainPreference) { + private fun buildPlainPreference( + model: DeviceSettingPreferenceModel.PlainPreference, + prefKey: String, + ) { SpaPreference( object : PreferenceModel { override val title = model.title override val summary = { model.summary ?: "" } override val onClick = { + logItemClick(prefKey, EVENT_CLICK_PRIMARY) model.action?.let { triggerAction(it) } Unit } @@ -319,7 +363,7 @@ class DeviceDetailsFragmentFormatterImpl( } @Composable - fun buildMoreSettingsPreference() { + fun buildMoreSettingsPreference(prefKey: String) { SpaPreference( object : PreferenceModel { override val title = @@ -328,6 +372,7 @@ class DeviceDetailsFragmentFormatterImpl( context.getString(R.string.bluetooth_device_more_settings_preference_summary) } override val onClick = { + logItemClick(prefKey, EVENT_CLICK_PRIMARY) SubSettingLauncher(context) .setDestination(DeviceDetailsMoreSettingsFragment::class.java.name) .setSourceMetricsCategory(fragment.getMetricsCategory()) @@ -356,6 +401,35 @@ class DeviceDetailsFragmentFormatterImpl( icon?.let { Icon(it, modifier = Modifier.size(SettingsDimension.itemIconSize)) } } + private fun logItemClick(preferenceKey: String, value: Int = 0) { + logAction(preferenceKey, SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_CLICKED, value) + } + + private fun logItemShown(preferenceKey: String, visible: Boolean) { + if (!visible && !prefVisibility.containsKey(preferenceKey)) { + return + } + prefVisibility + .computeIfAbsent(preferenceKey) { + MutableStateFlow(true).also { visibilityFlow -> + visibilityFlow + .onEach { + logAction( + preferenceKey, + SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_SHOWN, + if (it) EVENT_VISIBLE else EVENT_INVISIBLE, + ) + } + .launchIn(fragment.lifecycleScope) + } + } + .value = visible + } + + private fun logAction(preferenceKey: String, action: Int, value: Int) { + metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN, action, 0, preferenceKey, value) + } + private fun triggerAction(action: DeviceSettingActionModel) { when (action) { is DeviceSettingActionModel.IntentAction -> { @@ -375,7 +449,12 @@ class DeviceDetailsFragmentFormatterImpl( private fun getPreferenceKey(settingId: Int) = "DEVICE_SETTING_${settingId}" - companion object { + private companion object { const val TAG = "DeviceDetailsFormatter" + const val EVENT_SWITCH_OFF = 0 + const val EVENT_SWITCH_ON = 1 + const val EVENT_CLICK_PRIMARY = 2 + const val EVENT_INVISIBLE = 0 + const val EVENT_VISIBLE = 1 } } diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index 6a96089cc96..df1e71ecd44 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -427,6 +427,7 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment private void removeControllersForHybridMode() { Set keys = getPreferenceKeysInHierarchy(); Iterator iterator = mControllers.iterator(); + Lifecycle lifecycle = getSettingsLifecycle(); while (iterator.hasNext()) { AbstractPreferenceController controller = iterator.next(); String key = controller.getPreferenceKey(); @@ -438,6 +439,9 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment if (controllers != null) { controllers.remove(controller); } + if (controller instanceof LifecycleObserver) { + lifecycle.removeObserver((LifecycleObserver) controller); + } } } } diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt new file mode 100644 index 00000000000..0537e625159 --- /dev/null +++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt @@ -0,0 +1,116 @@ +/* + * 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.display + +import android.content.Context +import android.hardware.display.AmbientDisplayConfiguration +import android.os.SystemProperties +import android.os.UserHandle +import android.os.UserManager +import android.provider.Settings.Secure.DOZE_ALWAYS_ON +import com.android.settings.PreferenceRestrictionMixin +import com.android.settings.R +import com.android.settings.display.AmbientDisplayAlwaysOnPreferenceController.isAodSuppressedByBedtime +import com.android.settingslib.datastore.HandlerExecutor +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.KeyedObservableDelegate +import com.android.settingslib.datastore.KeyedObserver +import com.android.settingslib.datastore.SettingsSecureStore +import com.android.settingslib.datastore.SettingsStore +import com.android.settingslib.metadata.PreferenceAvailabilityProvider +import com.android.settingslib.metadata.PreferenceLifecycleContext +import com.android.settingslib.metadata.PreferenceLifecycleProvider +import com.android.settingslib.metadata.PreferenceSummaryProvider +import com.android.settingslib.metadata.ReadWritePermit +import com.android.settingslib.metadata.SwitchPreference + +// LINT.IfChange +class AmbientDisplayAlwaysOnPreference : + SwitchPreference(KEY, R.string.doze_always_on_title, R.string.doze_always_on_summary), + PreferenceAvailabilityProvider, + PreferenceSummaryProvider, + PreferenceLifecycleProvider, + PreferenceRestrictionMixin { + + private var keyMappingObserver: KeyedObserver? = null + + override val keywords: Int + get() = R.string.keywords_always_show_time_info + + override val restrictionKeys: Array + get() = arrayOf(UserManager.DISALLOW_AMBIENT_DISPLAY) + + override fun isEnabled(context: Context) = super.isEnabled(context) + + override fun isAvailable(context: Context) = + !SystemProperties.getBoolean(PROP_AWARE_AVAILABLE, false) && + AmbientDisplayConfiguration(context).alwaysOnAvailableForUser(UserHandle.myUserId()) + + override fun getSummary(context: Context): CharSequence? = + context.getText( + when { + isAodSuppressedByBedtime(context) -> R.string.aware_summary_when_bedtime_on + else -> R.string.doze_always_on_summary + } + ) + + override fun storage(context: Context): KeyValueStore = Storage(context) + + override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) = + ReadWritePermit.ALLOW + + override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) = + ReadWritePermit.ALLOW + + override fun onCreate(context: PreferenceLifecycleContext) { + val storage = SettingsSecureStore.get(context) + keyMappingObserver = + KeyedObserver { _, reason -> storage.notifyChange(KEY, reason) } + .also { storage.addObserver(DOZE_ALWAYS_ON, it, HandlerExecutor.main) } + } + + override fun onDestroy(context: PreferenceLifecycleContext) { + keyMappingObserver?.let { + SettingsSecureStore.get(context).removeObserver(DOZE_ALWAYS_ON, it) + } + } + + @Suppress("UNCHECKED_CAST") + class Storage( + private val context: Context, + private val settingsStore: SettingsStore = SettingsSecureStore.get(context), + ) : KeyedObservableDelegate(settingsStore), KeyValueStore { + + override fun contains(key: String) = settingsStore.contains(DOZE_ALWAYS_ON) + + override fun getDefaultValue(key: String, valueType: Class) = + context.resources.getBoolean(com.android.internal.R.bool.config_dozeAlwaysOnEnabled) + as T + + override fun getValue(key: String, valueType: Class) = + settingsStore.getValue(DOZE_ALWAYS_ON, valueType) ?: getDefaultValue(key, valueType) + + override fun setValue(key: String, valueType: Class, value: T?) = + settingsStore.setValue(DOZE_ALWAYS_ON, valueType, value) + } + + companion object { + const val KEY = "ambient_display_always_on" + private const val PROP_AWARE_AVAILABLE = "ro.vendor.aware_available" + } +} +// LINT.ThenChange(AmbientDisplayAlwaysOnPreferenceController.java) diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java index 245803493e2..17cecad0810 100644 --- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java +++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java @@ -29,6 +29,7 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; +// LINT.IfChange public class AmbientDisplayAlwaysOnPreferenceController extends TogglePreferenceController { private final int ON = 1; @@ -130,3 +131,4 @@ public class AmbientDisplayAlwaysOnPreferenceController extends TogglePreference return powerManager.isAmbientDisplaySuppressedForTokenByApp(AOD_SUPPRESSED_TOKEN, uid); } } +// LINT.ThenChange(AmbientDisplayAlwaysOnPreference.kt) diff --git a/src/com/android/settings/display/DisplayScreen.kt b/src/com/android/settings/display/DisplayScreen.kt index 422ea67618a..d8a6be11e21 100644 --- a/src/com/android/settings/display/DisplayScreen.kt +++ b/src/com/android/settings/display/DisplayScreen.kt @@ -21,6 +21,7 @@ import com.android.settings.R import com.android.settings.Settings.DisplaySettingsActivity import com.android.settings.display.darkmode.DarkModeScreen import com.android.settings.flags.Flags +import com.android.settings.security.LockScreenPreferenceScreen import com.android.settings.utils.makeLaunchIntent import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.PreferenceIconProvider @@ -50,12 +51,14 @@ open class DisplayScreen : override fun fragmentClass() = DisplaySettings::class.java - override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) { - +BrightnessLevelPreference() - +AutoBrightnessScreen.KEY - +DarkModeScreen.KEY - +PeakRefreshRateSwitchPreference() - } + override fun getPreferenceHierarchy(context: Context) = + preferenceHierarchy(this) { + +BrightnessLevelPreference() + +AutoBrightnessScreen.KEY + +LockScreenPreferenceScreen.KEY + +DarkModeScreen.KEY + +PeakRefreshRateSwitchPreference() + } override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) = makeLaunchIntent(context, DisplaySettingsActivity::class.java, metadata?.key) diff --git a/src/com/android/settings/inputmethod/PointerFillStylePreference.java b/src/com/android/settings/inputmethod/PointerFillStylePreference.java index 52535f9321d..74284d64250 100644 --- a/src/com/android/settings/inputmethod/PointerFillStylePreference.java +++ b/src/com/android/settings/inputmethod/PointerFillStylePreference.java @@ -47,6 +47,7 @@ public class PointerFillStylePreference extends Preference { public PointerFillStylePreference(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); setLayoutResource(R.layout.pointer_icon_fill_style_layout); + setSelectable(false); } @Override diff --git a/src/com/android/settings/inputmethod/PointerStrokeStylePreference.java b/src/com/android/settings/inputmethod/PointerStrokeStylePreference.java index 1c02332f8a1..fdbad929de2 100644 --- a/src/com/android/settings/inputmethod/PointerStrokeStylePreference.java +++ b/src/com/android/settings/inputmethod/PointerStrokeStylePreference.java @@ -38,6 +38,7 @@ public class PointerStrokeStylePreference extends Preference { public PointerStrokeStylePreference(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); setLayoutResource(R.layout.pointer_icon_stroke_style_layout); + setSelectable(false); } @Override diff --git a/src/com/android/settings/inputmethod/TouchpadThreeFingerTapSelector.java b/src/com/android/settings/inputmethod/TouchpadThreeFingerTapSelector.java index 164098b808b..b56d2ead775 100644 --- a/src/com/android/settings/inputmethod/TouchpadThreeFingerTapSelector.java +++ b/src/com/android/settings/inputmethod/TouchpadThreeFingerTapSelector.java @@ -45,6 +45,7 @@ public class TouchpadThreeFingerTapSelector extends Preference { super(context, attrs); setLayoutResource(R.layout.touchpad_three_finger_tap_layout); mInputManager = context.getSystemService(InputManager.class); + setSelectable(false); } @Override diff --git a/src/com/android/settings/security/LockScreenPreferenceScreen.kt b/src/com/android/settings/security/LockScreenPreferenceScreen.kt index 0c7877f88e5..3c00b428ebd 100644 --- a/src/com/android/settings/security/LockScreenPreferenceScreen.kt +++ b/src/com/android/settings/security/LockScreenPreferenceScreen.kt @@ -17,13 +17,19 @@ package com.android.settings.security import android.content.Context import com.android.settings.R +import com.android.settings.Settings.LockScreenSettingsActivity +import com.android.settings.display.AmbientDisplayAlwaysOnPreference import com.android.settings.flags.Flags +import com.android.settings.notification.LockScreenNotificationPreferenceController +import com.android.settings.utils.makeLaunchIntent +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.PreferenceSummaryProvider import com.android.settingslib.metadata.ProvidePreferenceScreen import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceScreenCreator @ProvidePreferenceScreen -open class LockScreenPreferenceScreen : PreferenceScreenCreator { +open class LockScreenPreferenceScreen : PreferenceScreenCreator, PreferenceSummaryProvider { override val key: String get() = KEY @@ -33,17 +39,24 @@ open class LockScreenPreferenceScreen : PreferenceScreenCreator { override val keywords: Int get() = R.string.keywords_ambient_display_screen + override fun getSummary(context: Context): CharSequence? = + context.getString(LockScreenNotificationPreferenceController.getSummaryResource(context)) + override fun isFlagEnabled(context: Context) = Flags.catalystLockscreenFromDisplaySettings() override fun hasCompleteHierarchy() = false override fun fragmentClass() = LockscreenDashboardFragment::class.java - override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) { - // add hierarchy here - } + override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) = + makeLaunchIntent(context, LockScreenSettingsActivity::class.java, metadata?.key) + + override fun getPreferenceHierarchy(context: Context) = + preferenceHierarchy(this) { + +AmbientDisplayAlwaysOnPreference() + } companion object { const val KEY = "lockscreen_from_display_settings" } -} \ No newline at end of file +} diff --git a/src/com/android/settings/security/LockscreenDashboardFragment.java b/src/com/android/settings/security/LockscreenDashboardFragment.java index 1e299a36cf5..ef4c778549c 100644 --- a/src/com/android/settings/security/LockscreenDashboardFragment.java +++ b/src/com/android/settings/security/LockscreenDashboardFragment.java @@ -56,8 +56,6 @@ import java.util.List; public class LockscreenDashboardFragment extends DashboardFragment implements OwnerInfoPreferenceController.OwnerInfoCallback { - public static final String KEY_AMBIENT_DISPLAY_ALWAYS_ON = "ambient_display_always_on"; - private static final String TAG = "LockscreenDashboardFragment"; @VisibleForTesting @@ -111,7 +109,9 @@ public class LockscreenDashboardFragment extends DashboardFragment @Override public void onAttach(Context context) { super.onAttach(context); - use(AmbientDisplayAlwaysOnPreferenceController.class).setConfig(getConfig(context)); + if (!isCatalystEnabled()) { + use(AmbientDisplayAlwaysOnPreferenceController.class).setConfig(getConfig(context)); + } use(AmbientDisplayNotificationsPreferenceController.class).setConfig(getConfig(context)); use(DoubleTapScreenPreferenceController.class).setConfig(getConfig(context)); use(PickupGesturePreferenceController.class).setConfig(getConfig(context)); diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt index bd56021e38d..28eaeaaa194 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt +++ b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt @@ -16,6 +16,7 @@ package com.android.settings.bluetooth.ui.view +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter import android.content.Context import android.content.Intent @@ -39,6 +40,7 @@ import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSetti import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn @@ -53,6 +55,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.any +import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @@ -62,6 +65,7 @@ import org.robolectric.shadows.ShadowLooper import org.robolectric.shadows.ShadowLooper.shadowMainLooper +@ExperimentalCoroutinesApi @RunWith(RobolectricTestRunner::class) class DeviceDetailsFragmentFormatterTest { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() @@ -70,6 +74,7 @@ class DeviceDetailsFragmentFormatterTest { @Mock private lateinit var bluetoothAdapter: BluetoothAdapter @Mock private lateinit var repository: DeviceSettingRepository + private lateinit var context: Context private lateinit var fragment: TestFragment private lateinit var underTest: DeviceDetailsFragmentFormatter private lateinit var featureFactory: FakeFeatureFactory @@ -78,7 +83,7 @@ class DeviceDetailsFragmentFormatterTest { @Before fun setUp() { - val context = ApplicationProvider.getApplicationContext() + context = ApplicationProvider.getApplicationContext() featureFactory = FakeFeatureFactory.setupForTest() `when`( featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository( @@ -204,9 +209,22 @@ class DeviceDetailsFragmentFormatterTest { null)) underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment) + runCurrent() assertThat(getDisplayedPreferences().mapNotNull { it.key }) .containsExactly("bluetooth_device_header", "keyboard_settings") + verify(featureFactory.metricsFeatureProvider) + .action( + SettingsEnums.PAGE_UNKNOWN, + SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_SHOWN, + 0, + "bluetooth_device_header", 1) + verify(featureFactory.metricsFeatureProvider) + .action( + SettingsEnums.PAGE_UNKNOWN, + SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_SHOWN, + 0, + "keyboard_settings", 1) } } @@ -249,12 +267,20 @@ class DeviceDetailsFragmentFormatterTest { updateState = {}))) underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment) + runCurrent() assertThat(getDisplayedPreferences().mapNotNull { it.key }) .containsExactly( "bluetooth_device_header", "DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}", "keyboard_settings") + verify(featureFactory.metricsFeatureProvider) + .action( + SettingsEnums.PAGE_UNKNOWN, + SettingsEnums.ACTION_BLUETOOTH_DEVICE_DETAILS_ITEM_SHOWN, + 0, + "DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}", 1 + ) } } diff --git a/tests/robotests/src/com/android/settings/security/LockscreenDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/security/LockscreenDashboardFragmentTest.java index bf5e9577250..5ca9f729cc4 100644 --- a/tests/robotests/src/com/android/settings/security/LockscreenDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/security/LockscreenDashboardFragmentTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import android.content.Context; @@ -88,7 +89,11 @@ public class LockscreenDashboardFragmentTest { AmbientDisplayAlwaysOnPreferenceController.class); mTestFragment.onAttach(mContext); - verify(controller).setConfig(any()); + if (mTestFragment.isCatalystEnabled()) { + verifyNoInteractions(controller); + } else { + verify(controller).setConfig(any()); + } } @Test