From 22d3b0420e7bc665e5d9f308cf74882d72230e1d Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 15 Jan 2025 13:05:35 +0800 Subject: [PATCH 1/2] [Catalyst] Setup metrics logger for preference ui action Bug: 389886085 Flag: com.android.settings.flags.catalyst Test: atest&manual Change-Id: I8c3ce6cbdeeefe7857132a40ea486d0f56928d7a --- src/com/android/settings/Metrics.kt | 54 +++++++++++++++++++ .../android/settings/SettingsApplication.java | 2 + tests/robotests/Android.bp | 3 +- .../android/settings/testutils/MetricsRule.kt | 41 ++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/Metrics.kt create mode 100644 tests/robotests/testutils/com/android/settings/testutils/MetricsRule.kt diff --git a/src/com/android/settings/Metrics.kt b/src/com/android/settings/Metrics.kt new file mode 100644 index 00000000000..0d5ea56aff3 --- /dev/null +++ b/src/com/android/settings/Metrics.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2025 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 + +import android.content.Context +import com.android.settings.overlay.FeatureFactory +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider +import com.android.settingslib.metadata.PreferenceUiActionMetricsLogger +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.PreferenceScreenMetadata + +/** Provides metrics for preference action. */ +interface PreferenceActionMetricsProvider { + + /** Metrics action id for the preference. */ + val preferenceActionMetrics: Int +} + +/** [PreferenceUiActionMetricsLogger] of Settings app. */ +class SettingsMetricsLogger +@JvmOverloads +constructor( + private val context: Context, + private val metricsFeatureProvider: MetricsFeatureProvider = + FeatureFactory.featureFactory.metricsFeatureProvider, +) : PreferenceUiActionMetricsLogger { + + override fun logPreferenceValueChange( + screen: PreferenceScreenMetadata, + preference: PreferenceMetadata, + value: Any?, + ) { + if (preference !is PreferenceActionMetricsProvider) return + when (value) { + is Boolean -> + metricsFeatureProvider.action(context, preference.preferenceActionMetrics, value) + else -> {} + } + } +} diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java index 442e3c2559a..9c5671f1027 100644 --- a/src/com/android/settings/SettingsApplication.java +++ b/src/com/android/settings/SettingsApplication.java @@ -77,6 +77,8 @@ public class SettingsApplication extends Application { if (Flags.catalyst()) { PreferenceScreenRegistry.INSTANCE.setPreferenceScreenMetadataFactories( preferenceScreenFactories()); + PreferenceScreenRegistry.INSTANCE.setPreferenceUiActionMetricsLogger( + new SettingsMetricsLogger(this)); PreferenceBindingFactory.setDefaultFactory(new SettingsPreferenceBindingFactory()); } diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp index 935c687ade3..464d9708c0a 100644 --- a/tests/robotests/Android.bp +++ b/tests/robotests/Android.bp @@ -58,7 +58,6 @@ android_robolectric_test { "Settings-robo-testutils", "Settings-testutils2", "SettingsLib-robo-testutils", - "SettingsLibPreference-testutils", "Settings_robolectric_meta_service_file", "aconfig_settings_flags_lib", "android.webkit.flags-aconfig-java", @@ -73,6 +72,7 @@ android_robolectric_test { "kotlin-test", "mockito-robolectric-prebuilt", // mockito deps order matters! "mockito-kotlin2", + "SettingsLibPreference-testutils", // order matters because it depends on mockito-kotlin2 "notification_flags_lib", "platform-test-annotations", "testables", @@ -115,6 +115,7 @@ java_library { libs: [ "Robolectric_all-target", "Settings-core", + "androidx.test.core", "mockito-robolectric-prebuilt", "truth", ], diff --git a/tests/robotests/testutils/com/android/settings/testutils/MetricsRule.kt b/tests/robotests/testutils/com/android/settings/testutils/MetricsRule.kt new file mode 100644 index 00000000000..44fa4bf90c6 --- /dev/null +++ b/tests/robotests/testutils/com/android/settings/testutils/MetricsRule.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2025 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.testutils + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import com.android.settings.SettingsMetricsLogger +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider +import com.android.settingslib.metadata.PreferenceScreenRegistry +import org.junit.rules.TestWatcher +import org.junit.runner.Description + +/** Test rule for metrics. */ +class MetricsRule : TestWatcher() { + val metricsFeatureProvider: MetricsFeatureProvider = + FakeFeatureFactory.setupForTest().metricsFeatureProvider + + override fun starting(description: Description) { + val context: Context = ApplicationProvider.getApplicationContext() + PreferenceScreenRegistry.preferenceUiActionMetricsLogger = + SettingsMetricsLogger(context, metricsFeatureProvider) + } + + override fun finished(description: Description) { + PreferenceScreenRegistry.preferenceUiActionMetricsLogger = null + } +} From 22ce4494148bc452f17cc044d2c8c3d58d3fb848 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 15 Jan 2025 13:06:34 +0800 Subject: [PATCH 2/2] [Catalyst] Update metrics logging for AirplaneModePreference NO_IFTTT=Catalyst only Bug: 386330825 Bug: 389886085 Flag: com.android.settings.flags.catalyst Test: atest Change-Id: I1d1fced2e24bd10839dd8f29a6fdd6f0bd04667c --- .../network/AirplaneModePreference.kt | 20 ++++++++-------- .../network/AirplaneModePreferenceTest.kt | 24 ++++--------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/com/android/settings/network/AirplaneModePreference.kt b/src/com/android/settings/network/AirplaneModePreference.kt index b870f30eaf1..3b2b58a5148 100644 --- a/src/com/android/settings/network/AirplaneModePreference.kt +++ b/src/com/android/settings/network/AirplaneModePreference.kt @@ -30,11 +30,11 @@ import android.telephony.TelephonyManager import androidx.annotation.DrawableRes import androidx.preference.Preference import com.android.settings.AirplaneModeEnabler +import com.android.settings.PreferenceActionMetricsProvider import com.android.settings.PreferenceRestrictionMixin import com.android.settings.R import com.android.settings.Utils import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn -import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.settingslib.RestrictedSwitchPreference import com.android.settingslib.datastore.AbstractKeyedDataObservable import com.android.settingslib.datastore.KeyValueStore @@ -51,6 +51,7 @@ import com.android.settingslib.metadata.SwitchPreference // LINT.IfChange class AirplaneModePreference : SwitchPreference(KEY, R.string.airplane_mode), + PreferenceActionMetricsProvider, PreferenceAvailabilityProvider, PreferenceLifecycleProvider, PreferenceRestrictionMixin { @@ -88,6 +89,9 @@ class AirplaneModePreference : override val sensitivityLevel get() = SensitivityLevel.HIGH_SENSITIVITY + override val preferenceActionMetrics: Int + get() = ACTION_AIRPLANE_TOGGLE + override fun storage(context: Context): KeyValueStore = AirplaneModeStorage(context, SettingsGlobalStore.get(context)) @@ -109,16 +113,12 @@ class AirplaneModePreference : (settingsStore.getBoolean(key) ?: DEFAULT_VALUE) as T override fun setValue(key: String, valueType: Class, value: T?) { - if (value is Boolean) { - settingsStore.setBoolean(key, value) + if (value !is Boolean) return + settingsStore.setBoolean(key, value) - val intent = Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED) - intent.putExtra("state", value) - context.sendBroadcastAsUser(intent, UserHandle.ALL) - - val metricsFeature = featureFactory.metricsFeatureProvider - metricsFeature.action(context, ACTION_AIRPLANE_TOGGLE, value) - } + val intent = Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED) + intent.putExtra("state", value) + context.sendBroadcastAsUser(intent, UserHandle.ALL) } override fun onFirstObserverAdded() { diff --git a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt index 75b843d8b46..5b39a269e97 100644 --- a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt +++ b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt @@ -27,10 +27,11 @@ import android.telephony.TelephonyManager import androidx.preference.SwitchPreferenceCompat import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settings.testutils.FakeFeatureFactory +import com.android.settings.testutils.MetricsRule import com.android.settingslib.datastore.SettingsGlobalStore import com.android.settingslib.preference.createAndBindWidget import com.google.common.truth.Truth.assertThat +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt @@ -41,6 +42,7 @@ import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class AirplaneModePreferenceTest { + @Rule(order = 0) @JvmField val metricsRule = MetricsRule() private val mockResources = mock() private val mockPackageManager = mock() @@ -106,24 +108,6 @@ class AirplaneModePreferenceTest { assertThat(getValue).isFalse() } - @Test - fun setValue_valueTrue_metricsActionAirplaneToggleTrue() { - val metricsFeatureProvider = FakeFeatureFactory.setupForTest().metricsFeatureProvider - - airplaneModePreference.storage(context).setBoolean(AirplaneModePreference.KEY, true) - - verify(metricsFeatureProvider).action(context, ACTION_AIRPLANE_TOGGLE, true) - } - - @Test - fun setValue_valueFalse_metricsActionAirplaneToggleFalse() { - val metricsFeatureProvider = FakeFeatureFactory.setupForTest().metricsFeatureProvider - - airplaneModePreference.storage(context).setBoolean(AirplaneModePreference.KEY, false) - - verify(metricsFeatureProvider).action(context, ACTION_AIRPLANE_TOGGLE, false) - } - @Test fun performClick_defaultOn_checkedIsFalse() { SettingsGlobalStore.get(context).setInt(Settings.Global.AIRPLANE_MODE_ON, 1) @@ -131,6 +115,7 @@ class AirplaneModePreferenceTest { val preference = getSwitchPreference().apply { performClick() } assertThat(preference.isChecked).isFalse() + verify(metricsRule.metricsFeatureProvider).action(context, ACTION_AIRPLANE_TOGGLE, false) } @Test @@ -140,6 +125,7 @@ class AirplaneModePreferenceTest { val preference = getSwitchPreference().apply { performClick() } assertThat(preference.isChecked).isTrue() + verify(metricsRule.metricsFeatureProvider).action(context, ACTION_AIRPLANE_TOGGLE, true) } private fun getSwitchPreference(): SwitchPreferenceCompat =