• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.traceur;
18 
19 import android.annotation.Nullable;
20 import android.app.AlertDialog;
21 import android.content.ActivityNotFoundException;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.SharedPreferences;
28 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
29 import android.content.pm.PackageManager;
30 import android.icu.text.MessageFormat;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.view.LayoutInflater;
34 import android.view.Menu;
35 import android.view.MenuInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.Toast;
39 import androidx.preference.ListPreference;
40 import androidx.preference.MultiSelectListPreference;
41 import androidx.preference.Preference;
42 import androidx.preference.PreferenceFragment;
43 import androidx.preference.PreferenceManager;
44 import androidx.preference.SwitchPreference;
45 
46 import com.android.settingslib.HelpUtils;
47 
48 import java.util.ArrayList;
49 import java.util.HashMap;
50 import java.util.Locale;
51 import java.util.Map;
52 import java.util.Map.Entry;
53 import java.util.Set;
54 import java.util.TreeMap;
55 
56 public class MainFragment extends PreferenceFragment {
57 
58     static final String TAG = TraceUtils.TAG;
59 
60     public static final String ACTION_REFRESH_TAGS = "com.android.traceur.REFRESH_TAGS";
61 
62     private static final String BETTERBUG_PACKAGE_NAME =
63             "com.google.android.apps.internal.betterbug";
64 
65     private static final String ROOT_MIME_TYPE = "vnd.android.document/root";
66     private static final String STORAGE_URI = "content://com.android.traceur.documents/root";
67 
68     private SwitchPreference mTracingOn;
69     private SwitchPreference mStackSamplingOn;
70 
71     private AlertDialog mAlertDialog;
72     private SharedPreferences mPrefs;
73 
74     private MultiSelectListPreference mTags;
75 
76     private boolean mRefreshing;
77 
78     private BroadcastReceiver mRefreshReceiver;
79 
80     OnSharedPreferenceChangeListener mSharedPreferenceChangeListener =
81         new OnSharedPreferenceChangeListener () {
82               public void onSharedPreferenceChanged(
83                       SharedPreferences sharedPreferences, String key) {
84                   refreshUi();
85               }
86         };
87 
88     @Override
onCreate(@ullable Bundle savedInstanceState)89     public void onCreate(@Nullable Bundle savedInstanceState) {
90         super.onCreate(savedInstanceState);
91 
92         Receiver.updateDeveloperOptionsWatcher(getContext(), /* fromBootIntent */ false);
93 
94         mPrefs = PreferenceManager.getDefaultSharedPreferences(
95                 getActivity().getApplicationContext());
96 
97         mTracingOn = (SwitchPreference) findPreference(getActivity().getString(R.string.pref_key_tracing_on));
98         mStackSamplingOn = (SwitchPreference) findPreference(
99                 getActivity().getString(R.string.pref_key_stack_sampling_on));
100 
101         mTracingOn.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
102             @Override
103             public boolean onPreferenceClick(Preference preference) {
104                 Receiver.updateTracing(getContext());
105                 // Immediately disable the stack sampling toggle if the trace toggle is enabled.
106                 mStackSamplingOn.setEnabled(
107                         ((SwitchPreference) preference).isChecked() ? false : true);
108                 return true;
109             }
110         });
111 
112         mStackSamplingOn.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
113             @Override
114             public boolean onPreferenceClick(Preference preference) {
115                 Receiver.updateTracing(getContext());
116                 // Immediately disable the trace toggle if the stack sampling toggle is enabled.
117                 mTracingOn.setEnabled(
118                         ((SwitchPreference) preference).isChecked() ? false : true);
119                 return true;
120             }
121         });
122 
123 
124         mTags = (MultiSelectListPreference) findPreference(getContext().getString(R.string.pref_key_tags));
125         mTags.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
126             @Override
127             public boolean onPreferenceChange(Preference preference, Object newValue) {
128                 if (mRefreshing) {
129                     return true;
130                 }
131                 Set<String> set = (Set<String>) newValue;
132                 TreeMap<String, String> available = TraceUtils.listCategories();
133                 ArrayList<String> clean = new ArrayList<>(set.size());
134 
135                 for (String s : set) {
136                     if (available.containsKey(s)) {
137                         clean.add(s);
138                     }
139                 }
140                 set.clear();
141                 set.addAll(clean);
142                 return true;
143             }
144         });
145 
146         findPreference("restore_default_tags").setOnPreferenceClickListener(
147                 new Preference.OnPreferenceClickListener() {
148                     @Override
149                     public boolean onPreferenceClick(Preference preference) {
150                         refreshUi(/* restoreDefaultTags =*/ true);
151                         Toast.makeText(getContext(),
152                             getContext().getString(R.string.default_categories_restored),
153                                 Toast.LENGTH_SHORT).show();
154                         return true;
155                     }
156                 });
157 
158         findPreference(getString(R.string.pref_key_quick_setting))
159             .setOnPreferenceClickListener(
160                 new Preference.OnPreferenceClickListener() {
161                     @Override
162                     public boolean onPreferenceClick(Preference preference) {
163                         Receiver.updateQuickSettings(getContext());
164                         return true;
165                     }
166                 });
167 
168         findPreference("clear_saved_files").setOnPreferenceClickListener(
169                 new Preference.OnPreferenceClickListener() {
170                     @Override
171                     public boolean onPreferenceClick(Preference preference) {
172                         new AlertDialog.Builder(getContext())
173                             .setTitle(R.string.clear_saved_files_question)
174                             .setMessage(R.string.all_recordings_will_be_deleted)
175                             .setPositiveButton(R.string.clear,
176                                 new DialogInterface.OnClickListener() {
177                                     public void onClick(DialogInterface dialog, int which) {
178                                         TraceUtils.clearSavedTraces();
179                                     }
180                                 })
181                             .setNegativeButton(android.R.string.cancel,
182                                 new DialogInterface.OnClickListener() {
183                                     public void onClick(DialogInterface dialog, int which) {
184                                         dialog.dismiss();
185                                     }
186                                 })
187                             .create()
188                             .show();
189                         return true;
190                     }
191                 });
192 
193         findPreference("trace_link_button")
194             .setOnPreferenceClickListener(
195                 new Preference.OnPreferenceClickListener() {
196                     @Override
197                     public boolean onPreferenceClick(Preference preference) {
198                         Intent intent = buildTraceFileViewIntent();
199                         try {
200                             startActivity(intent);
201                         } catch (ActivityNotFoundException e) {
202                             return false;
203                         }
204                         return true;
205                     }
206                 });
207 
208         // This disables "Attach to bugreports" when long traces are enabled. This cannot be done in
209         // main.xml because there are some other settings there that are enabled with long traces.
210         SwitchPreference attachToBugreport = findPreference(
211             getString(R.string.pref_key_attach_to_bugreport));
212         findPreference(getString(R.string.pref_key_long_traces))
213             .setOnPreferenceClickListener(
214                 new Preference.OnPreferenceClickListener() {
215                     @Override
216                     public boolean onPreferenceClick(Preference preference) {
217                         if (((SwitchPreference) preference).isChecked()) {
218                             attachToBugreport.setEnabled(false);
219                         } else {
220                             attachToBugreport.setEnabled(true);
221                         }
222                         return true;
223                     }
224                 });
225 
226         refreshUi();
227 
228         mRefreshReceiver = new BroadcastReceiver() {
229             @Override
230             public void onReceive(Context context, Intent intent) {
231                 refreshUi();
232             }
233         };
234 
235     }
236 
237     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)238     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
239         setHasOptionsMenu(true);
240         return super.onCreateView(inflater, container, savedInstanceState);
241     }
242 
243     @Override
onStart()244     public void onStart() {
245         super.onStart();
246         getPreferenceScreen().getSharedPreferences()
247             .registerOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
248         getActivity().registerReceiver(mRefreshReceiver, new IntentFilter(ACTION_REFRESH_TAGS),
249                 Context.RECEIVER_NOT_EXPORTED);
250         Receiver.updateTracing(getContext());
251     }
252 
253     @Override
onStop()254     public void onStop() {
255         getPreferenceScreen().getSharedPreferences()
256             .unregisterOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
257         getActivity().unregisterReceiver(mRefreshReceiver);
258 
259         if (mAlertDialog != null) {
260             mAlertDialog.cancel();
261             mAlertDialog = null;
262         }
263 
264         super.onStop();
265     }
266 
267     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)268     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
269         addPreferencesFromResource(R.xml.main);
270     }
271 
272     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)273     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
274         HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_url,
275             this.getClass().getName());
276     }
277 
buildTraceFileViewIntent()278     private Intent buildTraceFileViewIntent() {
279         Intent intent = new Intent(Intent.ACTION_VIEW);
280         intent.setDataAndType(Uri.parse(STORAGE_URI), ROOT_MIME_TYPE);
281         return intent;
282     }
283 
refreshUi()284     private void refreshUi() {
285         refreshUi(/* restoreDefaultTags =*/ false);
286     }
287 
288     /*
289      * Refresh the preferences UI to make sure it reflects the current state of the preferences and
290      * system.
291      */
refreshUi(boolean restoreDefaultTags)292     private void refreshUi(boolean restoreDefaultTags) {
293         Context context = getContext();
294 
295         // Make sure the Record trace and Record CPU profile toggles match their preference values.
296         mTracingOn.setChecked(mPrefs.getBoolean(mTracingOn.getKey(), false));
297         mStackSamplingOn.setChecked(mPrefs.getBoolean(mStackSamplingOn.getKey(), false));
298 
299         // Enable or disable each toggle based on the state of the other. This path exists in case
300         // the tracing state was updated with the QS tile or the ongoing-trace notification, which
301         // would not call the toggles' OnClickListeners.
302         mTracingOn.setEnabled(mStackSamplingOn.isChecked() ? false : true);
303         mStackSamplingOn.setEnabled(mTracingOn.isChecked() ? false : true);
304 
305         SwitchPreference stopOnReport =
306                 (SwitchPreference) findPreference(getString(R.string.pref_key_stop_on_bugreport));
307         stopOnReport.setChecked(mPrefs.getBoolean(stopOnReport.getKey(), false));
308 
309         // Update category list to match the categories available on the system.
310         Set<Entry<String, String>> availableTags = TraceUtils.listCategories().entrySet();
311         ArrayList<String> entries = new ArrayList<String>(availableTags.size());
312         ArrayList<String> values = new ArrayList<String>(availableTags.size());
313         for (Entry<String, String> entry : availableTags) {
314             entries.add(entry.getKey() + ": " + entry.getValue());
315             values.add(entry.getKey());
316         }
317 
318         mRefreshing = true;
319         try {
320             mTags.setEntries(entries.toArray(new String[0]));
321             mTags.setEntryValues(values.toArray(new String[0]));
322             if (restoreDefaultTags || !mPrefs.contains(context.getString(R.string.pref_key_tags))) {
323                 mTags.setValues(Receiver.getDefaultTagList());
324             }
325         } finally {
326             mRefreshing = false;
327         }
328 
329         // Update subtitles on this screen.
330         Set<String> categories = mTags.getValues();
331         MessageFormat msgFormat = new MessageFormat(
332                 getResources().getString(R.string.num_categories_selected),
333                 Locale.getDefault());
334         Map<String, Object> arguments = new HashMap<>();
335         arguments.put("count", categories.size());
336         mTags.setSummary(Receiver.getDefaultTagList().equals(categories)
337                          ? context.getString(R.string.default_categories)
338                          : msgFormat.format(arguments));
339 
340         ListPreference bufferSize = (ListPreference)findPreference(
341                 context.getString(R.string.pref_key_buffer_size));
342         bufferSize.setSummary(bufferSize.getEntry());
343 
344         ListPreference maxLongTraceSize = (ListPreference)findPreference(
345                 context.getString(R.string.pref_key_max_long_trace_size));
346         maxLongTraceSize.setSummary(maxLongTraceSize.getEntry());
347 
348         ListPreference maxLongTraceDuration = (ListPreference)findPreference(
349                 context.getString(R.string.pref_key_max_long_trace_duration));
350         maxLongTraceDuration.setSummary(maxLongTraceDuration.getEntry());
351 
352         // Check if BetterBug is installed to see if Traceur should display either the toggle for
353         // 'attach_to_bugreport' or 'stop_on_bugreport'.
354         try {
355             context.getPackageManager().getPackageInfo(BETTERBUG_PACKAGE_NAME,
356                     PackageManager.MATCH_SYSTEM_ONLY);
357             findPreference(getString(R.string.pref_key_attach_to_bugreport)).setVisible(true);
358             findPreference(getString(R.string.pref_key_stop_on_bugreport)).setVisible(false);
359             // Changes the long traces summary to add that they cannot be attached to bugreports.
360             findPreference(getString(R.string.pref_key_long_traces))
361                     .setSummary(getString(R.string.long_traces_summary_betterbug));
362         } catch (PackageManager.NameNotFoundException e) {
363             // attach_to_bugreport must be disabled here because it's true by default.
364             mPrefs.edit().putBoolean(
365                     getString(R.string.pref_key_attach_to_bugreport), false).commit();
366             findPreference(getString(R.string.pref_key_attach_to_bugreport)).setVisible(false);
367             findPreference(getString(R.string.pref_key_stop_on_bugreport)).setVisible(true);
368             // Sets long traces summary to the default in case Betterbug was removed.
369             findPreference(getString(R.string.pref_key_long_traces))
370                     .setSummary(getString(R.string.long_traces_summary));
371         }
372 
373         // Check if an activity exists to handle the trace_link_button intent. If not, hide the UI
374         // element
375         PackageManager packageManager = context.getPackageManager();
376         Intent intent = buildTraceFileViewIntent();
377         if (intent.resolveActivity(packageManager) == null) {
378             findPreference("trace_link_button").setVisible(false);
379         }
380     }
381 }
382