| /* |
| * Copyright (C) 2016 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.sensor; |
| |
| import android.Manifest; |
| import android.annotation.Nullable; |
| import android.car.Car; |
| import android.content.pm.PackageManager; |
| import android.location.Location; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.support.car.CarNotConnectedException; |
| import android.support.car.hardware.CarSensorConfig; |
| import android.support.car.hardware.CarSensorEvent; |
| import android.support.car.hardware.CarSensorManager; |
| import android.support.v4.app.Fragment; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.TextView; |
| |
| import com.google.android.car.kitchensink.KitchenSinkActivity; |
| import com.google.android.car.kitchensink.R; |
| |
| import java.text.DateFormat; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| public class SensorsTestFragment extends Fragment { |
| private static final String TAG = "CAR.SENSOR.KS"; |
| private static final boolean DBG = true; |
| private static final boolean DBG_VERBOSE = false; |
| private static final int KS_PERMISSIONS_REQUEST = 1; |
| |
| private final static String[] REQUIRED_PERMISSIONS = new String[]{ |
| Manifest.permission.ACCESS_FINE_LOCATION, |
| Manifest.permission.ACCESS_COARSE_LOCATION, |
| Car.PERMISSION_MILEAGE, |
| Car.PERMISSION_FUEL_DETAILED, |
| Car.PERMISSION_SPEED, |
| Car.PERMISSION_VEHICLE_DYNAMICS_STATE |
| }; |
| |
| private final CarSensorManager.OnSensorChangedListener mOnSensorChangedListener = |
| new CarSensorManager.OnSensorChangedListener() { |
| @Override |
| public void onSensorChanged(CarSensorManager manager, CarSensorEvent event) { |
| if (DBG_VERBOSE) { |
| Log.v(TAG, "New car sensor event: " + event); |
| } |
| synchronized (SensorsTestFragment.this) { |
| mEventMap.put(event.sensorType, event); |
| } |
| refreshUi(); |
| } |
| }; |
| private final Handler mHandler = new Handler(); |
| private final Map<Integer, CarSensorEvent> mEventMap = new ConcurrentHashMap<>(); |
| private final DateFormat mDateFormat = SimpleDateFormat.getDateTimeInstance(); |
| |
| private KitchenSinkActivity mActivity; |
| private TextView mSensorInfo; |
| private Car mCar; |
| private CarSensorManager mSensorManager; |
| private String mNaString; |
| private int[] supportedSensors = new int[0]; |
| private Set<String> mActivePermissions = new HashSet<String>(); |
| |
| @Nullable |
| @Override |
| public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, |
| @Nullable Bundle savedInstanceState) { |
| if (DBG) { |
| Log.i(TAG, "onCreateView"); |
| } |
| |
| View view = inflater.inflate(R.layout.sensors, container, false); |
| mActivity = (KitchenSinkActivity) getHost(); |
| |
| mSensorInfo = (TextView) view.findViewById(R.id.sensor_info); |
| mNaString = getContext().getString(R.string.sensor_na); |
| |
| return view; |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| initPermissions(); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| if (mSensorManager != null) { |
| mSensorManager.removeListener(mOnSensorChangedListener); |
| } |
| } |
| |
| private void initSensors() { |
| try { |
| mSensorManager = (CarSensorManager) |
| mActivity.getCar().getCarManager(Car.SENSOR_SERVICE); |
| supportedSensors = mSensorManager.getSupportedSensors(); |
| for (Integer sensor : supportedSensors) { |
| if ((sensor == CarSensorManager.SENSOR_TYPE_LOCATION |
| || sensor == CarSensorManager.SENSOR_TYPE_GPS_SATELLITE) |
| && !mActivePermissions.contains(Manifest.permission.ACCESS_FINE_LOCATION)) { |
| continue; |
| } |
| mSensorManager.addListener(mOnSensorChangedListener, sensor, |
| CarSensorManager.SENSOR_RATE_NORMAL); |
| } |
| } catch (CarNotConnectedException e) { |
| Log.e(TAG, "Car not connected or not supported", e); |
| } |
| } |
| |
| private void initPermissions() { |
| Set<String> missingPermissions = checkExistingPermissions(); |
| if (!missingPermissions.isEmpty()) { |
| requestPermissions(missingPermissions); |
| } else { |
| initSensors(); |
| } |
| } |
| |
| private Set<String> checkExistingPermissions() { |
| Set<String> missingPermissions = new HashSet<String>(); |
| for (String permission : REQUIRED_PERMISSIONS) { |
| if (mActivity.checkSelfPermission(permission) |
| == PackageManager.PERMISSION_GRANTED) { |
| mActivePermissions.add(permission); |
| } else { |
| missingPermissions.add(permission); |
| } |
| } |
| return missingPermissions; |
| } |
| |
| private void requestPermissions(Set<String> permissions) { |
| Log.d(TAG, "requesting additional permissions=" + permissions); |
| |
| requestPermissions(permissions.toArray(new String[permissions.size()]), |
| KS_PERMISSIONS_REQUEST); |
| } |
| |
| @Override |
| public void onRequestPermissionsResult(int requestCode, String[] permissions, |
| int[] grantResults) { |
| Log.d(TAG, "onRequestPermissionsResult reqCode=" + requestCode); |
| if (KS_PERMISSIONS_REQUEST == requestCode) { |
| for (int i=0; i<permissions.length; i++) { |
| if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { |
| mActivePermissions.add(permissions[i]); |
| } |
| } |
| initSensors(); |
| } |
| } |
| |
| private void refreshUi() { |
| String summaryString; |
| synchronized (this) { |
| List<String> summary = new ArrayList<>(); |
| for (Integer i : supportedSensors) { |
| CarSensorEvent event = mEventMap.get(i); |
| switch (i) { |
| case CarSensorManager.SENSOR_TYPE_COMPASS: |
| summary.add(getCompassString(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_CAR_SPEED: |
| summary.add(getContext().getString(R.string.sensor_speed, |
| getTimestamp(event), |
| event == null ? mNaString : event.getCarSpeedData().carSpeed)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_RPM: |
| summary.add(getContext().getString(R.string.sensor_rpm, |
| getTimestamp(event), |
| event == null ? mNaString : event.getRpmData().rpm)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_ODOMETER: |
| summary.add(getContext().getString(R.string.sensor_odometer, |
| getTimestamp(event), |
| event == null ? mNaString : event.getOdometerData().kms)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL: |
| summary.add(getFuelLevel(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_FUEL_DOOR_OPEN: |
| summary.add(getFuelDoorOpen(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE: |
| summary.add(getContext().getString(R.string.sensor_parking_brake, |
| getTimestamp(event), |
| event == null ? mNaString : |
| event.getParkingBrakeData().isEngaged)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_GEAR: |
| summary.add(getContext().getString(R.string.sensor_gear, |
| getTimestamp(event), |
| event == null ? mNaString : event.getGearData().gear)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_NIGHT: |
| summary.add(getContext().getString(R.string.sensor_night, |
| getTimestamp(event), |
| event == null ? mNaString : event.getNightData().isNightMode)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_LOCATION: |
| summary.add(getLocationString(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS: |
| String drivingStatus = mNaString; |
| String binDrivingStatus = mNaString; |
| if (event != null) { |
| CarSensorEvent.DrivingStatusData drivingStatusData = |
| event.getDrivingStatusData(); |
| drivingStatus = String.valueOf(drivingStatusData.status); |
| binDrivingStatus = Integer.toBinaryString(drivingStatusData.status); |
| } |
| summary.add(getContext().getString(R.string.sensor_driving_status, |
| getTimestamp(event), drivingStatus, binDrivingStatus)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_ENVIRONMENT: |
| String temperature = mNaString; |
| String pressure = mNaString; |
| if (event != null) { |
| CarSensorEvent.EnvironmentData env = event.getEnvironmentData(); |
| temperature = Float.isNaN(env.temperature) ? temperature : |
| String.valueOf(env.temperature); |
| pressure = Float.isNaN(env.pressure) ? pressure : |
| String.valueOf(env.pressure); |
| } |
| summary.add(getContext().getString(R.string.sensor_environment, |
| getTimestamp(event), temperature, pressure)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_ACCELEROMETER: |
| summary.add(getAccelerometerString(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE: |
| summary.add(getGpsSatelliteString(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_GYROSCOPE: |
| summary.add(getGyroscopeString(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE: |
| if(event != null) { |
| CarSensorEvent.CarWheelTickDistanceData d = |
| event.getCarWheelTickDistanceData(); |
| summary.add(getContext().getString(R.string.sensor_wheel_ticks, |
| getTimestamp(event), d.sensorResetCount, d.frontLeftWheelDistanceMm, |
| d.frontRightWheelDistanceMm, d.rearLeftWheelDistanceMm, |
| d.rearRightWheelDistanceMm)); |
| } else { |
| summary.add(getContext().getString(R.string.sensor_wheel_ticks, |
| getTimestamp(event), mNaString, mNaString, mNaString, mNaString, |
| mNaString)); |
| } |
| // Get the config data |
| try { |
| CarSensorConfig c = mSensorManager.getSensorConfig( |
| CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE); |
| summary.add(getContext().getString(R.string.sensor_wheel_ticks_cfg, |
| c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS), |
| c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK), |
| c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK), |
| c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK), |
| c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK))); |
| } catch (CarNotConnectedException e) { |
| Log.e(TAG, "Car not connected or not supported", e); |
| } |
| break; |
| case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE: |
| summary.add(getContext().getString(R.string.sensor_abs_is_active, |
| getTimestamp(event), event == null ? mNaString : |
| event.getCarAbsActiveData().absIsActive)); |
| break; |
| |
| case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE: |
| summary.add( |
| getContext().getString(R.string.sensor_traction_control_is_active, |
| getTimestamp(event), event == null ? mNaString : |
| event.getCarTractionControlActiveData().tractionControlIsActive)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_ENGINE_ON: |
| summary.add(getEngineOn(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_EV_BATTERY_LEVEL: |
| summary.add(getEvBatteryLevel(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_OPEN: |
| summary.add(getEvChargePortOpen(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED: |
| summary.add(getEvChargePortConnected(event)); |
| break; |
| case CarSensorManager.SENSOR_TYPE_EV_BATTERY_CHARGE_RATE: |
| summary.add(getEvChargeRate(event)); |
| break; |
| default: |
| // Should never happen. |
| Log.w(TAG, "Unrecognized event type: " + i); |
| } |
| } |
| summaryString = TextUtils.join("\n", summary); |
| } |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mSensorInfo.setText(summaryString); |
| } |
| }); |
| } |
| |
| private String getTimestamp(CarSensorEvent event) { |
| if (event == null) { |
| return mNaString; |
| } |
| return mDateFormat.format(new Date(event.timestamp / (1000L * 1000L))); |
| } |
| |
| private String getCompassString(CarSensorEvent event) { |
| String bear = mNaString; |
| String pitch = mNaString; |
| String roll = mNaString; |
| if (event != null) { |
| CarSensorEvent.CompassData compass = event.getCompassData(); |
| bear = Float.isNaN(compass.bearing) ? bear : String.valueOf(compass.bearing); |
| pitch = Float.isNaN(compass.pitch) ? pitch : String.valueOf(compass.pitch); |
| roll = Float.isNaN(compass.roll) ? roll : String.valueOf(compass.roll); |
| } |
| return getContext().getString(R.string.sensor_compass, |
| getTimestamp(event), bear, pitch, roll); |
| } |
| |
| private String getGyroscopeString(CarSensorEvent event) { |
| String x = mNaString; |
| String y = mNaString; |
| String z = mNaString; |
| if (event != null) { |
| CarSensorEvent.GyroscopeData gyro = event.getGyroscopeData(); |
| x = Float.isNaN(gyro.x) ? x : String.valueOf(gyro.x); |
| y = Float.isNaN(gyro.y) ? y : String.valueOf(gyro.y); |
| z = Float.isNaN(gyro.z) ? z : String.valueOf(gyro.z); |
| } |
| return getContext().getString(R.string.sensor_gyroscope, |
| getTimestamp(event), x, y, z); |
| } |
| |
| private String getAccelerometerString(CarSensorEvent event) { |
| String x = mNaString; |
| String y = mNaString; |
| String z = mNaString; |
| if (event != null) { |
| CarSensorEvent.AccelerometerData gyro = event.getAccelerometerData(); |
| x = Float.isNaN(gyro.x) ? x : String.valueOf(gyro.x); |
| y = Float.isNaN(gyro.y) ? y : String.valueOf(gyro.y); |
| z = Float.isNaN(gyro.z) ? z : String.valueOf(gyro.z); |
| } |
| return getContext().getString(R.string.sensor_accelerometer, |
| getTimestamp(event), x, y, z); |
| } |
| |
| private String getLocationString(CarSensorEvent event) { |
| String lat = mNaString; |
| String lon = mNaString; |
| String accuracy = mNaString; |
| String alt = mNaString; |
| String speed = mNaString; |
| String bearing = mNaString; |
| if (event != null) { |
| Location location = event.getLocation(null); |
| lat = String.valueOf(location.getLatitude()); |
| lon = String.valueOf(location.getLongitude()); |
| accuracy = location.hasAccuracy() ? String.valueOf(location.getAccuracy()) : accuracy; |
| alt = location.hasAltitude() ? String.valueOf(location.getAltitude()) : alt; |
| speed = location.hasSpeed() ? String.valueOf(location.getSpeed()) : speed; |
| bearing = location.hasBearing() ? String.valueOf(location.getBearing()) : bearing; |
| } |
| return getContext().getString(R.string.sensor_location, |
| getTimestamp(event), lat, lon, accuracy, alt, speed, bearing); |
| } |
| |
| private String getGpsSatelliteString(CarSensorEvent event) { |
| String inUse = mNaString; |
| String inView = mNaString; |
| String perSattelite = ""; |
| if (event != null) { |
| CarSensorEvent.GpsSatelliteData gpsData = event.getGpsSatelliteData(true); |
| inUse = gpsData.numberInUse != -1 ? String.valueOf(gpsData.numberInUse) : inUse; |
| inView = gpsData.numberInView != -1 ? String.valueOf(gpsData.numberInView) : inView; |
| List<String> perSatteliteList = new ArrayList<>(); |
| int num = gpsData.usedInFix.length; |
| for (int i=0; i<num; i++) { |
| perSatteliteList.add(getContext().getString(R.string.sensor_single_gps_satellite, |
| i+1, gpsData.usedInFix[i], gpsData.prn[i], gpsData.snr[i], |
| gpsData.azimuth[i], gpsData.elevation[i])); |
| } |
| perSattelite = TextUtils.join(", ", perSatteliteList); |
| } |
| return getContext().getString(R.string.sensor_gps, |
| getTimestamp(event), inView, inUse, perSattelite); |
| } |
| |
| private String getFuelLevel(CarSensorEvent event) { |
| String fuelLevel = mNaString; |
| if(event != null) { |
| fuelLevel = String.valueOf(event.getFuelLevelData().level); |
| } |
| return getContext().getString(R.string.sensor_fuel_level, getTimestamp(event), fuelLevel); |
| } |
| |
| private String getFuelDoorOpen(CarSensorEvent event) { |
| String fuelDoorOpen = mNaString; |
| if(event != null) { |
| fuelDoorOpen = String.valueOf(event.getCarFuelDoorOpenData().fuelDoorIsOpen); |
| } |
| return getContext().getString(R.string.sensor_fuel_door_open, getTimestamp(event), |
| fuelDoorOpen); |
| } |
| |
| private String getEngineOn(CarSensorEvent event) { |
| String engineOn = mNaString; |
| if (event != null) { |
| engineOn = String.valueOf(event.getCarEngineOnData().engineIsOn); |
| } |
| return getContext().getString(R.string.sensor_engine_is_on, getTimestamp(event), engineOn); |
| } |
| |
| private String getEvBatteryLevel(CarSensorEvent event) { |
| String evBatteryLevel = mNaString; |
| if(event != null) { |
| evBatteryLevel = String.valueOf(event.getCarEvBatteryLevelData().evBatteryLevel); |
| } |
| return getContext().getString(R.string.sensor_ev_battery_level, getTimestamp(event), |
| evBatteryLevel); |
| } |
| |
| private String getEvChargePortOpen(CarSensorEvent event) { |
| String evChargePortOpen = mNaString; |
| if(event != null) { |
| evChargePortOpen = String.valueOf( |
| event.getCarEvChargePortOpenData().evChargePortIsOpen); |
| } |
| return getContext().getString(R.string.sensor_ev_charge_port_is_open, getTimestamp(event), |
| evChargePortOpen); |
| } |
| |
| private String getEvChargePortConnected(CarSensorEvent event) { |
| String evChargePortConnected = mNaString; |
| if(event != null) { |
| evChargePortConnected = String.valueOf( |
| event.getCarEvChargePortConnectedData().evChargePortIsConnected); |
| } |
| return getContext().getString(R.string.sensor_ev_charge_port_is_connected, |
| getTimestamp(event), evChargePortConnected); |
| } |
| |
| private String getEvChargeRate(CarSensorEvent event) { |
| String evChargeRate = mNaString; |
| if(event != null) { |
| evChargeRate = String.valueOf(event.getCarEvBatteryChargeRateData().evChargeRate); |
| } |
| return getContext().getString(R.string.sensor_ev_charge_rate, getTimestamp(event), |
| evChargeRate); |
| } |
| } |