Home | History | Annotate | Download | only in sensor
      1 /*
      2  * Copyright (C) 2016 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.sensor;
     18 
     19 import android.Manifest;
     20 import android.annotation.Nullable;
     21 import android.car.Car;
     22 import android.content.pm.PackageManager;
     23 import android.location.Location;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.support.car.CarNotConnectedException;
     27 import android.support.car.hardware.CarSensorEvent;
     28 import android.support.car.hardware.CarSensorManager;
     29 import android.support.v4.app.Fragment;
     30 import android.text.TextUtils;
     31 import android.util.Log;
     32 import android.view.LayoutInflater;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.widget.TextView;
     36 
     37 import com.google.android.car.kitchensink.KitchenSinkActivity;
     38 import com.google.android.car.kitchensink.R;
     39 
     40 import java.text.DateFormat;
     41 import java.text.SimpleDateFormat;
     42 import java.util.ArrayList;
     43 import java.util.Date;
     44 import java.util.HashSet;
     45 import java.util.List;
     46 import java.util.Map;
     47 import java.util.Set;
     48 import java.util.concurrent.ConcurrentHashMap;
     49 
     50 public class SensorsTestFragment extends Fragment {
     51     private static final String TAG = "CAR.SENSOR.KS";
     52     private static final boolean DBG = true;
     53     private static final boolean DBG_VERBOSE = false;
     54     private static final int KS_PERMISSIONS_REQUEST = 1;
     55 
     56     private final static String[] REQUIRED_PERMISSIONS = new String[]{
     57         Manifest.permission.ACCESS_FINE_LOCATION,
     58         Manifest.permission.ACCESS_COARSE_LOCATION,
     59         Car.PERMISSION_MILEAGE,
     60         Car.PERMISSION_FUEL,
     61         Car.PERMISSION_SPEED
     62     };
     63 
     64     private final CarSensorManager.OnSensorChangedListener mOnSensorChangedListener =
     65             new CarSensorManager.OnSensorChangedListener() {
     66                 @Override
     67                 public void onSensorChanged(CarSensorManager manager, CarSensorEvent event) {
     68                     if (DBG_VERBOSE) {
     69                         Log.v(TAG, "New car sensor event: " + event);
     70                     }
     71                     synchronized (SensorsTestFragment.this) {
     72                         mEventMap.put(event.sensorType, event);
     73                     }
     74                     refreshUi();
     75                 }
     76             };
     77     private final Handler mHandler = new Handler();
     78     private final Map<Integer, CarSensorEvent> mEventMap = new ConcurrentHashMap<>();
     79     private final DateFormat mDateFormat = SimpleDateFormat.getDateTimeInstance();
     80 
     81     private KitchenSinkActivity mActivity;
     82     private TextView mSensorInfo;
     83     private Car mCar;
     84     private CarSensorManager mSensorManager;
     85     private String mNaString;
     86     private int[] supportedSensors = new int[0];
     87     private Set<String> mActivePermissions = new HashSet<String>();
     88 
     89 
     90     @Nullable
     91     @Override
     92     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
     93                              @Nullable Bundle savedInstanceState) {
     94         if (DBG) {
     95             Log.i(TAG, "onCreateView");
     96         }
     97 
     98         View view = inflater.inflate(R.layout.sensors, container, false);
     99         mActivity = (KitchenSinkActivity) getHost();
    100 
    101         mSensorInfo = (TextView) view.findViewById(R.id.sensor_info);
    102         mNaString = getContext().getString(R.string.sensor_na);
    103 
    104         return view;
    105     }
    106 
    107     @Override
    108     public void onResume() {
    109         super.onResume();
    110         initPermissions();
    111     }
    112 
    113     @Override
    114     public void onPause() {
    115         super.onPause();
    116         if (mSensorManager != null) {
    117             mSensorManager.removeListener(mOnSensorChangedListener);
    118         }
    119     }
    120 
    121     private void initSensors() {
    122         try {
    123             mSensorManager = (CarSensorManager)
    124                     mActivity.getCar().getCarManager(Car.SENSOR_SERVICE);
    125             supportedSensors = mSensorManager.getSupportedSensors();
    126             for (Integer sensor : supportedSensors) {
    127                 if ((sensor == CarSensorManager.SENSOR_TYPE_LOCATION
    128                      || sensor == CarSensorManager.SENSOR_TYPE_GPS_SATELLITE)
    129                     && !mActivePermissions.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
    130                     continue;
    131                 }
    132                 mSensorManager.addListener(mOnSensorChangedListener, sensor,
    133                         CarSensorManager.SENSOR_RATE_NORMAL);
    134             }
    135         } catch (CarNotConnectedException e) {
    136             Log.e(TAG, "Car not connected or not supported", e);
    137         }
    138     }
    139 
    140     private void initPermissions() {
    141         Set<String> missingPermissions = checkExistingPermissions();
    142         if (!missingPermissions.isEmpty()) {
    143             requestPermissions(missingPermissions);
    144         } else {
    145             initSensors();
    146         }
    147     }
    148 
    149     private Set<String> checkExistingPermissions() {
    150         Set<String> missingPermissions = new HashSet<String>();
    151         for (String permission : REQUIRED_PERMISSIONS) {
    152             if (mActivity.checkSelfPermission(permission)
    153                 == PackageManager.PERMISSION_GRANTED) {
    154                 mActivePermissions.add(permission);
    155             } else {
    156                 missingPermissions.add(permission);
    157             }
    158         }
    159         return missingPermissions;
    160     }
    161 
    162     private void requestPermissions(Set<String> permissions) {
    163         Log.d(TAG, "requesting additional permissions=" + permissions);
    164 
    165         requestPermissions(permissions.toArray(new String[permissions.size()]),
    166                 KS_PERMISSIONS_REQUEST);
    167     }
    168 
    169     @Override
    170     public void onRequestPermissionsResult(int requestCode, String[] permissions,
    171             int[] grantResults) {
    172         Log.d(TAG, "onRequestPermissionsResult reqCode=" + requestCode);
    173         if (KS_PERMISSIONS_REQUEST == requestCode) {
    174             for (int i=0; i<permissions.length; i++) {
    175                 if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
    176                     mActivePermissions.add(permissions[i]);
    177                 }
    178             }
    179             initSensors();
    180         }
    181     }
    182 
    183     private void refreshUi() {
    184         String summaryString;
    185         synchronized (this) {
    186             List<String> summary = new ArrayList<>();
    187             for (Integer i : supportedSensors) {
    188                 CarSensorEvent event = mEventMap.get(i);
    189                 switch (i) {
    190                     case CarSensorManager.SENSOR_TYPE_COMPASS:
    191                         summary.add(getCompassString(event));
    192                         break;
    193                     case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
    194                         summary.add(getContext().getString(R.string.sensor_speed,
    195                                 getTimestamp(event),
    196                                 event == null ? mNaString : event.getCarSpeedData().carSpeed));
    197                         break;
    198                     case CarSensorManager.SENSOR_TYPE_RPM:
    199                         summary.add(getContext().getString(R.string.sensor_rpm,
    200                                 getTimestamp(event),
    201                                 event == null ? mNaString : event.getRpmData().rpm));
    202                         break;
    203                     case CarSensorManager.SENSOR_TYPE_ODOMETER:
    204                         summary.add(getContext().getString(R.string.sensor_odometer,
    205                                 getTimestamp(event),
    206                                 event == null ? mNaString : event.getOdometerData().kms));
    207                         break;
    208                     case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
    209                         String level = mNaString;
    210                         String range = mNaString;
    211                         String lowFuelWarning = mNaString;
    212                         if (event != null) {
    213                             CarSensorEvent.FuelLevelData fuelData = event.getFuelLevelData();
    214                             level = fuelData.level == -1 ? level : String.valueOf(fuelData.level);
    215                             range = fuelData.range == -1 ? range : String.valueOf(fuelData.range);
    216                             lowFuelWarning = String.valueOf(fuelData.lowFuelWarning);
    217                         }
    218                         summary.add(getContext().getString(R.string.sensor_fuel_level,
    219                                 getTimestamp(event), level, range, lowFuelWarning));
    220                         break;
    221                     case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
    222                         summary.add(getContext().getString(R.string.sensor_parking_break,
    223                                 getTimestamp(event),
    224                                 event == null ? mNaString :
    225                                 event.getParkingBrakeData().isEngaged));
    226                         break;
    227                     case CarSensorManager.SENSOR_TYPE_GEAR:
    228                         summary.add(getContext().getString(R.string.sensor_gear,
    229                                 getTimestamp(event),
    230                                 event == null ? mNaString : event.getGearData().gear));
    231                         break;
    232                     case CarSensorManager.SENSOR_TYPE_NIGHT:
    233                         summary.add(getContext().getString(R.string.sensor_night,
    234                                 getTimestamp(event),
    235                                 event == null ? mNaString : event.getNightData().isNightMode));
    236                         break;
    237                     case CarSensorManager.SENSOR_TYPE_LOCATION:
    238                         summary.add(getLocationString(event));
    239                         break;
    240                     case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
    241                         String drivingStatus = mNaString;
    242                         String binDrivingStatus = mNaString;
    243                         if (event != null) {
    244                             CarSensorEvent.DrivingStatusData drivingStatusData =
    245                                     event.getDrivingStatusData();
    246                             drivingStatus = String.valueOf(drivingStatusData.status);
    247                             binDrivingStatus = Integer.toBinaryString(drivingStatusData.status);
    248                         }
    249                         summary.add(getContext().getString(R.string.sensor_driving_status,
    250                                 getTimestamp(event), drivingStatus, binDrivingStatus));
    251                         break;
    252                     case CarSensorManager.SENSOR_TYPE_ENVIRONMENT:
    253                         String temperature = mNaString;
    254                         String pressure = mNaString;
    255                         if (event != null) {
    256                             CarSensorEvent.EnvironmentData env = event.getEnvironmentData();
    257                             temperature = Float.isNaN(env.temperature) ? temperature :
    258                                     String.valueOf(env.temperature);
    259                             pressure = Float.isNaN(env.pressure) ? pressure :
    260                                     String.valueOf(env.pressure);
    261                         }
    262                         summary.add(getContext().getString(R.string.sensor_environment,
    263                                 getTimestamp(event), temperature, pressure));
    264                         break;
    265                     case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
    266                         summary.add(getAccelerometerString(event));
    267                         break;
    268                     case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE:
    269                         summary.add(getGpsSatelliteString(event));
    270                         break;
    271                     case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
    272                         summary.add(getGyroscopeString(event));
    273                         break;
    274                     default:
    275                         // Should never happen.
    276                         Log.w(TAG, "Unrecognized event type: " + i);
    277                 }
    278             }
    279             summaryString = TextUtils.join("\n", summary);
    280         }
    281         mHandler.post(new Runnable() {
    282             @Override
    283             public void run() {
    284                 mSensorInfo.setText(summaryString);
    285             }
    286         });
    287     }
    288 
    289     private String getTimestamp(CarSensorEvent event) {
    290         if (event == null) {
    291             return mNaString;
    292         }
    293         return mDateFormat.format(new Date(event.timestamp / 1000L));
    294     }
    295 
    296     private String getCompassString(CarSensorEvent event) {
    297         String bear = mNaString;
    298         String pitch = mNaString;
    299         String roll = mNaString;
    300         if (event != null) {
    301             CarSensorEvent.CompassData compass = event.getCompassData();
    302             bear = Float.isNaN(compass.bearing) ? bear : String.valueOf(compass.bearing);
    303             pitch = Float.isNaN(compass.pitch) ? pitch : String.valueOf(compass.pitch);
    304             roll = Float.isNaN(compass.roll) ? roll : String.valueOf(compass.roll);
    305         }
    306         return getContext().getString(R.string.sensor_compass,
    307                 getTimestamp(event), bear, pitch, roll);
    308     }
    309 
    310     private String getGyroscopeString(CarSensorEvent event) {
    311         String x = mNaString;
    312         String y = mNaString;
    313         String z = mNaString;
    314         if (event != null) {
    315             CarSensorEvent.GyroscopeData gyro = event.getGyroscopeData();
    316             x = Float.isNaN(gyro.x) ? x : String.valueOf(gyro.x);
    317             y = Float.isNaN(gyro.y) ? y : String.valueOf(gyro.y);
    318             z = Float.isNaN(gyro.z) ? z : String.valueOf(gyro.z);
    319         }
    320         return getContext().getString(R.string.sensor_gyroscope,
    321                 getTimestamp(event), x, y, z);
    322     }
    323 
    324     private String getAccelerometerString(CarSensorEvent event) {
    325         String x = mNaString;
    326         String y = mNaString;
    327         String z = mNaString;
    328         if (event != null) {
    329             CarSensorEvent.AccelerometerData gyro = event.getAccelerometerData();
    330             x = Float.isNaN(gyro.x) ? x : String.valueOf(gyro.x);
    331             y = Float.isNaN(gyro.y) ? y : String.valueOf(gyro.y);
    332             z = Float.isNaN(gyro.z) ? z : String.valueOf(gyro.z);
    333         }
    334         return getContext().getString(R.string.sensor_accelerometer,
    335                 getTimestamp(event), x, y, z);
    336     }
    337 
    338     private String getLocationString(CarSensorEvent event) {
    339         String lat = mNaString;
    340         String lon = mNaString;
    341         String accuracy = mNaString;
    342         String alt = mNaString;
    343         String speed = mNaString;
    344         String bearing = mNaString;
    345         if (event != null) {
    346             Location location = event.getLocation(null);
    347             lat = String.valueOf(location.getLatitude());
    348             lon = String.valueOf(location.getLongitude());
    349             accuracy = location.hasAccuracy() ? String.valueOf(location.getAccuracy()) : accuracy;
    350             alt = location.hasAltitude() ? String.valueOf(location.getAltitude()) : alt;
    351             speed = location.hasSpeed() ? String.valueOf(location.getSpeed()) : speed;
    352             bearing = location.hasBearing() ? String.valueOf(location.getBearing()) : bearing;
    353         }
    354         return getContext().getString(R.string.sensor_location,
    355                 getTimestamp(event), lat, lon, accuracy, alt, speed, bearing);
    356     }
    357 
    358     private String getGpsSatelliteString(CarSensorEvent event) {
    359         String inUse = mNaString;
    360         String inView = mNaString;
    361         String perSattelite = "";
    362         if (event != null) {
    363             CarSensorEvent.GpsSatelliteData gpsData = event.getGpsSatelliteData(true);
    364             inUse = gpsData.numberInUse != -1 ? String.valueOf(gpsData.numberInUse) : inUse;
    365             inView = gpsData.numberInView != -1 ? String.valueOf(gpsData.numberInView) : inView;
    366             List<String> perSatteliteList = new ArrayList<>();
    367             int num = gpsData.usedInFix.length;
    368             for (int i=0; i<num; i++) {
    369                 perSatteliteList.add(getContext().getString(R.string.sensor_single_gps_satellite,
    370                         i+1, gpsData.usedInFix[i], gpsData.prn[i], gpsData.snr[i],
    371                         gpsData.azimuth[i], gpsData.elevation[i]));
    372             }
    373             perSattelite = TextUtils.join(", ", perSatteliteList);
    374         }
    375         return getContext().getString(R.string.sensor_gps,
    376                 getTimestamp(event), inView, inUse, perSattelite);
    377     }
    378 }
    379