• 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.google.android.car.kitchensink;
18 
19 import android.car.Car;
20 import android.car.CarAppFocusManager;
21 import android.car.CarProjectionManager;
22 import android.car.hardware.CarSensorManager;
23 import android.car.hardware.hvac.CarHvacManager;
24 import android.car.hardware.power.CarPowerManager;
25 import android.car.hardware.property.CarPropertyManager;
26 import android.car.telemetry.CarTelemetryManager;
27 import android.car.watchdog.CarWatchdogManager;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.PackageManager;
31 import android.os.AsyncTask;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.util.Log;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.Button;
39 import android.widget.TextView;
40 
41 import androidx.fragment.app.Fragment;
42 import androidx.fragment.app.FragmentActivity;
43 import androidx.recyclerview.widget.GridLayoutManager;
44 import androidx.recyclerview.widget.RecyclerView;
45 
46 import com.google.android.car.kitchensink.activityresolver.ActivityResolverFragment;
47 import com.google.android.car.kitchensink.admin.DevicePolicyFragment;
48 import com.google.android.car.kitchensink.alertdialog.AlertDialogTestFragment;
49 import com.google.android.car.kitchensink.assistant.CarAssistantFragment;
50 import com.google.android.car.kitchensink.audio.AudioTestFragment;
51 import com.google.android.car.kitchensink.audio.CarAudioInputTestFragment;
52 import com.google.android.car.kitchensink.audiorecorder.AudioRecorderTestFragment;
53 import com.google.android.car.kitchensink.backup.BackupAndRestoreFragment;
54 import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment;
55 import com.google.android.car.kitchensink.bluetooth.BluetoothUuidFragment;
56 import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
57 import com.google.android.car.kitchensink.carboard.KeyboardTestFragment;
58 import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment;
59 import com.google.android.car.kitchensink.connectivity.ConnectivityFragment;
60 import com.google.android.car.kitchensink.cube.CubesTestFragment;
61 import com.google.android.car.kitchensink.diagnostic.DiagnosticTestFragment;
62 import com.google.android.car.kitchensink.displayinfo.DisplayInfoFragment;
63 import com.google.android.car.kitchensink.experimental.ExperimentalFeatureTestFragment;
64 import com.google.android.car.kitchensink.hvac.HvacTestFragment;
65 import com.google.android.car.kitchensink.insets.WindowInsetsFullScreenFragment;
66 import com.google.android.car.kitchensink.mainline.CarMainlineFragment;
67 import com.google.android.car.kitchensink.notification.NotificationFragment;
68 import com.google.android.car.kitchensink.orientation.OrientationTestFragment;
69 import com.google.android.car.kitchensink.packageinfo.PackageInfoFragment;
70 import com.google.android.car.kitchensink.power.PowerTestFragment;
71 import com.google.android.car.kitchensink.projection.ProjectionFragment;
72 import com.google.android.car.kitchensink.property.PropertyTestFragment;
73 import com.google.android.car.kitchensink.qc.QCViewerFragment;
74 import com.google.android.car.kitchensink.rotary.RotaryFragment;
75 import com.google.android.car.kitchensink.sensor.SensorsTestFragment;
76 import com.google.android.car.kitchensink.storagelifetime.StorageLifetimeFragment;
77 import com.google.android.car.kitchensink.storagevolumes.StorageVolumesFragment;
78 import com.google.android.car.kitchensink.systembars.SystemBarsFragment;
79 import com.google.android.car.kitchensink.systemfeatures.SystemFeaturesFragment;
80 import com.google.android.car.kitchensink.telemetry.CarTelemetryTestFragment;
81 import com.google.android.car.kitchensink.touch.TouchTestFragment;
82 import com.google.android.car.kitchensink.users.ProfileUserFragment;
83 import com.google.android.car.kitchensink.users.UserFragment;
84 import com.google.android.car.kitchensink.users.UserRestrictionsFragment;
85 import com.google.android.car.kitchensink.vehiclectrl.VehicleCtrlFragment;
86 import com.google.android.car.kitchensink.volume.VolumeTestFragment;
87 import com.google.android.car.kitchensink.watchdog.CarWatchdogTestFragment;
88 import com.google.android.car.kitchensink.weblinks.WebLinksTestFragment;
89 
90 import java.io.FileDescriptor;
91 import java.io.PrintWriter;
92 import java.util.Arrays;
93 import java.util.Comparator;
94 import java.util.List;
95 
96 public class KitchenSinkActivity extends FragmentActivity {
97     private static final String TAG = "KitchenSinkActivity";
98     private static final String LAST_FRAGMENT_TAG = "lastFragmentTag";
99     private static final String DEFAULT_FRAGMENT_TAG = "";
100     private RecyclerView mMenu;
101     private Button mMenuButton;
102     private View mKitchenContent;
103     private String mLastFragmentTag = DEFAULT_FRAGMENT_TAG;
104 
105     private interface ClickHandler {
onClick()106         void onClick();
107     }
108 
109     private static abstract class MenuEntry implements ClickHandler {
getText()110         abstract String getText();
111     }
112 
113     private final class OnClickMenuEntry extends MenuEntry {
114         private final String mText;
115         private final ClickHandler mClickHandler;
116 
OnClickMenuEntry(String text, ClickHandler clickHandler)117         OnClickMenuEntry(String text, ClickHandler clickHandler) {
118             mText = text;
119             mClickHandler = clickHandler;
120         }
121 
122         @Override
getText()123         String getText() {
124             return mText;
125         }
126 
127         @Override
onClick()128         public void onClick() {
129             toggleMenuVisibility();
130             mClickHandler.onClick();
131         }
132     }
133 
134     private final class FragmentMenuEntry<T extends Fragment> extends MenuEntry {
135         private final class FragmentClassOrInstance<T extends Fragment> {
136             final Class<T> mClazz;
137             T mFragment = null;
138 
FragmentClassOrInstance(Class<T> clazz)139             FragmentClassOrInstance(Class<T> clazz) {
140                 mClazz = clazz;
141             }
142 
getFragment()143             T getFragment() {
144                 if (mFragment == null) {
145                     try {
146                         mFragment = mClazz.newInstance();
147                     } catch (InstantiationException | IllegalAccessException e) {
148                         Log.e(TAG, "unable to create fragment", e);
149                     }
150                 }
151                 return mFragment;
152             }
153         }
154 
155         private final String mText;
156         private final FragmentClassOrInstance<T> mFragment;
157 
FragmentMenuEntry(String text, Class<T> clazz)158         FragmentMenuEntry(String text, Class<T> clazz) {
159             mText = text;
160             mFragment = new FragmentClassOrInstance<>(clazz);
161         }
162 
163         @Override
getText()164         String getText() {
165             return mText;
166         }
167 
168         @Override
onClick()169         public void onClick() {
170             Fragment fragment = mFragment.getFragment();
171             if (fragment != null) {
172                 KitchenSinkActivity.this.showFragment(fragment);
173                 toggleMenuVisibility();
174                 mLastFragmentTag = fragment.getTag();
175             } else {
176                 Log.e(TAG, "cannot show fragment for " + getText());
177             }
178         }
179     }
180 
181     private final List<MenuEntry> mMenuEntries = Arrays.asList(
182             new FragmentMenuEntry("activity resolver", ActivityResolverFragment.class),
183             new FragmentMenuEntry("alert window", AlertDialogTestFragment.class),
184             new FragmentMenuEntry("assistant", CarAssistantFragment.class),
185             new FragmentMenuEntry(AudioTestFragment.FRAGMENT_NAME, AudioTestFragment.class),
186             new FragmentMenuEntry(AudioRecorderTestFragment.FRAGMENT_NAME,
187                     AudioRecorderTestFragment.class),
188             new FragmentMenuEntry(CarAudioInputTestFragment.FRAGMENT_NAME,
189                     CarAudioInputTestFragment.class),
190             new FragmentMenuEntry("B&R", BackupAndRestoreFragment.class),
191             new FragmentMenuEntry("BT headset", BluetoothHeadsetFragment.class),
192             new FragmentMenuEntry("BT messaging", MapMceTestFragment.class),
193             new FragmentMenuEntry("BT Uuids", BluetoothUuidFragment.class),
194             new FragmentMenuEntry("carapi", CarApiTestFragment.class),
195             new FragmentMenuEntry("carboard", KeyboardTestFragment.class),
196             new FragmentMenuEntry("connectivity", ConnectivityFragment.class),
197             new FragmentMenuEntry("cubes test", CubesTestFragment.class),
198             new FragmentMenuEntry("device policy", DevicePolicyFragment.class),
199             new FragmentMenuEntry("diagnostic", DiagnosticTestFragment.class),
200             new FragmentMenuEntry("display info", DisplayInfoFragment.class),
201             new FragmentMenuEntry("experimental feature", ExperimentalFeatureTestFragment.class),
202             new FragmentMenuEntry("hvac", HvacTestFragment.class),
203             new FragmentMenuEntry("inst cluster", InstrumentClusterFragment.class),
204             new FragmentMenuEntry("mainline", CarMainlineFragment.class),
205             new FragmentMenuEntry("notification", NotificationFragment.class),
206             new FragmentMenuEntry("orientation test", OrientationTestFragment.class),
207             new FragmentMenuEntry("package info", PackageInfoFragment.class),
208             new FragmentMenuEntry("power test", PowerTestFragment.class),
209             new FragmentMenuEntry("profile_user", ProfileUserFragment.class),
210             new FragmentMenuEntry("projection", ProjectionFragment.class),
211             new FragmentMenuEntry("property test", PropertyTestFragment.class),
212             new FragmentMenuEntry("qc viewer", QCViewerFragment.class),
213             new FragmentMenuEntry("rotary", RotaryFragment.class),
214             new FragmentMenuEntry("sensors", SensorsTestFragment.class),
215             new FragmentMenuEntry("storage lifetime", StorageLifetimeFragment.class),
216             new FragmentMenuEntry("storage volumes", StorageVolumesFragment.class),
217             new FragmentMenuEntry("system bars", SystemBarsFragment.class),
218             new FragmentMenuEntry("system features", SystemFeaturesFragment.class),
219             new FragmentMenuEntry("telemetry", CarTelemetryTestFragment.class),
220             new FragmentMenuEntry("touch test", TouchTestFragment.class),
221             new FragmentMenuEntry("users", UserFragment.class),
222             new FragmentMenuEntry("user restrictions", UserRestrictionsFragment.class),
223             new FragmentMenuEntry("vehicle ctrl", VehicleCtrlFragment.class),
224             new FragmentMenuEntry("volume test", VolumeTestFragment.class),
225             new FragmentMenuEntry("watchdog", CarWatchdogTestFragment.class),
226             new FragmentMenuEntry("web links", WebLinksTestFragment.class),
227             new FragmentMenuEntry("window insets full screen",
228                     WindowInsetsFullScreenFragment.class));
229 
230     private Car mCarApi;
231     private CarHvacManager mHvacManager;
232     private CarPowerManager mPowerManager;
233     private CarPropertyManager mPropertyManager;
234     private CarSensorManager mSensorManager;
235     private CarAppFocusManager mCarAppFocusManager;
236     private CarProjectionManager mCarProjectionManager;
237     private CarTelemetryManager mCarTelemetryManager;
238     private CarWatchdogManager mCarWatchdogManager;
239     private Object mPropertyManagerReady = new Object();
240 
KitchenSinkActivity()241     public KitchenSinkActivity() {
242         mMenuEntries.sort(Comparator.comparing(MenuEntry::getText));
243     }
244 
getHvacManager()245     public CarHvacManager getHvacManager() {
246         return mHvacManager;
247     }
248 
getPowerManager()249     public CarPowerManager getPowerManager() {
250         return mPowerManager;
251     }
252 
getPropertyManager()253     public CarPropertyManager getPropertyManager() {
254         return mPropertyManager;
255     }
256 
getSensorManager()257     public CarSensorManager getSensorManager() {
258         return mSensorManager;
259     }
260 
getProjectionManager()261     public CarProjectionManager getProjectionManager() {
262         return mCarProjectionManager;
263     }
264 
getCarTelemetryManager()265     public CarTelemetryManager getCarTelemetryManager() {
266         return mCarTelemetryManager;
267     }
268 
getCarWatchdogManager()269     public CarWatchdogManager getCarWatchdogManager() {
270         return mCarWatchdogManager;
271     }
272 
273     /* Open any tab directly:
274      * adb shell am force-stop com.google.android.car.kitchensink
275      * adb shell am start -n com.google.android.car.kitchensink/.KitchenSinkActivity \
276      *     --es "select" "connectivity"
277      *
278      * Test car watchdog:
279      * adb shell am force-stop com.google.android.car.kitchensink
280      * adb shell am start -n com.google.android.car.kitchensink/.KitchenSinkActivity \
281      *     --es "watchdog" "[timeout] [not_respond_after] [inactive_main_after] [verbose]"
282      * - timeout: critical | moderate | normal
283      * - not_respond_after: after the given seconds, the client will not respond to car watchdog
284      *                      (-1 for making the client respond always)
285      * - inactive_main_after: after the given seconds, the main thread will not be responsive
286      *                        (-1 for making the main thread responsive always)
287      * - verbose: whether to output verbose logs (default: false)
288      */
289     @Override
onNewIntent(Intent intent)290     protected void onNewIntent(Intent intent) {
291         super.onNewIntent(intent);
292         Log.i(TAG, "onNewIntent");
293         Bundle extras = intent.getExtras();
294         if (extras == null) {
295             return;
296         }
297         String watchdog = extras.getString("watchdog");
298         if (watchdog != null) {
299             CarWatchdogClient.start(getCar(), watchdog);
300         }
301         String select = extras.getString("select");
302         if (select != null) {
303             mMenuEntries.stream().filter(me -> select.equals(me.getText()))
304                     .findAny().ifPresent(me -> me.onClick());
305         }
306     }
307 
308     @Override
onCreate(Bundle savedInstanceState)309     protected void onCreate(Bundle savedInstanceState) {
310         super.onCreate(savedInstanceState);
311         setContentView(R.layout.kitchen_activity);
312 
313         // Connection to Car Service does not work for non-automotive yet.
314         if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
315             initCarApi();
316         }
317 
318         mKitchenContent = findViewById(R.id.kitchen_content);
319 
320         mMenu = findViewById(R.id.menu);
321         mMenu.setAdapter(new MenuAdapter(this));
322         mMenu.setLayoutManager(new GridLayoutManager(this, 4));
323 
324         mMenuButton = findViewById(R.id.menu_button);
325         mMenuButton.setOnClickListener(view -> toggleMenuVisibility());
326         Log.i(TAG, "onCreate");
327         onNewIntent(getIntent());
328     }
329 
330     @Override
onRestoreInstanceState(Bundle savedInstanceState)331     protected void onRestoreInstanceState(Bundle savedInstanceState) {
332         super.onRestoreInstanceState(savedInstanceState);
333         // The app is being started for the first time.
334         if (savedInstanceState == null) {
335             return;
336         }
337 
338         // The app is being reloaded, restores the last fragment UI.
339         mLastFragmentTag = savedInstanceState.getString(LAST_FRAGMENT_TAG);
340         if (mLastFragmentTag != DEFAULT_FRAGMENT_TAG) {
341             toggleMenuVisibility();
342         }
343     }
344 
toggleMenuVisibility()345     private void toggleMenuVisibility() {
346         boolean menuVisible = mMenu.getVisibility() == View.VISIBLE;
347         mMenu.setVisibility(menuVisible ? View.GONE : View.VISIBLE);
348         mKitchenContent.setVisibility(menuVisible ? View.VISIBLE : View.GONE);
349         mMenuButton.setText(menuVisible ? "Show KitchenSink Menu" : "Hide KitchenSink Menu");
350     }
351 
initCarApi()352     private void initCarApi() {
353         if (mCarApi != null && mCarApi.isConnected()) {
354             mCarApi.disconnect();
355             mCarApi = null;
356         }
357         mCarApi = Car.createCar(this, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
358                 (Car car, boolean ready) -> {
359                     if (ready) {
360                         initManagers(car);
361                     }
362                 });
363     }
364 
365     @Override
onStart()366     protected void onStart() {
367         super.onStart();
368         Log.i(TAG, "onStart");
369     }
370 
371     @Override
onRestart()372     protected void onRestart() {
373         super.onRestart();
374         Log.i(TAG, "onRestart");
375     }
376 
377     @Override
onResume()378     protected void onResume() {
379         super.onResume();
380         Log.i(TAG, "onResume");
381     }
382 
383     @Override
onSaveInstanceState(Bundle outState)384     protected void onSaveInstanceState(Bundle outState) {
385         outState.putString(LAST_FRAGMENT_TAG, mLastFragmentTag);
386         super.onSaveInstanceState(outState);
387     }
388 
389     @Override
onPause()390     protected void onPause() {
391         super.onPause();
392         Log.i(TAG, "onPause");
393     }
394 
395     @Override
onStop()396     protected void onStop() {
397         super.onStop();
398         Log.i(TAG, "onStop");
399     }
400 
401     @Override
onDestroy()402     protected void onDestroy() {
403         if (mCarApi != null) {
404             mCarApi.disconnect();
405         }
406         Log.i(TAG, "onDestroy");
407         super.onDestroy();
408     }
409 
410     @Override
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)411     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
412         if (args != null && args.length > 0 && args[0].equals("cmd")) {
413             String[] cmdArgs = new String[args.length - 1];
414             System.arraycopy(args, 1, cmdArgs, 0, args.length - 1);
415             new KitchenSinkShellCommand(this, writer, cmdArgs).run();
416             return;
417         }
418         super.dump(prefix, fd, writer, args);
419     }
420 
showFragment(Fragment fragment)421     private void showFragment(Fragment fragment) {
422         getSupportFragmentManager().beginTransaction()
423                 .replace(R.id.kitchen_content, fragment)
424                 .commit();
425     }
426 
initManagers(Car car)427     private void initManagers(Car car) {
428         synchronized (mPropertyManagerReady) {
429             mHvacManager = (CarHvacManager) car.getCarManager(
430                     android.car.Car.HVAC_SERVICE);
431             mPowerManager = (CarPowerManager) car.getCarManager(
432                     android.car.Car.POWER_SERVICE);
433             mPropertyManager = (CarPropertyManager) car.getCarManager(
434                     android.car.Car.PROPERTY_SERVICE);
435             mSensorManager = (CarSensorManager) car.getCarManager(
436                     android.car.Car.SENSOR_SERVICE);
437             mCarAppFocusManager =
438                     (CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
439             mCarProjectionManager =
440                     (CarProjectionManager) car.getCarManager(Car.PROJECTION_SERVICE);
441             mCarTelemetryManager =
442                     (CarTelemetryManager) car.getCarManager(Car.CAR_TELEMETRY_SERVICE);
443             mCarWatchdogManager =
444                     (CarWatchdogManager) car.getCarManager(Car.CAR_WATCHDOG_SERVICE);
445             mPropertyManagerReady.notifyAll();
446         }
447     }
448 
getCar()449     public Car getCar() {
450         return mCarApi;
451     }
452 
453     private final class MenuAdapter extends RecyclerView.Adapter<ItemViewHolder> {
454 
455         private final LayoutInflater mLayoutInflator;
456 
MenuAdapter(Context context)457         MenuAdapter(Context context) {
458             mLayoutInflator = LayoutInflater.from(context);
459         }
460 
461         @Override
onCreateViewHolder(ViewGroup parent, int viewType)462         public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
463             View view = mLayoutInflator.inflate(R.layout.menu_item, parent, false);
464             return new ItemViewHolder(view);
465         }
466 
467         @Override
onBindViewHolder(ItemViewHolder holder, int position)468         public void onBindViewHolder(ItemViewHolder holder, int position) {
469             holder.mTitle.setText(mMenuEntries.get(position).getText());
470             holder.mTitle.setOnClickListener(v -> mMenuEntries.get(position).onClick());
471         }
472 
473         @Override
getItemCount()474         public int getItemCount() {
475             return mMenuEntries.size();
476         }
477     }
478 
479     private final class ItemViewHolder extends RecyclerView.ViewHolder {
480         TextView mTitle;
481 
ItemViewHolder(View itemView)482         ItemViewHolder(View itemView) {
483             super(itemView);
484             mTitle = itemView.findViewById(R.id.title);
485         }
486     }
487 
488     // Use AsyncTask to refresh Car*Manager after car service connected
requestRefreshManager(final Runnable r, final Handler h)489     public void requestRefreshManager(final Runnable r, final Handler h) {
490         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
491             @Override
492             protected Void doInBackground(Void... unused) {
493                 synchronized (mPropertyManagerReady) {
494                     while (!mCarApi.isConnected()) {
495                         try {
496                             mPropertyManagerReady.wait();
497                         } catch (InterruptedException e) {
498                             return null;
499                         }
500                     }
501                 }
502                 return null;
503             }
504 
505             @Override
506             protected void onPostExecute(Void unused) {
507                 h.post(r);
508             }
509         };
510         task.execute();
511     }
512 }
513