• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.core;
18 
19 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SETTINGS_PAGE_SCROLL;
20 import static com.android.internal.jank.InteractionJankMonitor.Configuration;
21 
22 import android.content.Context;
23 import android.os.Bundle;
24 import android.text.TextUtils;
25 import android.util.Log;
26 
27 import androidx.annotation.XmlRes;
28 import androidx.preference.Preference;
29 import androidx.preference.PreferenceScreen;
30 import androidx.preference.SwitchPreference;
31 import androidx.recyclerview.widget.RecyclerView;
32 
33 import com.android.internal.jank.InteractionJankMonitor;
34 import com.android.settings.overlay.FeatureFactory;
35 import com.android.settings.survey.SurveyMixin;
36 import com.android.settingslib.core.instrumentation.Instrumentable;
37 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
38 import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
39 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
40 import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
41 
42 /**
43  * Instrumented fragment that logs visibility state.
44  */
45 public abstract class InstrumentedPreferenceFragment extends ObservablePreferenceFragment
46         implements Instrumentable {
47 
48     private static final String TAG = "InstrumentedPrefFrag";
49 
50     protected MetricsFeatureProvider mMetricsFeatureProvider;
51 
52     // metrics placeholder value. Only use this for development.
53     protected final int PLACEHOLDER_METRIC = 10000;
54 
55     private VisibilityLoggerMixin mVisibilityLoggerMixin;
56     private RecyclerView.OnScrollListener mOnScrollListener;
57 
58     @Override
onAttach(Context context)59     public void onAttach(Context context) {
60         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
61         // Mixin that logs visibility change for activity.
62         mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(),
63                 mMetricsFeatureProvider);
64         getSettingsLifecycle().addObserver(mVisibilityLoggerMixin);
65         getSettingsLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName()));
66         super.onAttach(context);
67     }
68 
69     @Override
onStart()70     public void onStart() {
71         super.onStart();
72         // Override the OnPreferenceTreeClickListener in super.onStart() to inject jank detection.
73         getPreferenceManager().setOnPreferenceTreeClickListener((preference) -> {
74             if (preference instanceof SwitchPreference) {
75                 SettingsJankMonitor.detectSwitchPreferenceClickJank(
76                         getListView(), (SwitchPreference) preference);
77             }
78             return onPreferenceTreeClick(preference);
79         });
80     }
81 
82     @Override
onResume()83     public void onResume() {
84         mVisibilityLoggerMixin.setSourceMetricsCategory(getActivity());
85         // Add scroll listener to trace interaction jank.
86         final RecyclerView recyclerView = getListView();
87         if (recyclerView != null) {
88             mOnScrollListener = new OnScrollListener(getClass().getName());
89             recyclerView.addOnScrollListener(mOnScrollListener);
90         }
91         super.onResume();
92     }
93 
94     @Override
onPause()95     public void onPause() {
96         final RecyclerView recyclerView = getListView();
97         if (mOnScrollListener != null) {
98             recyclerView.removeOnScrollListener(mOnScrollListener);
99             mOnScrollListener = null;
100         }
101         super.onPause();
102     }
103 
104     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)105     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
106         final int resId = getPreferenceScreenResId();
107         if (resId > 0) {
108             addPreferencesFromResource(resId);
109         }
110     }
111 
112     @Override
addPreferencesFromResource(@mlRes int preferencesResId)113     public void addPreferencesFromResource(@XmlRes int preferencesResId) {
114         super.addPreferencesFromResource(preferencesResId);
115         updateActivityTitleWithScreenTitle(getPreferenceScreen());
116     }
117 
118     @Override
findPreference(CharSequence key)119     public <T extends Preference> T findPreference(CharSequence key) {
120         if (key == null) {
121             return null;
122         }
123         return super.findPreference(key);
124     }
125 
126     @Override
onPreferenceTreeClick(Preference preference)127     public boolean onPreferenceTreeClick(Preference preference) {
128         writePreferenceClickMetric(preference);
129         return super.onPreferenceTreeClick(preference);
130     }
131 
getPrefContext()132     protected final Context getPrefContext() {
133         return getPreferenceManager().getContext();
134     }
135 
136     /**
137      * Get the res id for static preference xml for this fragment.
138      */
getPreferenceScreenResId()139     protected int getPreferenceScreenResId() {
140         return -1;
141     }
142 
writeElapsedTimeMetric(int action, String key)143     protected void writeElapsedTimeMetric(int action, String key) {
144         mVisibilityLoggerMixin.writeElapsedTimeMetric(action, key);
145     }
146 
writePreferenceClickMetric(Preference preference)147     protected void writePreferenceClickMetric(Preference preference) {
148         mMetricsFeatureProvider.logClickedPreference(preference, getMetricsCategory());
149     }
150 
updateActivityTitleWithScreenTitle(PreferenceScreen screen)151     private void updateActivityTitleWithScreenTitle(PreferenceScreen screen) {
152         if (screen != null) {
153             final CharSequence title = screen.getTitle();
154             if (!TextUtils.isEmpty(title)) {
155                 getActivity().setTitle(title);
156             } else {
157                 Log.w(TAG, "Screen title missing for fragment " + this.getClass().getName());
158             }
159         }
160     }
161 
162     private static final class OnScrollListener extends RecyclerView.OnScrollListener {
163         private final InteractionJankMonitor mMonitor = InteractionJankMonitor.getInstance();
164         private final String mClassName;
165 
OnScrollListener(String className)166         private OnScrollListener(String className) {
167             mClassName = className;
168         }
169 
170         @Override
onScrollStateChanged(RecyclerView recyclerView, int newState)171         public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
172             switch (newState) {
173                 case RecyclerView.SCROLL_STATE_DRAGGING:
174                     final Configuration.Builder builder =
175                             Configuration.Builder.withView(CUJ_SETTINGS_PAGE_SCROLL, recyclerView)
176                                     .setTag(mClassName);
177                     mMonitor.begin(builder);
178                     break;
179                 case RecyclerView.SCROLL_STATE_IDLE:
180                     mMonitor.end(CUJ_SETTINGS_PAGE_SCROLL);
181                     break;
182                 default:
183             }
184         }
185     }
186 }
187