blob: 5574465e31fd915ff5b743e32285f4687529e655 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.car.kitchensink;
import android.car.Car;
import android.car.CarAppFocusManager;
import android.car.CarProjectionManager;
import android.car.hardware.CarSensorManager;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.property.CarPropertyManager;
import android.car.telemetry.CarTelemetryManager;
import android.car.watchdog.CarWatchdogManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.car.kitchensink.activityresolver.ActivityResolverFragment;
import com.google.android.car.kitchensink.admin.DevicePolicyFragment;
import com.google.android.car.kitchensink.alertdialog.AlertDialogTestFragment;
import com.google.android.car.kitchensink.assistant.CarAssistantFragment;
import com.google.android.car.kitchensink.audio.AudioTestFragment;
import com.google.android.car.kitchensink.audio.CarAudioInputTestFragment;
import com.google.android.car.kitchensink.bluetooth.BluetoothDeviceFragment;
import com.google.android.car.kitchensink.bluetooth.BluetoothHeadsetFragment;
import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
import com.google.android.car.kitchensink.carboard.KeyboardTestFragment;
import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment;
import com.google.android.car.kitchensink.connectivity.ConnectivityFragment;
import com.google.android.car.kitchensink.cube.CubesTestFragment;
import com.google.android.car.kitchensink.diagnostic.DiagnosticTestFragment;
import com.google.android.car.kitchensink.displayinfo.DisplayInfoFragment;
import com.google.android.car.kitchensink.experimental.ExperimentalFeatureTestFragment;
import com.google.android.car.kitchensink.hvac.HvacTestFragment;
import com.google.android.car.kitchensink.insets.WindowInsetsFullScreenFragment;
import com.google.android.car.kitchensink.notification.NotificationFragment;
import com.google.android.car.kitchensink.orientation.OrientationTestFragment;
import com.google.android.car.kitchensink.packageinfo.PackageInfoFragment;
import com.google.android.car.kitchensink.power.PowerTestFragment;
import com.google.android.car.kitchensink.projection.ProjectionFragment;
import com.google.android.car.kitchensink.property.PropertyTestFragment;
import com.google.android.car.kitchensink.qc.QCViewerFragment;
import com.google.android.car.kitchensink.rotary.RotaryFragment;
import com.google.android.car.kitchensink.sensor.SensorsTestFragment;
import com.google.android.car.kitchensink.storagelifetime.StorageLifetimeFragment;
import com.google.android.car.kitchensink.storagevolumes.StorageVolumesFragment;
import com.google.android.car.kitchensink.systembars.SystemBarsFragment;
import com.google.android.car.kitchensink.systemfeatures.SystemFeaturesFragment;
import com.google.android.car.kitchensink.telemetry.CarTelemetryTestFragment;
import com.google.android.car.kitchensink.touch.TouchTestFragment;
import com.google.android.car.kitchensink.users.ProfileUserFragment;
import com.google.android.car.kitchensink.users.UserFragment;
import com.google.android.car.kitchensink.users.UserRestrictionsFragment;
import com.google.android.car.kitchensink.vehiclectrl.VehicleCtrlFragment;
import com.google.android.car.kitchensink.vhal.VehicleHalFragment;
import com.google.android.car.kitchensink.volume.VolumeTestFragment;
import com.google.android.car.kitchensink.watchdog.CarWatchdogTestFragment;
import com.google.android.car.kitchensink.weblinks.WebLinksTestFragment;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class KitchenSinkActivity extends FragmentActivity {
private static final String TAG = "KitchenSinkActivity";
private static final String LAST_FRAGMENT_TAG = "lastFragmentTag";
private static final String DEFAULT_FRAGMENT_TAG = "";
private RecyclerView mMenu;
private Button mMenuButton;
private View mKitchenContent;
private String mLastFragmentTag = DEFAULT_FRAGMENT_TAG;
private interface ClickHandler {
void onClick();
}
private static abstract class MenuEntry implements ClickHandler {
abstract String getText();
}
private final class OnClickMenuEntry extends MenuEntry {
private final String mText;
private final ClickHandler mClickHandler;
OnClickMenuEntry(String text, ClickHandler clickHandler) {
mText = text;
mClickHandler = clickHandler;
}
@Override
String getText() {
return mText;
}
@Override
public void onClick() {
toggleMenuVisibility();
mClickHandler.onClick();
}
}
private final class FragmentMenuEntry<T extends Fragment> extends MenuEntry {
private final class FragmentClassOrInstance<T extends Fragment> {
final Class<T> mClazz;
T mFragment = null;
FragmentClassOrInstance(Class<T> clazz) {
mClazz = clazz;
}
T getFragment() {
if (mFragment == null) {
try {
mFragment = mClazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
Log.e(TAG, "unable to create fragment", e);
}
}
return mFragment;
}
}
private final String mText;
private final FragmentClassOrInstance<T> mFragment;
FragmentMenuEntry(String text, Class<T> clazz) {
mText = text;
mFragment = new FragmentClassOrInstance<>(clazz);
}
@Override
String getText() {
return mText;
}
@Override
public void onClick() {
Fragment fragment = mFragment.getFragment();
if (fragment != null) {
KitchenSinkActivity.this.showFragment(fragment);
toggleMenuVisibility();
mLastFragmentTag = fragment.getTag();
} else {
Log.e(TAG, "cannot show fragment for " + getText());
}
}
}
private final List<MenuEntry> mMenuEntries = Arrays.asList(
new FragmentMenuEntry("activity resolver", ActivityResolverFragment.class),
new FragmentMenuEntry("alert window", AlertDialogTestFragment.class),
new FragmentMenuEntry("assistant", CarAssistantFragment.class),
new FragmentMenuEntry("audio", AudioTestFragment.class),
new FragmentMenuEntry("Audio Input", CarAudioInputTestFragment.class),
new FragmentMenuEntry("BT device", BluetoothDeviceFragment.class),
new FragmentMenuEntry("BT headset", BluetoothHeadsetFragment.class),
new FragmentMenuEntry("BT messaging", MapMceTestFragment.class),
new FragmentMenuEntry("carapi", CarApiTestFragment.class),
new FragmentMenuEntry("carboard", KeyboardTestFragment.class),
new FragmentMenuEntry("connectivity", ConnectivityFragment.class),
new FragmentMenuEntry("cubes test", CubesTestFragment.class),
new FragmentMenuEntry("device policy", DevicePolicyFragment.class),
new FragmentMenuEntry("diagnostic", DiagnosticTestFragment.class),
new FragmentMenuEntry("display info", DisplayInfoFragment.class),
new FragmentMenuEntry("experimental feature", ExperimentalFeatureTestFragment.class),
new FragmentMenuEntry("hvac", HvacTestFragment.class),
new FragmentMenuEntry("inst cluster", InstrumentClusterFragment.class),
// TODO (b/141774865) Enable after b/141635607 is fixed
// new FragmentMenuEntry("input test", InputTestFragment.class),
new FragmentMenuEntry("notification", NotificationFragment.class),
new FragmentMenuEntry("orientation test", OrientationTestFragment.class),
new FragmentMenuEntry("package info", PackageInfoFragment.class),
new FragmentMenuEntry("power test", PowerTestFragment.class),
new FragmentMenuEntry("profile_user", ProfileUserFragment.class),
new FragmentMenuEntry("projection", ProjectionFragment.class),
new FragmentMenuEntry("property test", PropertyTestFragment.class),
new FragmentMenuEntry("qc viewer", QCViewerFragment.class),
new FragmentMenuEntry("rotary", RotaryFragment.class),
new FragmentMenuEntry("sensors", SensorsTestFragment.class),
new FragmentMenuEntry("storage lifetime", StorageLifetimeFragment.class),
new FragmentMenuEntry("storage volumes", StorageVolumesFragment.class),
new FragmentMenuEntry("system bars", SystemBarsFragment.class),
new FragmentMenuEntry("system features", SystemFeaturesFragment.class),
new FragmentMenuEntry("telemetry", CarTelemetryTestFragment.class),
new FragmentMenuEntry("touch test", TouchTestFragment.class),
new FragmentMenuEntry("users", UserFragment.class),
new FragmentMenuEntry("user restrictions", UserRestrictionsFragment.class),
new FragmentMenuEntry("vehicle ctrl", VehicleCtrlFragment.class),
new FragmentMenuEntry("vehicle hal", VehicleHalFragment.class),
new FragmentMenuEntry("volume test", VolumeTestFragment.class),
new FragmentMenuEntry("watchdog", CarWatchdogTestFragment.class),
new FragmentMenuEntry("web links", WebLinksTestFragment.class),
new FragmentMenuEntry("window insets full screen",
WindowInsetsFullScreenFragment.class));
private Car mCarApi;
private CarHvacManager mHvacManager;
private CarPowerManager mPowerManager;
private CarPropertyManager mPropertyManager;
private CarSensorManager mSensorManager;
private CarAppFocusManager mCarAppFocusManager;
private CarProjectionManager mCarProjectionManager;
private CarTelemetryManager mCarTelemetryManager;
private CarWatchdogManager mCarWatchdogManager;
private Object mPropertyManagerReady = new Object();
public KitchenSinkActivity() {
mMenuEntries.sort(Comparator.comparing(MenuEntry::getText));
}
public CarHvacManager getHvacManager() {
return mHvacManager;
}
public CarPowerManager getPowerManager() {
return mPowerManager;
}
public CarPropertyManager getPropertyManager() {
return mPropertyManager;
}
public CarSensorManager getSensorManager() {
return mSensorManager;
}
public CarProjectionManager getProjectionManager() {
return mCarProjectionManager;
}
public CarTelemetryManager getCarTelemetryManager() {
return mCarTelemetryManager;
}
public CarWatchdogManager getCarWatchdogManager() {
return mCarWatchdogManager;
}
/* Open any tab directly:
* adb shell am force-stop com.google.android.car.kitchensink
* adb shell am start -n com.google.android.car.kitchensink/.KitchenSinkActivity \
* --es "select" "connectivity"
*
* Test car watchdog:
* adb shell am force-stop com.google.android.car.kitchensink
* adb shell am start -n com.google.android.car.kitchensink/.KitchenSinkActivity \
* --es "watchdog" "[timeout] [not_respond_after] [inactive_main_after] [verbose]"
* - timeout: critical | moderate | normal
* - not_respond_after: after the given seconds, the client will not respond to car watchdog
* (-1 for making the client respond always)
* - inactive_main_after: after the given seconds, the main thread will not be responsive
* (-1 for making the main thread responsive always)
* - verbose: whether to output verbose logs (default: false)
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.i(TAG, "onNewIntent");
Bundle extras = intent.getExtras();
if (extras == null) {
return;
}
String watchdog = extras.getString("watchdog");
if (watchdog != null) {
CarWatchdogClient.start(getCar(), watchdog);
}
String select = extras.getString("select");
if (select != null) {
mMenuEntries.stream().filter(me -> select.equals(me.getText()))
.findAny().ifPresent(me -> me.onClick());
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.kitchen_activity);
// Connection to Car Service does not work for non-automotive yet.
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
initCarApi();
}
mKitchenContent = findViewById(R.id.kitchen_content);
mMenu = findViewById(R.id.menu);
mMenu.setAdapter(new MenuAdapter(this));
mMenu.setLayoutManager(new GridLayoutManager(this, 4));
mMenuButton = findViewById(R.id.menu_button);
mMenuButton.setOnClickListener(view -> toggleMenuVisibility());
Log.i(TAG, "onCreate");
onNewIntent(getIntent());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// The app is being started for the first time.
if (savedInstanceState == null) {
return;
}
// The app is being reloaded, restores the last fragment UI.
mLastFragmentTag = savedInstanceState.getString(LAST_FRAGMENT_TAG);
if (mLastFragmentTag != DEFAULT_FRAGMENT_TAG) {
toggleMenuVisibility();
}
}
private void toggleMenuVisibility() {
boolean menuVisible = mMenu.getVisibility() == View.VISIBLE;
mMenu.setVisibility(menuVisible ? View.GONE : View.VISIBLE);
mKitchenContent.setVisibility(menuVisible ? View.VISIBLE : View.GONE);
mMenuButton.setText(menuVisible ? "Show KitchenSink Menu" : "Hide KitchenSink Menu");
}
private void initCarApi() {
if (mCarApi != null && mCarApi.isConnected()) {
mCarApi.disconnect();
mCarApi = null;
}
mCarApi = Car.createCar(this, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
(Car car, boolean ready) -> {
if (ready) {
initManagers(car);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "onRestart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString(LAST_FRAGMENT_TAG, mLastFragmentTag);
super.onSaveInstanceState(outState);
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop");
}
@Override
protected void onDestroy() {
if (mCarApi != null) {
mCarApi.disconnect();
}
Log.i(TAG, "onDestroy");
super.onDestroy();
}
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
if (args != null && args.length > 0 && args[0].equals("cmd")) {
String[] cmdArgs = new String[args.length - 1];
System.arraycopy(args, 1, cmdArgs, 0, args.length - 1);
new KitchenSinkShellCommand(this, writer, cmdArgs).run();
return;
}
super.dump(prefix, fd, writer, args);
}
private void showFragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.kitchen_content, fragment)
.commit();
}
private void initManagers(Car car) {
synchronized (mPropertyManagerReady) {
mHvacManager = (CarHvacManager) car.getCarManager(
android.car.Car.HVAC_SERVICE);
mPowerManager = (CarPowerManager) car.getCarManager(
android.car.Car.POWER_SERVICE);
mPropertyManager = (CarPropertyManager) car.getCarManager(
android.car.Car.PROPERTY_SERVICE);
mSensorManager = (CarSensorManager) car.getCarManager(
android.car.Car.SENSOR_SERVICE);
mCarAppFocusManager =
(CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
mCarProjectionManager =
(CarProjectionManager) car.getCarManager(Car.PROJECTION_SERVICE);
mCarTelemetryManager =
(CarTelemetryManager) car.getCarManager(Car.CAR_TELEMETRY_SERVICE);
mCarWatchdogManager =
(CarWatchdogManager) car.getCarManager(Car.CAR_WATCHDOG_SERVICE);
mPropertyManagerReady.notifyAll();
}
}
public Car getCar() {
return mCarApi;
}
private final class MenuAdapter extends RecyclerView.Adapter<ItemViewHolder> {
private final LayoutInflater mLayoutInflator;
MenuAdapter(Context context) {
mLayoutInflator = LayoutInflater.from(context);
}
@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mLayoutInflator.inflate(R.layout.menu_item, parent, false);
return new ItemViewHolder(view);
}
@Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
holder.mTitle.setText(mMenuEntries.get(position).getText());
holder.mTitle.setOnClickListener(v -> mMenuEntries.get(position).onClick());
}
@Override
public int getItemCount() {
return mMenuEntries.size();
}
}
private final class ItemViewHolder extends RecyclerView.ViewHolder {
TextView mTitle;
ItemViewHolder(View itemView) {
super(itemView);
mTitle = itemView.findViewById(R.id.title);
}
}
// Use AsyncTask to refresh Car*Manager after car service connected
public void requestRefreshManager(final Runnable r, final Handler h) {
final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... unused) {
synchronized (mPropertyManagerReady) {
while (!mCarApi.isConnected()) {
try {
mPropertyManagerReady.wait();
} catch (InterruptedException e) {
return null;
}
}
}
return null;
}
@Override
protected void onPostExecute(Void unused) {
h.post(r);
}
};
task.execute();
}
}