Add the jank detection to Settings toggles

Add jank detection when click the following preferences,
 - SwitchPreference
   Single target, detect click in InstrumentedPreferenceFragment
 - PrimarySwitchPreference
   Two target, only detect click switch in switch's onClick()
 - MainSwitchPreference
   Single target, detect click in TogglePreferenceController
 - SettingsMainSwitchPreference
   Single target, detect click in its onSwitchChanged()

Bug: 230285829
Test: manual & robo tests
Change-Id: I97a13e05a601237b16cd2d903ba2fb6ec4a69a74
This commit is contained in:
Chaohui Wang
2022-05-06 01:17:20 +08:00
parent 72642fa03d
commit 692068d535
6 changed files with 46 additions and 4 deletions

View File

@@ -27,6 +27,7 @@ import android.util.Log;
import androidx.annotation.XmlRes; import androidx.annotation.XmlRes;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.jank.InteractionJankMonitor;
@@ -34,6 +35,7 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settings.survey.SurveyMixin; import com.android.settings.survey.SurveyMixin;
import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment; import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
@@ -45,7 +47,6 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc
private static final String TAG = "InstrumentedPrefFrag"; private static final String TAG = "InstrumentedPrefFrag";
protected MetricsFeatureProvider mMetricsFeatureProvider; protected MetricsFeatureProvider mMetricsFeatureProvider;
// metrics placeholder value. Only use this for development. // metrics placeholder value. Only use this for development.
@@ -65,6 +66,19 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc
super.onAttach(context); super.onAttach(context);
} }
@Override
public void onStart() {
super.onStart();
// Override the OnPreferenceTreeClickListener in super.onStart() to inject jank detection.
getPreferenceManager().setOnPreferenceTreeClickListener((preference) -> {
if (preference instanceof SwitchPreference) {
SettingsJankMonitor.detectSwitchPreferenceClickJank(
getListView(), (SwitchPreference) preference);
}
return onPreferenceTreeClick(preference);
});
}
@Override @Override
public void onResume() { public void onResume() {
mVisibilityLoggerMixin.setSourceMetricsCategory(getActivity()); mVisibilityLoggerMixin.setSourceMetricsCategory(getActivity());

View File

@@ -16,12 +16,15 @@ package com.android.settings.core;
import android.content.Context; import android.content.Context;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference; import androidx.preference.TwoStatePreference;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.SliceData; import com.android.settings.slices.SliceData;
import com.android.settings.widget.TwoStateButtonPreference; import com.android.settings.widget.TwoStateButtonPreference;
import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
import com.android.settingslib.widget.MainSwitchPreference;
/** /**
* Abstract class that consolidates logic for updating toggle controllers. * Abstract class that consolidates logic for updating toggle controllers.
@@ -50,6 +53,16 @@ public abstract class TogglePreferenceController extends BasePreferenceControlle
*/ */
public abstract boolean setChecked(boolean isChecked); public abstract boolean setChecked(boolean isChecked);
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
Preference preference = screen.findPreference(getPreferenceKey());
if (preference instanceof MainSwitchPreference) {
((MainSwitchPreference) preference).addOnSwitchChangeListener((switchView, isChecked) ->
SettingsJankMonitor.detectToggleJank(getPreferenceKey(), switchView));
}
}
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
if (preference instanceof TwoStatePreference) { if (preference instanceof TwoStatePreference) {

View File

@@ -32,6 +32,7 @@ import androidx.preference.TwoStatePreference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.widget.SettingsMainSwitchBar.OnBeforeCheckedChangeListener; import com.android.settings.widget.SettingsMainSwitchBar.OnBeforeCheckedChangeListener;
import com.android.settingslib.RestrictedPreferenceHelper; import com.android.settingslib.RestrictedPreferenceHelper;
import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
import com.android.settingslib.widget.OnMainSwitchChangeListener; import com.android.settingslib.widget.OnMainSwitchChangeListener;
import com.google.android.setupdesign.util.LayoutStyler; import com.google.android.setupdesign.util.LayoutStyler;
@@ -153,6 +154,7 @@ public class SettingsMainSwitchPreference extends TwoStatePreference implements
@Override @Override
public void onSwitchChanged(Switch switchView, boolean isChecked) { public void onSwitchChanged(Switch switchView, boolean isChecked) {
super.setChecked(isChecked); super.setChecked(isChecked);
SettingsJankMonitor.detectToggleJank(getKey(), switchView);
} }
/** /**

View File

@@ -31,9 +31,11 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.provider.Settings; import android.provider.Settings;
import android.widget.Switch;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settingslib.widget.MainSwitchPreference; import com.android.settingslib.widget.MainSwitchPreference;
import org.junit.Before; import org.junit.Before;
@@ -44,16 +46,21 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow; import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowActivityManager; import org.robolectric.shadows.ShadowActivityManager;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowInteractionJankMonitor.class})
public class BubbleNotificationPreferenceControllerTest { public class BubbleNotificationPreferenceControllerTest {
private Context mContext; private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen; private PreferenceScreen mScreen;
@Mock
private Switch mSwitch;
private BubbleNotificationPreferenceController mController; private BubbleNotificationPreferenceController mController;
private MainSwitchPreference mPreference; private MainSwitchPreference mPreference;
@@ -102,7 +109,7 @@ public class BubbleNotificationPreferenceControllerTest {
public void onSwitchChanged_true_settingIsOff_flagShouldOn() { public void onSwitchChanged_true_settingIsOff_flagShouldOn() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
mController.onSwitchChanged(null, true); mController.onSwitchChanged(mSwitch, true);
assertThat(Settings.Global.getInt(mContext.getContentResolver(), assertThat(Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, OFF)).isEqualTo(ON); NOTIFICATION_BUBBLES, OFF)).isEqualTo(ON);
@@ -112,7 +119,7 @@ public class BubbleNotificationPreferenceControllerTest {
public void onSwitchChanged_false_settingIsOn_flagShouldOff() { public void onSwitchChanged_false_settingIsOn_flagShouldOff() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON);
mController.onSwitchChanged(null, false); mController.onSwitchChanged(mSwitch, false);
assertThat(Settings.Global.getInt(mContext.getContentResolver(), assertThat(Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, ON)).isEqualTo(OFF); NOTIFICATION_BUBBLES, ON)).isEqualTo(OFF);

View File

@@ -17,7 +17,6 @@
package com.android.settings.notification.app; package com.android.settings.notification.app;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_NONE;
@@ -49,6 +48,7 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend;
import com.android.settings.widget.SettingsMainSwitchPreference; import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -57,11 +57,13 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication; import org.robolectric.shadows.ShadowApplication;
import java.util.ArrayList; import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowInteractionJankMonitor.class})
public class BlockPreferenceControllerTest { public class BlockPreferenceControllerTest {
private Context mContext; private Context mContext;

View File

@@ -23,6 +23,10 @@ import com.android.internal.jank.InteractionJankMonitor;
import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements; import org.robolectric.annotation.Implements;
/**
* @deprecated use {@link com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor}
*/
@Deprecated
@Implements(InteractionJankMonitor.class) @Implements(InteractionJankMonitor.class)
public class ShadowInteractionJankMonitor { public class ShadowInteractionJankMonitor {