diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java index 06e65846f2d..183c6f730ba 100644 --- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java +++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java @@ -16,6 +16,8 @@ package com.android.settings.core; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_SETTINGS_PAGE_SCROLL; + import android.content.Context; import android.os.Bundle; import android.text.TextUtils; @@ -24,7 +26,9 @@ import android.util.Log; import androidx.annotation.XmlRes; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import androidx.recyclerview.widget.RecyclerView; +import com.android.internal.jank.InteractionJankMonitor; import com.android.settings.overlay.FeatureFactory; import com.android.settings.survey.SurveyMixin; import com.android.settingslib.core.instrumentation.Instrumentable; @@ -47,6 +51,7 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc protected final int PLACEHOLDER_METRIC = 10000; private VisibilityLoggerMixin mVisibilityLoggerMixin; + private RecyclerView.OnScrollListener mOnScrollListener; @Override public void onAttach(Context context) { @@ -62,9 +67,25 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc @Override public void onResume() { mVisibilityLoggerMixin.setSourceMetricsCategory(getActivity()); + // Add scroll listener to trace interaction jank. + final RecyclerView recyclerView = getListView(); + if (recyclerView != null) { + mOnScrollListener = new OnScrollListener(getClass().getName()); + recyclerView.addOnScrollListener(mOnScrollListener); + } super.onResume(); } + @Override + public void onPause() { + final RecyclerView recyclerView = getListView(); + if (mOnScrollListener != null) { + recyclerView.removeOnScrollListener(mOnScrollListener); + mOnScrollListener = null; + } + super.onPause(); + } + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { final int resId = getPreferenceScreenResId(); @@ -123,4 +144,26 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc } } + private static final class OnScrollListener extends RecyclerView.OnScrollListener { + private final InteractionJankMonitor mMonitor = InteractionJankMonitor.getInstance(); + private final String mClassName; + + private OnScrollListener(String className) { + mClassName = className; + } + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + switch (newState) { + case RecyclerView.SCROLL_STATE_DRAGGING: + // TODO: Update API with tag parameter (class name). + mMonitor.begin(recyclerView, CUJ_SETTINGS_PAGE_SCROLL); + break; + case RecyclerView.SCROLL_STATE_IDLE: + mMonitor.end(CUJ_SETTINGS_PAGE_SCROLL); + break; + default: + } + } + } } diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java index 6b92e570099..fd711f8ab61 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java @@ -57,6 +57,7 @@ import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.core.FeatureFlags; import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment; import com.android.settings.search.SearchFeatureProvider; +import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowStorageManager; import com.android.settings.testutils.shadow.ShadowUserManager; @@ -81,7 +82,8 @@ import org.robolectric.shadows.ShadowPersistentDataBlockManager; ShadowPersistentDataBlockManager.class, ShadowStorageManager.class, ShadowUserManager.class, - ShadowUtils.class + ShadowUtils.class, + ShadowInteractionJankMonitor.class }) public class ChooseLockGenericTest { diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInteractionJankMonitor.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInteractionJankMonitor.java new file mode 100644 index 00000000000..3ec07c515ce --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInteractionJankMonitor.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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.shadow; + +import static org.mockito.Mockito.mock; + +import com.android.internal.jank.InteractionJankMonitor; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(InteractionJankMonitor.class) +public class ShadowInteractionJankMonitor { + + @Implementation + public static InteractionJankMonitor getInstance() { + return mock(InteractionJankMonitor.class); + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java index c9d21190bb6..ab306d9a1a1 100644 --- a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java @@ -45,6 +45,7 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import com.android.settings.testutils.XmlTestUtils; +import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; import com.android.settingslib.core.AbstractPreferenceController; import org.junit.Before; @@ -55,11 +56,13 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowInteractionJankMonitor.class) public class WifiP2pSettingsTest { private Context mContext; diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java index 657d3a7603e..8c07ac3f9c5 100644 --- a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java +++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java @@ -33,6 +33,7 @@ import androidx.fragment.app.FragmentTransaction; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; import com.android.settingslib.core.AbstractPreferenceController; import org.junit.Before; @@ -43,8 +44,10 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowInteractionJankMonitor.class) public class SavedAccessPointsWifiSettings2Test { @Mock