blob: 02b80888f18ba419134446dde9c43e4f7ea22b61 [file] [log] [blame]
Roberto Perez004455c2019-08-23 17:18:37 -07001/*
2 * Copyright (C) 2019 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 */
Roberto Perezfeec33c2019-09-19 16:23:22 -070016
Brad Stenning298e58b2019-09-12 15:29:54 -070017package com.android.car.ui.paintbooth;
Roberto Perez004455c2019-08-23 17:18:37 -070018
19import android.app.Activity;
Cole Faustbdbee872019-12-06 12:47:08 -080020import android.app.ActivityManager;
Cole Faustf8d7c742020-04-01 16:02:52 -070021import android.app.Service;
Cole Faustbdbee872019-12-06 12:47:08 -080022import android.content.Context;
Roberto Perez004455c2019-08-23 17:18:37 -070023import android.content.Intent;
24import android.os.Bundle;
Cole Fauste2644762019-12-06 10:56:43 -080025import android.util.Log;
Roberto Perezfcbe6f52019-09-11 08:13:12 -070026import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
Roberto Perez004455c2019-08-23 17:18:37 -070029import android.widget.Button;
Cole Faustbdbee872019-12-06 12:47:08 -080030import android.widget.Switch;
Cole Fauste2644762019-12-06 10:56:43 -080031import android.widget.Toast;
Roberto Perez004455c2019-08-23 17:18:37 -070032
Roberto Perezfcbe6f52019-09-11 08:13:12 -070033import androidx.annotation.NonNull;
34import androidx.recyclerview.widget.RecyclerView;
35
Cole Faustb30c3882020-02-25 14:26:50 -080036import com.android.car.ui.baselayout.Insets;
37import com.android.car.ui.baselayout.InsetsChangedListener;
38import com.android.car.ui.core.CarUi;
Ram Parameswaranfa84da12019-10-31 09:57:35 -070039import com.android.car.ui.paintbooth.caruirecyclerview.CarUiListItemActivity;
Priyank Singh7361b4b2019-10-04 11:24:10 -070040import com.android.car.ui.paintbooth.caruirecyclerview.CarUiRecyclerViewActivity;
41import com.android.car.ui.paintbooth.caruirecyclerview.GridCarUiRecyclerViewActivity;
Cole Faust05034242019-12-23 14:40:37 -080042import com.android.car.ui.paintbooth.currentactivity.CurrentActivityService;
Roberto Perezfeec33c2019-09-19 16:23:22 -070043import com.android.car.ui.paintbooth.dialogs.DialogsActivity;
Roberto Perez638196f2019-10-11 17:24:34 -070044import com.android.car.ui.paintbooth.overlays.OverlayActivity;
Roberto Perezfeec33c2019-09-19 16:23:22 -070045import com.android.car.ui.paintbooth.preferences.PreferenceActivity;
Priyank Singh36bd38d2020-03-20 16:50:12 -070046import com.android.car.ui.paintbooth.toolbar.NoCarUiToolbarActivity;
Cole Faust92f30662020-05-21 12:12:22 -070047import com.android.car.ui.paintbooth.toolbar.OldToolbarActivity;
Cole Faustf87980d2019-09-20 10:23:22 -070048import com.android.car.ui.paintbooth.toolbar.ToolbarActivity;
Priyank Singh440572d2019-10-15 09:56:59 -070049import com.android.car.ui.paintbooth.widgets.WidgetActivity;
Priyank Singh7361b4b2019-10-04 11:24:10 -070050import com.android.car.ui.recyclerview.CarUiRecyclerView;
Cole Faustb30c3882020-02-25 14:26:50 -080051import com.android.car.ui.toolbar.ToolbarController;
Roberto Perezfcbe6f52019-09-11 08:13:12 -070052
Cole Fauste2644762019-12-06 10:56:43 -080053import java.lang.reflect.Method;
Roberto Perezfcbe6f52019-09-11 08:13:12 -070054import java.util.Arrays;
55import java.util.List;
56
Roberto Perez004455c2019-08-23 17:18:37 -070057/**
58 * Paint booth app
59 */
Cole Faustb30c3882020-02-25 14:26:50 -080060public class MainActivity extends Activity implements InsetsChangedListener {
Priyank Singhcedf1d22019-10-04 12:34:14 -070061
Cole Faustf8d7c742020-04-01 16:02:52 -070062 public static final String STOP_SERVICE = "com.android.car.ui.paintbooth.StopService";
63
Roberto Perezfcbe6f52019-09-11 08:13:12 -070064 /**
65 * List of all sample activities.
66 */
Cole Faustf8d7c742020-04-01 16:02:52 -070067 private final List<ListElement> mActivities = Arrays.asList(
68 new ServiceElement("Show foreground activities", CurrentActivityService.class),
69 new ServiceElement("Simulate Screen Bounds", VisibleBoundsSimulator.class),
70 new ActivityElement("Dialogs sample", DialogsActivity.class),
71 new ActivityElement("List sample", CarUiRecyclerViewActivity.class),
72 new ActivityElement("Grid sample", GridCarUiRecyclerViewActivity.class),
73 new ActivityElement("Preferences sample", PreferenceActivity.class),
74 new ActivityElement("Overlays", OverlayActivity.class),
75 new ActivityElement("Toolbar sample", ToolbarActivity.class),
Cole Faust92f30662020-05-21 12:12:22 -070076 new ActivityElement("Old toolbar sample", OldToolbarActivity.class),
Cole Faustf8d7c742020-04-01 16:02:52 -070077 new ActivityElement("No CarUiToolbar sample", NoCarUiToolbarActivity.class),
78 new ActivityElement("Widget sample", WidgetActivity.class),
Cole Faust92f30662020-05-21 12:12:22 -070079 new ActivityElement("ListItem sample", CarUiListItemActivity.class));
Cole Faustbdbee872019-12-06 12:47:08 -080080
Cole Faustf8d7c742020-04-01 16:02:52 -070081 private abstract static class ViewHolder extends RecyclerView.ViewHolder {
Roberto Perezfcbe6f52019-09-11 08:13:12 -070082
83 ViewHolder(@NonNull View itemView) {
84 super(itemView);
Cole Faustf8d7c742020-04-01 16:02:52 -070085 }
86
87 public abstract void bind(ListElement element);
88 }
89
90 private class ActivityViewHolder extends ViewHolder {
Cole Faust95e316e2020-04-02 17:09:40 -070091 private final Button mButton;
Cole Faustf8d7c742020-04-01 16:02:52 -070092
93 ActivityViewHolder(@NonNull View itemView) {
94 super(itemView);
Cole Faust95e316e2020-04-02 17:09:40 -070095 mButton = itemView.requireViewById(R.id.button);
Roberto Perezfcbe6f52019-09-11 08:13:12 -070096 }
97
Cole Faustf8d7c742020-04-01 16:02:52 -070098 @Override
99 public void bind(ListElement e) {
100 if (!(e instanceof ActivityElement)) {
101 throw new IllegalArgumentException("Expected an ActivityElement");
Cole Faustbdbee872019-12-06 12:47:08 -0800102 }
Cole Faustf8d7c742020-04-01 16:02:52 -0700103 ActivityElement element = (ActivityElement) e;
104 mButton.setText(element.getText());
105 mButton.setOnClickListener(v ->
106 startActivity(new Intent(itemView.getContext(), element.getActivity())));
107 }
108 }
109
110 private class ServiceViewHolder extends ViewHolder {
Cole Faust95e316e2020-04-02 17:09:40 -0700111 private final Switch mSwitch;
Cole Faustf8d7c742020-04-01 16:02:52 -0700112
113 ServiceViewHolder(@NonNull View itemView) {
114 super(itemView);
Cole Faust95e316e2020-04-02 17:09:40 -0700115 mSwitch = itemView.requireViewById(R.id.button);
Cole Faustf8d7c742020-04-01 16:02:52 -0700116 }
117
118 @Override
119 public void bind(ListElement e) {
120 if (!(e instanceof ServiceElement)) {
121 throw new IllegalArgumentException("Expected an ActivityElement");
122 }
123 ServiceElement element = (ServiceElement) e;
124 mSwitch.setChecked(isServiceRunning(element.getService()));
125 mSwitch.setText(element.getText());
126 mSwitch.setOnClickListener(v -> {
127 Intent intent = new Intent(itemView.getContext(), element.getService());
128 if (isServiceRunning(element.getService())) {
129 intent.setAction(STOP_SERVICE);
130 }
131 startForegroundService(intent);
132 });
Roberto Perezfcbe6f52019-09-11 08:13:12 -0700133 }
134 }
135
Roberto Perez638196f2019-10-11 17:24:34 -0700136 private final RecyclerView.Adapter<ViewHolder> mAdapter =
137 new RecyclerView.Adapter<ViewHolder>() {
Cole Faustbdbee872019-12-06 12:47:08 -0800138 @NonNull
139 @Override
140 public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Cole Faustf8d7c742020-04-01 16:02:52 -0700141 LayoutInflater inflater = LayoutInflater.from(parent.getContext());
142 if (viewType == ListElement.TYPE_ACTIVITY) {
143 return new ActivityViewHolder(
144 inflater.inflate(R.layout.list_item, parent, false));
145 } else if (viewType == ListElement.TYPE_SERVICE) {
146 return new ServiceViewHolder(
147 inflater.inflate(R.layout.list_item_switch, parent, false));
148 } else {
149 throw new IllegalArgumentException("Unknown viewType: " + viewType);
150 }
Cole Faustbdbee872019-12-06 12:47:08 -0800151 }
152
153 @Override
154 public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Cole Faustf8d7c742020-04-01 16:02:52 -0700155 holder.bind(mActivities.get(position));
Cole Faustbdbee872019-12-06 12:47:08 -0800156 }
157
158 @Override
159 public int getItemCount() {
Priyank Singhc2e159b2020-03-27 12:14:48 -0700160 return mActivities.size();
Cole Faustbdbee872019-12-06 12:47:08 -0800161 }
162
163 @Override
164 public int getItemViewType(int position) {
Cole Faustf8d7c742020-04-01 16:02:52 -0700165 return mActivities.get(position).getType();
Cole Faustbdbee872019-12-06 12:47:08 -0800166 }
167 };
Roberto Perezfcbe6f52019-09-11 08:13:12 -0700168
Roberto Perez004455c2019-08-23 17:18:37 -0700169 @Override
170 protected void onCreate(Bundle savedInstanceState) {
171 super.onCreate(savedInstanceState);
Cole Faustb30c3882020-02-25 14:26:50 -0800172 setContentView(R.layout.car_ui_recycler_view_activity);
Roberto Perez004455c2019-08-23 17:18:37 -0700173
Cole Faustb30c3882020-02-25 14:26:50 -0800174 ToolbarController toolbar = CarUi.requireToolbar(this);
175 toolbar.setLogo(R.drawable.ic_launcher);
176 toolbar.setTitle(getTitle());
177
178 CarUiRecyclerView prv = findViewById(R.id.list);
Roberto Perezfcbe6f52019-09-11 08:13:12 -0700179 prv.setAdapter(mAdapter);
Cole Fauste2644762019-12-06 10:56:43 -0800180
181 initLeakCanary();
182 }
183
184 private void initLeakCanary() {
185 // This sets LeakCanary to report errors after a single leak instead of 5, and to ask for
186 // permission to use storage, which it needs to work.
187 //
188 // Equivalent to this non-reflection code:
189 //
190 // Config config = LeakCanary.INSTANCE.getConfig();
191 // LeakCanary.INSTANCE.setConfig(config.copy(config.getDumpHeap(),
192 // config.getDumpHeapWhenDebugging(),
193 // 1,
194 // config.getReferenceMatchers(),
195 // config.getObjectInspectors(),
196 // config.getOnHeapAnalyzedListener(),
197 // config.getMetatadaExtractor(),
198 // config.getComputeRetainedHeapSize(),
199 // config.getMaxStoredHeapDumps(),
200 // true,
201 // config.getUseExperimentalLeakFinders()));
202 try {
203 Class<?> canaryClass = Class.forName("leakcanary.LeakCanary");
204 try {
205 Class<?> onHeapAnalyzedListenerClass =
206 Class.forName("leakcanary.OnHeapAnalyzedListener");
207 Class<?> metadataExtractorClass = Class.forName("shark.MetadataExtractor");
208 Method getConfig = canaryClass.getMethod("getConfig");
209 Class<?> configClass = getConfig.getReturnType();
210 Method setConfig = canaryClass.getMethod("setConfig", configClass);
211 Method copy = configClass.getMethod("copy", boolean.class, boolean.class,
212 int.class, List.class, List.class, onHeapAnalyzedListenerClass,
213 metadataExtractorClass, boolean.class, int.class, boolean.class,
214 boolean.class);
215
216 Object canary = canaryClass.getField("INSTANCE").get(null);
217 Object currentConfig = getConfig.invoke(canary);
218
219 Boolean dumpHeap = (Boolean) configClass
220 .getMethod("getDumpHeap").invoke(currentConfig);
221 Boolean dumpHeapWhenDebugging = (Boolean) configClass
222 .getMethod("getDumpHeapWhenDebugging").invoke(currentConfig);
Cole Faustac23e8b2019-12-05 11:33:09 -0800223 List<?> referenceMatchers = (List<?>) configClass
Cole Fauste2644762019-12-06 10:56:43 -0800224 .getMethod("getReferenceMatchers").invoke(currentConfig);
Cole Faustac23e8b2019-12-05 11:33:09 -0800225 List<?> objectInspectors = (List<?>) configClass
Cole Fauste2644762019-12-06 10:56:43 -0800226 .getMethod("getObjectInspectors").invoke(currentConfig);
227 Object onHeapAnalyzedListener = configClass
228 .getMethod("getOnHeapAnalyzedListener").invoke(currentConfig);
229 // Yes, LeakCanary misspelled metadata
230 Object metadataExtractor = configClass
231 .getMethod("getMetatadaExtractor").invoke(currentConfig);
232 Boolean computeRetainedHeapSize = (Boolean) configClass
233 .getMethod("getComputeRetainedHeapSize").invoke(currentConfig);
234 Integer maxStoredHeapDumps = (Integer) configClass
235 .getMethod("getMaxStoredHeapDumps").invoke(currentConfig);
236 Boolean useExperimentalLeakFinders = (Boolean) configClass
237 .getMethod("getUseExperimentalLeakFinders").invoke(currentConfig);
238
239 setConfig.invoke(canary, copy.invoke(currentConfig,
240 dumpHeap,
241 dumpHeapWhenDebugging,
242 1,
243 referenceMatchers,
244 objectInspectors,
245 onHeapAnalyzedListener,
246 metadataExtractor,
247 computeRetainedHeapSize,
248 maxStoredHeapDumps,
249 true,
250 useExperimentalLeakFinders));
251
252 } catch (ReflectiveOperationException e) {
253 Log.e("paintbooth", "Error initializing LeakCanary", e);
254 Toast.makeText(this, "Error initializing LeakCanary", Toast.LENGTH_LONG).show();
255 }
256 } catch (ClassNotFoundException e) {
257 // LeakCanary is not used in this build, do nothing.
258 }
Roberto Perez004455c2019-08-23 17:18:37 -0700259 }
Cole Faustbdbee872019-12-06 12:47:08 -0800260
Cole Faustf8d7c742020-04-01 16:02:52 -0700261 private boolean isServiceRunning(Class<? extends Service> serviceClazz) {
Cole Faustbdbee872019-12-06 12:47:08 -0800262 ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
263 for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
264 Integer.MAX_VALUE)) {
Priyank Singhc2e159b2020-03-27 12:14:48 -0700265 if (serviceClazz.getName().equals(service.service.getClassName())) {
Cole Faustbdbee872019-12-06 12:47:08 -0800266 return true;
267 }
268 }
269 return false;
270 }
Cole Faustb30c3882020-02-25 14:26:50 -0800271
272 @Override
Ram Parameswarand2f51b72020-06-05 14:30:56 -0700273 public void onCarUiInsetsChanged(@NonNull Insets insets) {
Cole Faustb30c3882020-02-25 14:26:50 -0800274 requireViewById(R.id.list)
275 .setPadding(0, insets.getTop(), 0, insets.getBottom());
276 requireViewById(android.R.id.content)
277 .setPadding(insets.getLeft(), 0, insets.getRight(), 0);
278 }
Priyank Singhc2e159b2020-03-27 12:14:48 -0700279
Cole Faustf8d7c742020-04-01 16:02:52 -0700280 private abstract static class ListElement {
281 static final int TYPE_ACTIVITY = 0;
282 static final int TYPE_SERVICE = 1;
Priyank Singhc2e159b2020-03-27 12:14:48 -0700283
Cole Faustf8d7c742020-04-01 16:02:52 -0700284 private final String mText;
285
286 ListElement(String text) {
287 mText = text;
288 }
289
290 String getText() {
291 return mText;
292 }
293
294 abstract int getType();
295 }
296
297 private static class ActivityElement extends ListElement {
298 private final Class<? extends Activity> mActivityClass;
299
300 ActivityElement(String text, Class<? extends Activity> activityClass) {
301 super(text);
302 mActivityClass = activityClass;
303 }
304
305 Class<? extends Activity> getActivity() {
306 return mActivityClass;
307 }
308
309 @Override
310 int getType() {
311 return TYPE_ACTIVITY;
312 }
313 }
314
315 private static class ServiceElement extends ListElement {
316 private final Class<? extends Service> mServiceClass;
317
318 ServiceElement(String text, Class<? extends Service> serviceClass) {
319 super(text);
320 mServiceClass = serviceClass;
321 }
322
323 Class<? extends Service> getService() {
324 return mServiceClass;
325 }
326
327 @Override
328 int getType() {
329 return TYPE_SERVICE;
Priyank Singhc2e159b2020-03-27 12:14:48 -0700330 }
331 }
Roberto Perez004455c2019-08-23 17:18:37 -0700332}