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.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageManager;
28 import android.content.SharedPreferences;
29 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
30 import android.os.Build;
31 import android.os.Bundle;
32 import androidx.preference.MultiSelectListPreference;
33 import androidx.preference.ListPreference;
34 import androidx.preference.Preference;
35 import androidx.preference.PreferenceFragment;
36 import androidx.preference.PreferenceManager;
37 import androidx.preference.PreferenceScreen;
38 import androidx.preference.SwitchPreference;
39 import android.view.LayoutInflater;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.view.Menu;
43 import android.view.MenuInflater;
44 import android.widget.Toast;
45 
46 import com.android.settingslib.HelpUtils;
47 
48 import java.util.ArrayList;
49 import java.util.Collections;
50 import java.util.Comparator;
51 import java.util.Iterator;
52 import java.util.List;
53 import java.util.Map.Entry;
54 import java.util.Set;
55 import java.util.TreeMap;
56 
57 public class MainFragment extends PreferenceFragment {
58 
59     static final String TAG = TraceUtils.TAG;
60 
61     public static final String ACTION_REFRESH_TAGS = "com.android.traceur.REFRESH_TAGS";
62 
63     private SwitchPreference mTracingOn;
64 
65     private AlertDialog mAlertDialog;
66     private SharedPreferences mPrefs;
67 
68     private MultiSelectListPreference mTags;
69 
70     private boolean mRefreshing;
71 
72     private BroadcastReceiver mRefreshReceiver;
73 
74     OnSharedPreferenceChangeListener mSharedPreferenceChangeListener =
75         new OnSharedPreferenceChangeListener () {
76               public void onSharedPreferenceChanged(
77                       SharedPreferences sharedPreferences, String key) {
78                   refreshUi();
79               }
80         };
81 
82     @Override
onCreate(@ullable Bundle savedInstanceState)83     public void onCreate(@Nullable Bundle savedInstanceState) {
84         super.onCreate(savedInstanceState);
85 
86         Receiver.updateDeveloperOptionsWatcher(getContext());
87 
88         mPrefs = PreferenceManager.getDefaultSharedPreferences(
89                 getActivity().getApplicationContext());
90 
91         mTracingOn = (SwitchPreference) findPreference(getActivity().getString(R.string.pref_key_tracing_on));
92         mTracingOn.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
93             @Override
94             public boolean onPreferenceClick(Preference preference) {
95               Receiver.updateTracing(getContext());
96               return true;
97             }
98         });
99 
100         mTags = (MultiSelectListPreference) findPreference(getContext().getString(R.string.pref_key_tags));
101         mTags.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
102             @Override
103             public boolean onPreferenceChange(Preference preference, Object newValue) {
104                 if (mRefreshing) {
105                     return true;
106                 }
107                 Set<String> set = (Set<String>) newValue;
108                 TreeMap<String, String> available = TraceUtils.listCategories();
109                 ArrayList<String> clean = new ArrayList<>(set.size());
110 
111                 for (String s : set) {
112                     if (available.containsKey(s)) {
113                         clean.add(s);
114                     }
115                 }
116                 set.clear();
117                 set.addAll(clean);
118                 return true;
119             }
120         });
121 
122         findPreference("restore_default_tags").setOnPreferenceClickListener(
123                 new Preference.OnPreferenceClickListener() {
124                     @Override
125                     public boolean onPreferenceClick(Preference preference) {
126                         refreshUi(/* restoreDefaultTags =*/ true);
127                         Toast.makeText(getContext(),
128                             getContext().getString(R.string.default_categories_restored),
129                                 Toast.LENGTH_SHORT).show();
130                         return true;
131                     }
132                 });
133 
134         findPreference(getString(R.string.pref_key_quick_setting))
135             .setOnPreferenceClickListener(
136                 new Preference.OnPreferenceClickListener() {
137                     @Override
138                     public boolean onPreferenceClick(Preference preference) {
139                         Receiver.updateQuickSettings(getContext());
140                         return true;
141                     }
142                 });
143 
144         findPreference("clear_saved_traces").setOnPreferenceClickListener(
145                 new Preference.OnPreferenceClickListener() {
146                     @Override
147                     public boolean onPreferenceClick(Preference preference) {
148                         new AlertDialog.Builder(getContext())
149                             .setTitle(R.string.clear_saved_traces_question)
150                             .setMessage(R.string.all_traces_will_be_deleted)
151                             .setPositiveButton(R.string.clear,
152                                 new DialogInterface.OnClickListener() {
153                                     public void onClick(DialogInterface dialog, int which) {
154                                         TraceUtils.clearSavedTraces();
155                                     }
156                                 })
157                             .setNegativeButton(android.R.string.no,
158                                 new DialogInterface.OnClickListener() {
159                                     public void onClick(DialogInterface dialog, int which) {
160                                         dialog.dismiss();
161                                     }
162                                 })
163                             .create()
164                             .show();
165                         return true;
166                     }
167                 });
168 
169         refreshUi();
170 
171         mRefreshReceiver = new BroadcastReceiver() {
172             @Override
173             public void onReceive(Context context, Intent intent) {
174                 refreshUi();
175             }
176         };
177 
178     }
179 
180     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)181     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
182         setHasOptionsMenu(true);
183         return super.onCreateView(inflater, container, savedInstanceState);
184     }
185 
186     @Override
onStart()187     public void onStart() {
188         super.onStart();
189         getPreferenceScreen().getSharedPreferences()
190             .registerOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
191         getActivity().registerReceiver(mRefreshReceiver, new IntentFilter(ACTION_REFRESH_TAGS));
192         Receiver.updateTracing(getContext());
193     }
194 
195     @Override
onStop()196     public void onStop() {
197         getPreferenceScreen().getSharedPreferences()
198             .unregisterOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
199         getActivity().unregisterReceiver(mRefreshReceiver);
200 
201         if (mAlertDialog != null) {
202             mAlertDialog.cancel();
203             mAlertDialog = null;
204         }
205 
206         super.onStop();
207     }
208 
209     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)210     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
211         addPreferencesFromResource(R.xml.main);
212     }
213 
214     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)215     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
216         HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_url,
217             this.getClass().getName());
218     }
219 
refreshUi()220     private void refreshUi() {
221         refreshUi(/* restoreDefaultTags =*/ false);
222     }
223 
224     /*
225      * Refresh the preferences UI to make sure it reflects the current state of the preferences and
226      * system.
227      */
refreshUi(boolean restoreDefaultTags)228     private void refreshUi(boolean restoreDefaultTags) {
229         Context context = getContext();
230 
231         // Make sure the Record Trace toggle matches the preference value.
232         mTracingOn.setChecked(mTracingOn.getPreferenceManager().getSharedPreferences().getBoolean(
233                 mTracingOn.getKey(), false));
234 
235         // Update category list to match the categories available on the system.
236         Set<Entry<String, String>> availableTags = TraceUtils.listCategories().entrySet();
237         ArrayList<String> entries = new ArrayList<String>(availableTags.size());
238         ArrayList<String> values = new ArrayList<String>(availableTags.size());
239         for (Entry<String, String> entry : availableTags) {
240             entries.add(entry.getKey() + ": " + entry.getValue());
241             values.add(entry.getKey());
242         }
243 
244         mRefreshing = true;
245         try {
246             mTags.setEntries(entries.toArray(new String[0]));
247             mTags.setEntryValues(values.toArray(new String[0]));
248             if (restoreDefaultTags || !mPrefs.contains(context.getString(R.string.pref_key_tags))) {
249                 mTags.setValues(Receiver.getDefaultTagList());
250             }
251         } finally {
252             mRefreshing = false;
253         }
254 
255         // Update subtitles on this screen.
256         Set<String> categories = mTags.getValues();
257         mTags.setSummary(Receiver.getDefaultTagList().equals(categories)
258                          ? context.getString(R.string.default_categories)
259                          : context.getResources().getQuantityString(R.plurals.num_categories_selected,
260                               categories.size(), categories.size()));
261 
262         ListPreference bufferSize = (ListPreference)findPreference(
263                 context.getString(R.string.pref_key_buffer_size));
264         bufferSize.setSummary(bufferSize.getEntry());
265 
266         // If we are not using the Perfetto trace backend,
267         // hide the unsupported preferences.
268         if (TraceUtils.currentTraceEngine().equals(PerfettoUtils.NAME)) {
269             ListPreference maxLongTraceSize = (ListPreference)findPreference(
270                     context.getString(R.string.pref_key_max_long_trace_size));
271             maxLongTraceSize.setSummary(maxLongTraceSize.getEntry());
272 
273             ListPreference maxLongTraceDuration = (ListPreference)findPreference(
274                     context.getString(R.string.pref_key_max_long_trace_duration));
275             maxLongTraceDuration.setSummary(maxLongTraceDuration.getEntry());
276         } else {
277             Preference longTraceCategory = findPreference("long_trace_category");
278             if (longTraceCategory != null) {
279                 getPreferenceScreen().removePreference(longTraceCategory);
280             }
281         }
282     }
283 }
284