bootanalyze: fix errata am: 40c290051f
am: 7c582428d6
Change-Id: Ic076085687dd6869808b09cf0fdd82fe19957a77
diff --git a/.gitignore b/.gitignore
index 5bf05e5..b292847 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@
.classpath
*.iml
gen/
+*.pyc
+__pycache__
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1c6a5b1..4bf7e9a 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -51,6 +51,8 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/PACKAGING/android.support.car*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car7_intermediates/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car_intermediates/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car7_intermediates/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car_intermediates/)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index 94c21be..b846481 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -28,15 +28,12 @@
field public static final deprecated java.lang.String PERMISSION_MOCK_VEHICLE_HAL = "android.car.permission.CAR_MOCK_VEHICLE_HAL";
field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
field public static final java.lang.String PERMISSION_VENDOR_EXTENSION = "android.car.permission.CAR_VENDOR_EXTENSION";
- field public static final java.lang.String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER";
- field public static final java.lang.String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
field public static final java.lang.String PROJECTION_SERVICE = "projection";
field public static final java.lang.String RADIO_SERVICE = "radio";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String TEST_SERVICE = "car-service-test";
field public static final java.lang.String VENDOR_EXTENSION_SERVICE = "vendor_extension";
field public static final int VERSION = 2; // 0x2
- field public static final java.lang.String VMS_SUBSCRIBER_SERVICE = "vehicle_map_subscriber_service";
}
public final class CarAppFocusManager {
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 80fabd0..6ed5d73 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -124,7 +124,6 @@
* @FutureFeature Cannot drop due to usage in non-flag protected place.
* @hide
*/
- @SystemApi
public static final String VMS_SUBSCRIBER_SERVICE = "vehicle_map_subscriber_service";
/**
@@ -231,7 +230,6 @@
* @hide
*/
@FutureFeature
- @SystemApi
public static final String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER";
/**
@@ -240,7 +238,6 @@
* @hide
*/
@FutureFeature
- @SystemApi
public static final String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
/**
@@ -582,9 +579,7 @@
manager = new CarCabinManager(binder, mContext, mEventHandler);
break;
case DIAGNOSTIC_SERVICE:
- if (FeatureConfiguration.ENABLE_DIAGNOSTIC) {
- manager = new CarDiagnosticManager(binder, mContext, mEventHandler);
- }
+ manager = new CarDiagnosticManager(binder, mContext, mEventHandler);
break;
case HVAC_SERVICE:
manager = new CarHvacManager(binder, mContext, mEventHandler);
diff --git a/car-lib/src/android/car/ICarBluetoothUserService.aidl b/car-lib/src/android/car/ICarBluetoothUserService.aidl
index b1f7a39..a906a3c 100644
--- a/car-lib/src/android/car/ICarBluetoothUserService.aidl
+++ b/car-lib/src/android/car/ICarBluetoothUserService.aidl
@@ -24,4 +24,5 @@
void closeBluetoothConnectionProxy();
boolean isBluetoothConnectionProxyAvailable(in int profile);
void bluetoothConnectToProfile(in int profile, in BluetoothDevice device);
+ void setProfilePriority(in int profile, in BluetoothDevice device, in int priority);
}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticEvent.java b/car-lib/src/android/car/hardware/CarDiagnosticEvent.java
index b970b23..1d82533 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticEvent.java
+++ b/car-lib/src/android/car/hardware/CarDiagnosticEvent.java
@@ -21,8 +21,10 @@
import android.car.annotation.FutureFeature;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.JsonWriter;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -78,7 +80,7 @@
int value = in.readInt();
intValues.put(key, value);
}
- dtc = (String)in.readValue(String.class.getClassLoader());
+ dtc = (String) in.readValue(String.class.getClassLoader());
// version 1 up to here
}
@@ -106,6 +108,48 @@
dest.writeValue(dtc);
}
+ public void writeToJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginObject();
+
+ jsonWriter.name("type");
+ switch (frameType) {
+ case CarDiagnosticManager.FRAME_TYPE_LIVE:
+ jsonWriter.value("live");
+ break;
+ case CarDiagnosticManager.FRAME_TYPE_FREEZE:
+ jsonWriter.value("freeze");
+ break;
+ default:
+ throw new IllegalStateException("unknown frameType " + frameType);
+ }
+
+ jsonWriter.name("timestamp").value(timestamp);
+
+ jsonWriter.name("intValues").beginArray();
+ for (int i = 0; i < intValues.size(); ++i) {
+ jsonWriter.beginObject();
+ jsonWriter.name("id").value(intValues.keyAt(i));
+ jsonWriter.name("value").value(intValues.valueAt(i));
+ jsonWriter.endObject();
+ }
+ jsonWriter.endArray();
+
+ jsonWriter.name("floatValues").beginArray();
+ for (int i = 0; i < floatValues.size(); ++i) {
+ jsonWriter.beginObject();
+ jsonWriter.name("id").value(floatValues.keyAt(i));
+ jsonWriter.name("value").value(floatValues.valueAt(i));
+ jsonWriter.endObject();
+ }
+ jsonWriter.endArray();
+
+ if (dtc != null) {
+ jsonWriter.name("stringValue").value(dtc);
+ }
+
+ jsonWriter.endObject();
+ }
+
public static final Parcelable.Creator<CarDiagnosticEvent> CREATOR =
new Parcelable.Creator<CarDiagnosticEvent>() {
public CarDiagnosticEvent createFromParcel(Parcel in) {
@@ -131,7 +175,7 @@
}
public static class Builder {
- private int mType = CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE;
+ private int mType = CarDiagnosticManager.FRAME_TYPE_LIVE;
private long mTimestamp = 0;
private SparseArray<Float> mFloatValues = new SparseArray<>();
private SparseIntArray mIntValues = new SparseIntArray();
@@ -142,11 +186,11 @@
}
public static Builder newLiveFrameBuilder() {
- return new Builder(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE);
+ return new Builder(CarDiagnosticManager.FRAME_TYPE_LIVE);
}
public static Builder newFreezeFrameBuilder() {
- return new Builder(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE);
+ return new Builder(CarDiagnosticManager.FRAME_TYPE_FREEZE);
}
public Builder atTimestamp(long timestamp) {
@@ -176,18 +220,19 @@
/**
* Returns a copy of this CarDiagnosticEvent with all vendor-specific sensors removed.
+ *
* @hide
*/
public CarDiagnosticEvent withVendorSensorsRemoved() {
SparseIntArray newIntValues = intValues.clone();
SparseArray<Float> newFloatValues = floatValues.clone();
- for(int i = 0; i < intValues.size(); ++i) {
+ for (int i = 0; i < intValues.size(); ++i) {
int key = intValues.keyAt(i);
if (key >= CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.LAST_SYSTEM) {
newIntValues.delete(key);
}
}
- for(int i = 0; i < floatValues.size(); ++i) {
+ for (int i = 0; i < floatValues.size(); ++i) {
int key = floatValues.keyAt(i);
if (key >= CarDiagnosticSensorIndices.Obd2FloatSensorIndex.LAST_SYSTEM) {
newFloatValues.delete(key);
@@ -197,11 +242,11 @@
}
public boolean isLiveFrame() {
- return CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE == frameType;
+ return CarDiagnosticManager.FRAME_TYPE_LIVE == frameType;
}
public boolean isFreezeFrame() {
- return CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE == frameType;
+ return CarDiagnosticManager.FRAME_TYPE_FREEZE == frameType;
}
public boolean isEmptyFrame() {
@@ -230,6 +275,82 @@
}
@Override
+ public boolean equals(Object otherObject) {
+ if (this == otherObject) {
+ return true;
+ }
+ if (null == otherObject) {
+ return false;
+ }
+ if (!(otherObject instanceof CarDiagnosticEvent)) {
+ return false;
+ }
+ CarDiagnosticEvent otherEvent = (CarDiagnosticEvent)otherObject;
+ if (otherEvent.frameType != frameType)
+ return false;
+ if (otherEvent.timestamp != timestamp)
+ return false;
+ if (otherEvent.intValues.size() != intValues.size())
+ return false;
+ if (otherEvent.floatValues.size() != floatValues.size())
+ return false;
+ if (!Objects.equals(dtc, otherEvent.dtc))
+ return false;
+ for (int i = 0; i < intValues.size(); ++i) {
+ int key = intValues.keyAt(i);
+ int otherKey = otherEvent.intValues.keyAt(i);
+ if (key != otherKey) {
+ return false;
+ }
+ int value = intValues.valueAt(i);
+ int otherValue = otherEvent.intValues.valueAt(i);
+ if (value != otherValue) {
+ return false;
+ }
+ }
+ for (int i = 0; i < floatValues.size(); ++i) {
+ int key = floatValues.keyAt(i);
+ int otherKey = otherEvent.floatValues.keyAt(i);
+ if (key != otherKey) {
+ return false;
+ }
+ float value = floatValues.valueAt(i);
+ float otherValue = otherEvent.floatValues.valueAt(i);
+ if (value != otherValue) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ Integer[] intKeys = new Integer[intValues.size()];
+ Integer[] floatKeys = new Integer[floatValues.size()];
+ Integer[] intValues = new Integer[intKeys.length];
+ Float[] floatValues = new Float[floatKeys.length];
+ for (int i = 0; i < intKeys.length; ++i) {
+ intKeys[i] = this.intValues.keyAt(i);
+ intValues[i] = this.intValues.valueAt(i);
+ }
+ for (int i = 0; i < floatKeys.length; ++i) {
+ floatKeys[i] = this.floatValues.keyAt(i);
+ floatValues[i] = this.floatValues.valueAt(i);
+ }
+ int intKeysHash = Objects.hash((Object[])intKeys);
+ int intValuesHash = Objects.hash((Object[])intValues);
+ int floatKeysHash = Objects.hash((Object[])floatKeys);
+ int floatValuesHash = Objects.hash((Object[])floatValues);
+ return Objects.hash(frameType,
+ timestamp,
+ dtc,
+ intKeysHash,
+ intValuesHash,
+ floatKeysHash,
+ floatValuesHash);
+ }
+
+ @Override
public String toString() {
return String.format(
"%s diagnostic frame {\n"
@@ -244,11 +365,13 @@
floatValues.toString());
}
- public int getSystemIntegerSensor(@CarDiagnosticSensorIndices.IntegerSensorIndex int sensor, int defaultValue) {
+ public int getSystemIntegerSensor(
+ @CarDiagnosticSensorIndices.IntegerSensorIndex int sensor, int defaultValue) {
return intValues.get(sensor, defaultValue);
}
- public float getSystemFloatSensor(@CarDiagnosticSensorIndices.FloatSensorIndex int sensor, float defaultValue) {
+ public float getSystemFloatSensor(
+ @CarDiagnosticSensorIndices.FloatSensorIndex int sensor, float defaultValue) {
return floatValues.get(sensor, defaultValue);
}
@@ -260,33 +383,35 @@
return floatValues.get(sensor, defaultValue);
}
- public @Nullable Integer getSystemIntegerSensor(@CarDiagnosticSensorIndices.IntegerSensorIndex int sensor) {
+ public @Nullable Integer getSystemIntegerSensor(
+ @CarDiagnosticSensorIndices.IntegerSensorIndex int sensor) {
int index = intValues.indexOfKey(sensor);
- if(index < 0) return null;
+ if (index < 0) return null;
return intValues.valueAt(index);
}
- public @Nullable Float getSystemFloatSensor(@CarDiagnosticSensorIndices.FloatSensorIndex int sensor) {
+ public @Nullable Float getSystemFloatSensor(
+ @CarDiagnosticSensorIndices.FloatSensorIndex int sensor) {
int index = floatValues.indexOfKey(sensor);
- if(index < 0) return null;
+ if (index < 0) return null;
return floatValues.valueAt(index);
}
public @Nullable Integer getVendorIntegerSensor(int sensor) {
int index = intValues.indexOfKey(sensor);
- if(index < 0) return null;
+ if (index < 0) return null;
return intValues.valueAt(index);
}
public @Nullable Float getVendorFloatSensor(int sensor) {
int index = floatValues.indexOfKey(sensor);
- if(index < 0) return null;
+ if (index < 0) return null;
return floatValues.valueAt(index);
}
/**
- * Represents possible states of the fuel system;
- * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_SYSTEM_STATUS}
+ * Represents possible states of the fuel system; see {@link
+ * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_SYSTEM_STATUS}
*/
public static final class FuelSystemStatus {
private FuelSystemStatus() {}
@@ -309,8 +434,8 @@
}
/**
- * Represents possible states of the secondary air system;
- * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
+ * Represents possible states of the secondary air system; see {@link
+ * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
*/
public static final class SecondaryAirStatus {
private SecondaryAirStatus() {}
@@ -331,8 +456,8 @@
}
/**
- * Represents possible types of fuel;
- * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_TYPE}
+ * Represents possible types of fuel; see {@link
+ * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_TYPE}
*/
public static final class FuelType {
private FuelType() {}
@@ -393,9 +518,9 @@
}
/**
- * Represents possible states of the ignition monitors on the vehicle;
- * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_MONITORS_SUPPORTED}
- * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_SPECIFIC_MONITORS}
+ * Represents possible states of the ignition monitors on the vehicle; see {@link
+ * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_MONITORS_SUPPORTED} see {@link
+ * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_SPECIFIC_MONITORS}
*/
public static final class IgnitionMonitors {
public static final class IgnitionMonitor {
@@ -407,26 +532,16 @@
this.incomplete = incomplete;
}
- public static final class Builder {
- private int mAvailableBitmask;
- private int mIncompleteBitmask;
+ public static final class Decoder {
+ private final int mAvailableBitmask;
+ private final int mIncompleteBitmask;
- Builder() {
- mAvailableBitmask = 0;
- mIncompleteBitmask = 0;
+ Decoder(int availableBitmask, int incompleteBitmask) {
+ mAvailableBitmask = availableBitmask;
+ mIncompleteBitmask = incompleteBitmask;
}
- public Builder withAvailableBitmask(int bitmask) {
- mAvailableBitmask = bitmask;
- return this;
- }
-
- public Builder withIncompleteBitmask(int bitmask) {
- mIncompleteBitmask = bitmask;
- return this;
- }
-
- public IgnitionMonitor buildForValue(int value) {
+ public IgnitionMonitor fromValue(int value) {
boolean available = (0 != (value & mAvailableBitmask));
boolean incomplete = (0 != (value & mIncompleteBitmask));
@@ -449,36 +564,29 @@
static final int MISFIRE_AVAILABLE = 0x1 << 4;
static final int MISFIRE_INCOMPLETE = 0x1 << 5;
- static final IgnitionMonitor.Builder COMPONENTS_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(COMPONENTS_AVAILABLE)
- .withIncompleteBitmask(COMPONENTS_INCOMPLETE);
+ static final IgnitionMonitor.Decoder COMPONENTS_DECODER =
+ new IgnitionMonitor.Decoder(COMPONENTS_AVAILABLE, COMPONENTS_INCOMPLETE);
- static final IgnitionMonitor.Builder FUEL_SYSTEM_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(FUEL_SYSTEM_AVAILABLE)
- .withIncompleteBitmask(FUEL_SYSTEM_INCOMPLETE);
+ static final IgnitionMonitor.Decoder FUEL_SYSTEM_DECODER =
+ new IgnitionMonitor.Decoder(FUEL_SYSTEM_AVAILABLE, FUEL_SYSTEM_INCOMPLETE);
- static final IgnitionMonitor.Builder MISFIRE_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(MISFIRE_AVAILABLE)
- .withIncompleteBitmask(MISFIRE_INCOMPLETE);
+ static final IgnitionMonitor.Decoder MISFIRE_DECODER =
+ new IgnitionMonitor.Decoder(MISFIRE_AVAILABLE, MISFIRE_INCOMPLETE);
CommonIgnitionMonitors(int bitmask) {
- components = COMPONENTS_BUILDER.buildForValue(bitmask);
- fuelSystem = FUEL_SYSTEM_BUILDER.buildForValue(bitmask);
- misfire = MISFIRE_BUILDER.buildForValue(bitmask);
+ components = COMPONENTS_DECODER.fromValue(bitmask);
+ fuelSystem = FUEL_SYSTEM_DECODER.fromValue(bitmask);
+ misfire = MISFIRE_DECODER.fromValue(bitmask);
}
public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() {
- if (this instanceof SparkIgnitionMonitors)
- return (SparkIgnitionMonitors)this;
+ if (this instanceof SparkIgnitionMonitors) return (SparkIgnitionMonitors) this;
return null;
}
public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() {
if (this instanceof CompressionIgnitionMonitors)
- return (CompressionIgnitionMonitors)this;
+ return (CompressionIgnitionMonitors) this;
return null;
}
}
@@ -517,56 +625,45 @@
static final int CATALYST_AVAILABLE = 0x1 << 20;
static final int CATALYST_INCOMPLETE = 0x1 << 21;
- static final IgnitionMonitor.Builder EGR_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(EGR_AVAILABLE)
- .withIncompleteBitmask(EGR_INCOMPLETE);
+ static final IgnitionMonitor.Decoder EGR_DECODER =
+ new IgnitionMonitor.Decoder(EGR_AVAILABLE, EGR_INCOMPLETE);
- static final IgnitionMonitor.Builder OXYGEN_SENSOR_HEATER_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(OXYGEN_SENSOR_HEATER_AVAILABLE)
- .withIncompleteBitmask(OXYGEN_SENSOR_HEATER_INCOMPLETE);
+ static final IgnitionMonitor.Decoder OXYGEN_SENSOR_HEATER_DECODER =
+ new IgnitionMonitor.Decoder(OXYGEN_SENSOR_HEATER_AVAILABLE,
+ OXYGEN_SENSOR_HEATER_INCOMPLETE);
- static final IgnitionMonitor.Builder OXYGEN_SENSOR_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(OXYGEN_SENSOR_AVAILABLE)
- .withIncompleteBitmask(OXYGEN_SENSOR_INCOMPLETE);
+ static final IgnitionMonitor.Decoder OXYGEN_SENSOR_DECODER =
+ new IgnitionMonitor.Decoder(OXYGEN_SENSOR_AVAILABLE, OXYGEN_SENSOR_INCOMPLETE);
- static final IgnitionMonitor.Builder AC_REFRIGERANT_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(AC_REFRIGERANT_AVAILABLE)
- .withIncompleteBitmask(AC_REFRIGERANT_INCOMPLETE);
+ static final IgnitionMonitor.Decoder AC_REFRIGERANT_DECODER =
+ new IgnitionMonitor.Decoder(AC_REFRIGERANT_AVAILABLE,
+ AC_REFRIGERANT_INCOMPLETE);
- static final IgnitionMonitor.Builder SECONDARY_AIR_SYSTEM_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(SECONDARY_AIR_SYSTEM_AVAILABLE)
- .withIncompleteBitmask(SECONDARY_AIR_SYSTEM_INCOMPLETE);
+ static final IgnitionMonitor.Decoder SECONDARY_AIR_SYSTEM_DECODER =
+ new IgnitionMonitor.Decoder(SECONDARY_AIR_SYSTEM_AVAILABLE,
+ SECONDARY_AIR_SYSTEM_INCOMPLETE);
- static final IgnitionMonitor.Builder EVAPORATIVE_SYSTEM_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(EVAPORATIVE_SYSTEM_AVAILABLE)
- .withIncompleteBitmask(EVAPORATIVE_SYSTEM_INCOMPLETE);
+ static final IgnitionMonitor.Decoder EVAPORATIVE_SYSTEM_DECODER =
+ new IgnitionMonitor.Decoder(EVAPORATIVE_SYSTEM_AVAILABLE,
+ EVAPORATIVE_SYSTEM_INCOMPLETE);
- static final IgnitionMonitor.Builder HEATED_CATALYST_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(HEATED_CATALYST_AVAILABLE)
- .withIncompleteBitmask(HEATED_CATALYST_INCOMPLETE);
+ static final IgnitionMonitor.Decoder HEATED_CATALYST_DECODER =
+ new IgnitionMonitor.Decoder(HEATED_CATALYST_AVAILABLE,
+ HEATED_CATALYST_INCOMPLETE);
- static final IgnitionMonitor.Builder CATALYST_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(CATALYST_AVAILABLE)
- .withIncompleteBitmask(CATALYST_INCOMPLETE);
+ static final IgnitionMonitor.Decoder CATALYST_DECODER =
+ new IgnitionMonitor.Decoder(CATALYST_AVAILABLE, CATALYST_INCOMPLETE);
SparkIgnitionMonitors(int bitmask) {
super(bitmask);
- EGR = EGR_BUILDER.buildForValue(bitmask);
- oxygenSensorHeater = OXYGEN_SENSOR_HEATER_BUILDER.buildForValue(bitmask);
- oxygenSensor = OXYGEN_SENSOR_BUILDER.buildForValue(bitmask);
- ACRefrigerant = AC_REFRIGERANT_BUILDER.buildForValue(bitmask);
- secondaryAirSystem = SECONDARY_AIR_SYSTEM_BUILDER.buildForValue(bitmask);
- evaporativeSystem = EVAPORATIVE_SYSTEM_BUILDER.buildForValue(bitmask);
- heatedCatalyst = HEATED_CATALYST_BUILDER.buildForValue(bitmask);
- catalyst = CATALYST_BUILDER.buildForValue(bitmask);
+ EGR = EGR_DECODER.fromValue(bitmask);
+ oxygenSensorHeater = OXYGEN_SENSOR_HEATER_DECODER.fromValue(bitmask);
+ oxygenSensor = OXYGEN_SENSOR_DECODER.fromValue(bitmask);
+ ACRefrigerant = AC_REFRIGERANT_DECODER.fromValue(bitmask);
+ secondaryAirSystem = SECONDARY_AIR_SYSTEM_DECODER.fromValue(bitmask);
+ evaporativeSystem = EVAPORATIVE_SYSTEM_DECODER.fromValue(bitmask);
+ heatedCatalyst = HEATED_CATALYST_DECODER.fromValue(bitmask);
+ catalyst = CATALYST_DECODER.fromValue(bitmask);
}
}
@@ -596,69 +693,66 @@
static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16;
static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17;
- static final IgnitionMonitor.Builder EGR_OR_VVT_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(EGR_OR_VVT_AVAILABLE)
- .withIncompleteBitmask(EGR_OR_VVT_INCOMPLETE);
+ static final IgnitionMonitor.Decoder EGR_OR_VVT_DECODER =
+ new IgnitionMonitor.Decoder(EGR_OR_VVT_AVAILABLE, EGR_OR_VVT_INCOMPLETE);
- static final IgnitionMonitor.Builder PM_FILTER_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(PM_FILTER_AVAILABLE)
- .withIncompleteBitmask(PM_FILTER_INCOMPLETE);
+ static final IgnitionMonitor.Decoder PM_FILTER_DECODER =
+ new IgnitionMonitor.Decoder(PM_FILTER_AVAILABLE, PM_FILTER_INCOMPLETE);
- static final IgnitionMonitor.Builder EXHAUST_GAS_SENSOR_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(EXHAUST_GAS_SENSOR_AVAILABLE)
- .withIncompleteBitmask(EXHAUST_GAS_SENSOR_INCOMPLETE);
+ static final IgnitionMonitor.Decoder EXHAUST_GAS_SENSOR_DECODER =
+ new IgnitionMonitor.Decoder(EXHAUST_GAS_SENSOR_AVAILABLE,
+ EXHAUST_GAS_SENSOR_INCOMPLETE);
- static final IgnitionMonitor.Builder BOOST_PRESSURE_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(BOOST_PRESSURE_AVAILABLE)
- .withIncompleteBitmask(BOOST_PRESSURE_INCOMPLETE);
+ static final IgnitionMonitor.Decoder BOOST_PRESSURE_DECODER =
+ new IgnitionMonitor.Decoder(BOOST_PRESSURE_AVAILABLE,
+ BOOST_PRESSURE_INCOMPLETE);
- static final IgnitionMonitor.Builder NOx_SCR_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(NOx_SCR_AVAILABLE)
- .withIncompleteBitmask(NOx_SCR_INCOMPLETE);
+ static final IgnitionMonitor.Decoder NOx_SCR_DECODER =
+ new IgnitionMonitor.Decoder(NOx_SCR_AVAILABLE, NOx_SCR_INCOMPLETE);
- static final IgnitionMonitor.Builder NMHC_CATALYST_BUILDER =
- new IgnitionMonitor.Builder()
- .withAvailableBitmask(NMHC_CATALYST_AVAILABLE)
- .withIncompleteBitmask(NMHC_CATALYST_INCOMPLETE);
+ static final IgnitionMonitor.Decoder NMHC_CATALYST_DECODER =
+ new IgnitionMonitor.Decoder(NMHC_CATALYST_AVAILABLE, NMHC_CATALYST_INCOMPLETE);
CompressionIgnitionMonitors(int bitmask) {
super(bitmask);
- EGROrVVT = EGR_OR_VVT_BUILDER.buildForValue(bitmask);
- PMFilter = PM_FILTER_BUILDER.buildForValue(bitmask);
- exhaustGasSensor = EXHAUST_GAS_SENSOR_BUILDER.buildForValue(bitmask);
- boostPressure = BOOST_PRESSURE_BUILDER.buildForValue(bitmask);
- NOxSCR = NOx_SCR_BUILDER.buildForValue(bitmask);
- NMHCCatalyst = NMHC_CATALYST_BUILDER.buildForValue(bitmask);
+ EGROrVVT = EGR_OR_VVT_DECODER.fromValue(bitmask);
+ PMFilter = PM_FILTER_DECODER.fromValue(bitmask);
+ exhaustGasSensor = EXHAUST_GAS_SENSOR_DECODER.fromValue(bitmask);
+ boostPressure = BOOST_PRESSURE_DECODER.fromValue(bitmask);
+ NOxSCR = NOx_SCR_DECODER.fromValue(bitmask);
+ NMHCCatalyst = NMHC_CATALYST_DECODER.fromValue(bitmask);
}
}
}
public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() {
- return getSystemIntegerSensor(CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS);
+ return getSystemIntegerSensor(
+ CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS);
}
public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() {
- return getSystemIntegerSensor(CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
+ return getSystemIntegerSensor(
+ CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
}
public @Nullable IgnitionMonitors.CommonIgnitionMonitors getIgnitionMonitors() {
- Integer ignitionMonitorsType = getSystemIntegerSensor(
- CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED);
- Integer ignitionMonitorsBitmask = getSystemIntegerSensor(
- CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS);
+ Integer ignitionMonitorsType =
+ getSystemIntegerSensor(
+ CarDiagnosticSensorIndices.Obd2IntegerSensorIndex
+ .IGNITION_MONITORS_SUPPORTED);
+ Integer ignitionMonitorsBitmask =
+ getSystemIntegerSensor(
+ CarDiagnosticSensorIndices.Obd2IntegerSensorIndex
+ .IGNITION_SPECIFIC_MONITORS);
if (null == ignitionMonitorsType) return null;
if (null == ignitionMonitorsBitmask) return null;
switch (ignitionMonitorsType) {
- case 0: return new IgnitionMonitors.SparkIgnitionMonitors(
- ignitionMonitorsBitmask);
- case 1: return new IgnitionMonitors.CompressionIgnitionMonitors(
- ignitionMonitorsBitmask);
- default: return null;
+ case 0:
+ return new IgnitionMonitors.SparkIgnitionMonitors(ignitionMonitorsBitmask);
+ case 1:
+ return new IgnitionMonitors.CompressionIgnitionMonitors(ignitionMonitorsBitmask);
+ default:
+ return null;
}
}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticManager.java b/car-lib/src/android/car/hardware/CarDiagnosticManager.java
index 6d8b5ba..0444c14 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticManager.java
+++ b/car-lib/src/android/car/hardware/CarDiagnosticManager.java
@@ -16,6 +16,7 @@
package android.car.hardware;
+import android.annotation.IntDef;
import android.car.Car;
import android.car.CarApiUtil;
import android.car.CarLibLog;
@@ -32,6 +33,8 @@
import com.android.car.internal.CarRatedListeners;
import com.android.car.internal.SingleMessageHandler;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -40,8 +43,17 @@
/** API for monitoring car diagnostic data. */
/** @hide */
public final class CarDiagnosticManager implements CarManagerBase {
- public static final int FRAME_TYPE_FLAG_LIVE = 0;
- public static final int FRAME_TYPE_FLAG_FREEZE = 1;
+ public static final int FRAME_TYPE_LIVE = 0;
+ public static final int FRAME_TYPE_FREEZE = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FRAME_TYPE_LIVE, FRAME_TYPE_FREEZE})
+ public @interface FrameType {}
+
+ public static final @FrameType int FRAME_TYPES[] = {
+ FRAME_TYPE_LIVE,
+ FRAME_TYPE_FREEZE
+ };
private static final int MSG_DIAGNOSTIC_EVENTS = 0;
@@ -93,10 +105,10 @@
// OnDiagnosticEventListener registration
- private void assertFrameType(int frameType) {
+ private void assertFrameType(@FrameType int frameType) {
switch(frameType) {
- case FRAME_TYPE_FLAG_FREEZE:
- case FRAME_TYPE_FLAG_LIVE:
+ case FRAME_TYPE_FREEZE:
+ case FRAME_TYPE_LIVE:
return;
default:
throw new IllegalArgumentException(String.format(
@@ -113,7 +125,9 @@
* @throws CarNotConnectedException
* @throws IllegalArgumentException
*/
- public boolean registerListener(OnDiagnosticEventListener listener, int frameType, int rate)
+ public boolean registerListener(OnDiagnosticEventListener listener,
+ @FrameType int frameType,
+ int rate)
throws CarNotConnectedException, IllegalArgumentException {
assertFrameType(frameType);
synchronized(mActiveListeners) {
@@ -145,14 +159,15 @@
*/
public void unregisterListener(OnDiagnosticEventListener listener) {
synchronized(mActiveListeners) {
- for(int i = 0; i < mActiveListeners.size(); i++) {
- doUnregisterListenerLocked(listener, mActiveListeners.keyAt(i));
+ for(@FrameType int frameType : FRAME_TYPES) {
+ doUnregisterListenerLocked(listener, frameType);
}
}
}
- private void doUnregisterListenerLocked(OnDiagnosticEventListener listener, int sensor) {
- CarDiagnosticListeners listeners = mActiveListeners.get(sensor);
+ private void doUnregisterListenerLocked(OnDiagnosticEventListener listener,
+ @FrameType int frameType) {
+ CarDiagnosticListeners listeners = mActiveListeners.get(frameType);
if (listeners != null) {
boolean needsServerUpdate = false;
if (listeners.contains(listener)) {
@@ -160,15 +175,15 @@
}
if (listeners.isEmpty()) {
try {
- mService.unregisterDiagnosticListener(sensor,
+ mService.unregisterDiagnosticListener(frameType,
mListenerToService);
} catch (RemoteException e) {
//ignore
}
- mActiveListeners.remove(sensor);
+ mActiveListeners.remove(frameType);
} else if (needsServerUpdate) {
try {
- registerOrUpdateDiagnosticListener(sensor, listeners.getRate());
+ registerOrUpdateDiagnosticListener(frameType, listeners.getRate());
} catch (CarNotConnectedException e) {
// ignore
}
@@ -176,7 +191,7 @@
}
}
- private boolean registerOrUpdateDiagnosticListener(int frameType, int rate)
+ private boolean registerOrUpdateDiagnosticListener(@FrameType int frameType, int rate)
throws CarNotConnectedException {
try {
return mService.registerOrUpdateDiagnosticListener(frameType, rate, mListenerToService);
@@ -256,6 +271,72 @@
return false;
}
+ /**
+ * Returns true if this vehicle supports sending live frame information.
+ * @return
+ * @throws CarNotConnectedException
+ */
+ public boolean isLiveFrameSupported() throws CarNotConnectedException {
+ try {
+ return mService.isLiveFrameSupported();
+ } catch (IllegalStateException e) {
+ CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this vehicle supports sending freeze frame information.
+ * @return
+ * @throws CarNotConnectedException
+ */
+ public boolean isFreezeFrameSupported() throws CarNotConnectedException {
+ try {
+ return mService.isFreezeFrameSupported();
+ } catch (IllegalStateException e) {
+ CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this vehicle supports retrieving freeze frame timestamps.
+ * This is only meaningful if freeze frame data is also supported.
+ * @return
+ * @throws CarNotConnectedException
+ */
+ public boolean isFreezeFrameTimestampSupported() throws CarNotConnectedException {
+ try {
+ return mService.isFreezeFrameTimestampSupported();
+ } catch (IllegalStateException e) {
+ CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this vehicle supports clearing freeze frame timestamps.
+ * This is only meaningful if freeze frame data is also supported.
+ * @return
+ * @throws CarNotConnectedException
+ */
+ public boolean isFreezeFrameClearSupported() throws CarNotConnectedException {
+ try {
+ return mService.isFreezeFrameClearSupported();
+ } catch (IllegalStateException e) {
+ CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return false;
+ }
+
private static class CarDiagnosticEventListenerToService
extends ICarDiagnosticEventListener.Stub {
private final WeakReference<CarDiagnosticManager> mManager;
@@ -284,10 +365,10 @@
}
void onDiagnosticEvent(final CarDiagnosticEvent event) {
- // throw away old sensor data as oneway binder call can change order.
+ // throw away old data as oneway binder call can change order.
long updateTime = event.timestamp;
if (updateTime < mLastUpdateTime) {
- Log.w(CarLibLog.TAG_DIAGNOSTIC, "dropping old sensor data");
+ Log.w(CarLibLog.TAG_DIAGNOSTIC, "dropping old data");
return;
}
mLastUpdateTime = updateTime;
diff --git a/car-lib/src/android/car/hardware/ICarDiagnostic.aidl b/car-lib/src/android/car/hardware/ICarDiagnostic.aidl
index 098d2d4..3afffc5 100644
--- a/car-lib/src/android/car/hardware/ICarDiagnostic.aidl
+++ b/car-lib/src/android/car/hardware/ICarDiagnostic.aidl
@@ -52,4 +52,24 @@
*/
void unregisterDiagnosticListener(int frameType,
in ICarDiagnosticEventListener callback) = 6;
-}
\ No newline at end of file
+
+ /**
+ * Returns whether the underlying HAL supports live frames.
+ */
+ boolean isLiveFrameSupported() = 7;
+
+ /**
+ * Returns whether the underlying HAL supports freeze frames.
+ */
+ boolean isFreezeFrameSupported() = 8;
+
+ /**
+ * Returns whether the underlying HAL supports retrieving freeze frame timestamps.
+ */
+ boolean isFreezeFrameTimestampSupported() = 9;
+
+ /**
+ * Returns whether the underlying HAL supports clearing freeze frames.
+ */
+ boolean isFreezeFrameClearSupported() = 10;
+}
diff --git a/car-lib/src/android/car/vms/IVmsPublisherService.aidl b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
index 26b6e52..3312794 100644
--- a/car-lib/src/android/car/vms/IVmsPublisherService.aidl
+++ b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
@@ -40,4 +40,11 @@
* Sets which layers the publisher can publish under which dependencties.
*/
oneway void setLayersOffering(in IBinder token, in VmsLayersOffering offering) = 2;
+
+ /**
+ * The first time a publisher calls this API it will store the publisher info and assigns the
+ * publisher a static ID. Between reboots, subsequent calls with the same publisher info will
+ * return the same ID so that a restarting process can obtain the same ID as it had before.
+ */
+ int getPublisherStaticId(in byte[] publisherInfo) = 3;
}
diff --git a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
index 236ae5a..9234134 100644
--- a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
+++ b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
@@ -52,8 +52,12 @@
in IVmsSubscriberClient listener) = 3;
/**
- * Tells the VmsSubscriberService a client requests the list of available layers.
- * The service should call the client's onLayersAvailabilityChange in response.
+ * Returns a list of available layers from the closure of the publishers offerings.
*/
List<VmsLayer> getAvailableLayers() = 4;
+
+ /**
+ * Returns a the publisher information for a publisher ID.
+ */
+ byte[] getPublisherInfo(in int publisherId) = 5;
}
diff --git a/car-lib/src/android/car/vms/VmsLayer.java b/car-lib/src/android/car/vms/VmsLayer.java
index 702daec..afd0ae7 100644
--- a/car-lib/src/android/car/vms/VmsLayer.java
+++ b/car-lib/src/android/car/vms/VmsLayer.java
@@ -24,8 +24,10 @@
/**
* A VMS Layer which can be subscribed to by VMS clients.
- * Consists of the layer ID and the layer version.
+ * Consists of the layer ID and the layer major version.
*
+ * This class does not contain the minor version since all minor version are backward and forward
+ * compatible and should not be used for routing messages.
* @hide
*/
@FutureFeature
diff --git a/car-lib/src/android/car/vms/VmsLayerDependency.java b/car-lib/src/android/car/vms/VmsLayerDependency.java
index bb588eb..e14c7ec 100644
--- a/car-lib/src/android/car/vms/VmsLayerDependency.java
+++ b/car-lib/src/android/car/vms/VmsLayerDependency.java
@@ -80,6 +80,10 @@
}
};
+ public String toString() {
+ return "VmsLayerDependency{ Layer: " + mLayer + " Dependency: " + mDependency + "}";
+ }
+
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeParcelable(mLayer, flags);
diff --git a/car-lib/src/android/car/vms/VmsLayersOffering.java b/car-lib/src/android/car/vms/VmsLayersOffering.java
index 12b3dd3..51a0b99 100644
--- a/car-lib/src/android/car/vms/VmsLayersOffering.java
+++ b/car-lib/src/android/car/vms/VmsLayersOffering.java
@@ -55,6 +55,11 @@
};
@Override
+ public String toString() {
+ return "VmsLayersOffering{" + mDependencies+ "}";
+ }
+
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeParcelableList(mDependencies, flags);
}
diff --git a/car-lib/src/android/car/vms/VmsPublisherClientService.java b/car-lib/src/android/car/vms/VmsPublisherClientService.java
index 2743ff1..ea265df 100644
--- a/car-lib/src/android/car/vms/VmsPublisherClientService.java
+++ b/car-lib/src/android/car/vms/VmsPublisherClientService.java
@@ -106,6 +106,41 @@
if (DBG) {
Log.d(TAG, "Publishing for layer : " + layer);
}
+
+ IBinder token = getTokenForPublisherServiceThreadSafe();
+
+ try {
+ mVmsPublisherService.publish(token, layer, payload);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to publish message: " + payload, e);
+ }
+ return false;
+ }
+
+ /**
+ * Uses the VmsPublisherService binder to set the layers offering.
+ *
+ * @param offering the layers that the publisher may publish.
+ * @return if the call to VmsPublisherService.setLayersOffering was successful.
+ */
+ public final boolean setLayersOffering(VmsLayersOffering offering) {
+ if (DBG) {
+ Log.d(TAG, "Setting layers offering : " + offering);
+ }
+
+ IBinder token = getTokenForPublisherServiceThreadSafe();
+
+ try {
+ mVmsPublisherService.setLayersOffering(token, offering);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to set layers offering: " + offering, e);
+ }
+ return false;
+ }
+
+ private IBinder getTokenForPublisherServiceThreadSafe() {
if (mVmsPublisherService == null) {
throw new IllegalStateException("VmsPublisherService not set.");
}
@@ -117,13 +152,24 @@
if (token == null) {
throw new IllegalStateException("VmsPublisherService does not have a valid token.");
}
- try {
- mVmsPublisherService.publish(token, layer, payload);
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "unable to publish message: " + payload, e);
+ return token;
+ }
+
+ public final int getPublisherStaticId(byte[] publisherInfo) {
+ if (mVmsPublisherService == null) {
+ throw new IllegalStateException("VmsPublisherService not set.");
}
- return false;
+ Integer publisherStaticId = null;
+ try {
+ Log.i(TAG, "Getting publisher static ID");
+ publisherStaticId = mVmsPublisherService.getPublisherStaticId(publisherInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to invoke binder method.", e);
+ }
+ if (publisherStaticId == null) {
+ throw new IllegalStateException("VmsPublisherService cannot get a publisher static ID.");
+ }
+ return publisherStaticId;
}
/**
diff --git a/car-lib/src/android/car/vms/VmsSubscriberManager.java b/car-lib/src/android/car/vms/VmsSubscriberManager.java
index d0b8c62..84405f4 100644
--- a/car-lib/src/android/car/vms/VmsSubscriberManager.java
+++ b/car-lib/src/android/car/vms/VmsSubscriberManager.java
@@ -57,6 +57,9 @@
/** Called when layers availability change */
void onLayersAvailabilityChange(List<VmsLayer> availableLayers);
+
+ /** Notifies the client of the disconnect event */
+ void onCarDisconnected();
}
/**
@@ -136,6 +139,8 @@
* Therefore, notifications from the {@link com.android.car.VmsSubscriberService} are received
* by the {@link #mIListener} and then forwarded to the {@link #mListener}.
*
+ * It is expected that this method is invoked just once during the lifetime of the object.
+ *
* @param listener subscriber listener that will handle onVmsMessageReceived events.
* @throws IllegalStateException if the listener was already set.
*/
@@ -152,13 +157,21 @@
}
/**
- * Removes the listener and unsubscribes from all the layer/version.
+ * Returns a serialized publisher information for a publisher ID.
*/
- public void clearListener() {
- synchronized (mListenerLock) {
- mListener = null;
+ public byte[] getPublisherInfo(int publisherId) throws CarNotConnectedException, IllegalStateException {
+ if (DBG) {
+ Log.d(TAG, "Getting all publishers info.");
}
- // TODO(antoniocortes): logic to unsubscribe from all the layer/version pairs.
+ try {
+ return mVmsSubscriberService.getPublisherInfo(publisherId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not connect: ", e);
+ throw new CarNotConnectedException(e);
+ } catch (IllegalStateException ex) {
+ Car.checkCarNotConnectedExceptionFromCarService(ex);
+ throw new IllegalStateException(ex);
+ }
}
/**
@@ -167,8 +180,7 @@
* @param layer the layer to subscribe to.
* @throws IllegalStateException if the listener was not set via {@link #setListener}.
*/
- public void subscribe(VmsLayer layer)
- throws CarNotConnectedException {
+ public void subscribe(VmsLayer layer) throws CarNotConnectedException {
if (DBG) {
Log.d(TAG, "Subscribing to layer: " + layer);
}
@@ -191,8 +203,7 @@
}
}
- public void subscribeAll()
- throws CarNotConnectedException {
+ public void subscribeAll() throws CarNotConnectedException {
if (DBG) {
Log.d(TAG, "Subscribing passively to all data messages");
}
@@ -244,6 +255,29 @@
}
}
+ public void unsubscribeAll() {
+ if (DBG) {
+ Log.d(TAG, "Unsubscribing passively from all data messages");
+ }
+ VmsSubscriberClientListener listener;
+ synchronized (mListenerLock) {
+ listener = mListener;
+ }
+ if (listener == null) {
+ Log.w(TAG, "unsubscribeAll: listener was not set, " +
+ "setListener must be called first.");
+ throw new IllegalStateException("Listener was not set.");
+ }
+ try {
+ mVmsSubscriberService.removeVmsSubscriberClientPassiveListener(mIListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister subscriber ", e);
+ // ignore
+ } catch (IllegalStateException ex) {
+ Car.hideCarNotConnectedExceptionFromCarService(ex);
+ }
+ }
+
private void dispatchOnReceiveMessage(VmsLayer layer, byte[] payload) {
VmsSubscriberClientListener listener;
synchronized (mListenerLock) {
@@ -271,7 +305,15 @@
/** @hide */
@Override
public void onCarDisconnected() {
- clearListener();
+ VmsSubscriberClientListener listener;
+ synchronized (mListenerLock) {
+ listener = mListener;
+ }
+ if (listener == null) {
+ Log.e(TAG, "Listener died, not dispatching event.");
+ return;
+ }
+ listener.onCarDisconnected();
}
private static final class VmsDataMessage {
diff --git a/car-lib/src_feature_current/com/android/car/internal/FeatureConfiguration.java b/car-lib/src_feature_current/com/android/car/internal/FeatureConfiguration.java
index 8371463..55da870 100644
--- a/car-lib/src_feature_current/com/android/car/internal/FeatureConfiguration.java
+++ b/car-lib/src_feature_current/com/android/car/internal/FeatureConfiguration.java
@@ -26,6 +26,4 @@
/** product configuration in CarInfoManager */
public static final boolean ENABLE_PRODUCT_CONFIGURATION_INFO = DEFAULT;
public static final boolean ENABLE_VEHICLE_MAP_SERVICE = DEFAULT;
- public static final boolean ENABLE_DIAGNOSTIC = DEFAULT;
- public static final boolean ENABLE_VEHICLE_HAL_V2_1 = DEFAULT;
}
diff --git a/car-lib/src_feature_future/com/android/car/internal/FeatureConfiguration.java b/car-lib/src_feature_future/com/android/car/internal/FeatureConfiguration.java
index 62bfd06..66cff60 100644
--- a/car-lib/src_feature_future/com/android/car/internal/FeatureConfiguration.java
+++ b/car-lib/src_feature_future/com/android/car/internal/FeatureConfiguration.java
@@ -26,6 +26,4 @@
/** product configuration in CarInfoManager */
public static final boolean ENABLE_PRODUCT_CONFIGURATION_INFO = DEFAULT;
public static final boolean ENABLE_VEHICLE_MAP_SERVICE = DEFAULT;
- public static final boolean ENABLE_DIAGNOSTIC = DEFAULT;
- public static final boolean ENABLE_VEHICLE_HAL_V2_1 = DEFAULT;
}
diff --git a/car-maps-placeholder/Android.mk b/car-maps-placeholder/Android.mk
index a8bcd04..5f0b93a 100644
--- a/car-maps-placeholder/Android.mk
+++ b/car-maps-placeholder/Android.mk
@@ -32,6 +32,6 @@
LOCAL_DEX_PREOPT := false
-include packages/services/Car/car-support-lib/car-support.mk
+include packages/apps/Car/libs/car-stream-ui-lib/car-stream-ui-lib.mk
include $(BUILD_PACKAGE)
diff --git a/car-support-lib/Android.mk b/car-support-lib/Android.mk
index 2f8400e..d0052e4 100644
--- a/car-support-lib/Android.mk
+++ b/car-support-lib/Android.mk
@@ -28,14 +28,13 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+# Build against the current public APIs of the SDK
+LOCAL_SDK_VERSION := current
+
LOCAL_MANIFEST_FILE := AndroidManifest.xml
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
LOCAL_JAVA_LIBRARIES += android.car\
- android-support-v4 \
- android-support-v7-appcompat \
- android-support-v7-recyclerview \
- android-support-v7-cardview \
android-support-annotations
# Specify 1.7 for backwards compatibility.
# Otherwise the lib won't be usable on pre-N devices
@@ -51,41 +50,18 @@
$(call dist-for-goals,dist_files,$(built_aar):android.support.car.aar)
endif
-# Build the resources.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.support.car-res
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_RESOURCE_DIR += frameworks/support/v7/appcompat/res
-LOCAL_RESOURCE_DIR += frameworks/support/v7/recyclerview/res
-LOCAL_RESOURCE_DIR += frameworks/support/v7/cardview/res
-
-LOCAL_AAPT_FLAGS := --auto-add-overlay \
- --extra-packages android.support.v7.appcompat \
- --extra-packages android.support.v7.recyclerview \
- --extra-packages android.support.v7.cardview
-
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_MANIFEST_FILE := AndroidManifest.xml
-
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Build support library.
# ---------------------------------------------
include $(CLEAR_VARS)
LOCAL_MODULE := android.support.car
+LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 \
- android-support-v7-appcompat \
- android-support-v7-recyclerview \
- android-support-v7-cardview \
- android-support-annotations
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-annotations
-LOCAL_JAVA_LIBRARIES += android.car \
- android.support.car-res
+LOCAL_JAVA_LIBRARIES += android.car
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -112,11 +88,6 @@
LOCAL_JAVA_LIBRARIES := \
android.car \
- android.support.car-res \
- android-support-v4 \
- android-support-v7-appcompat \
- android-support-v7-recyclerview \
- android-support-v7-cardview \
android-support-annotations
LOCAL_MODULE := android.support.car
diff --git a/car-support-lib/car-support.mk b/car-support-lib/car-support.mk
index 081d6a6..7feba67 100644
--- a/car-support-lib/car-support.mk
+++ b/car-support-lib/car-support.mk
@@ -22,35 +22,9 @@
LOCAL_AAPT_FLAGS += --auto-add-overlay
endif
-# Include car ui library, if not already included
+# Include car support library, if not already included
ifeq (,$(findstring android.support.car, $(LOCAL_STATIC_JAVA_LIBRARIES)))
-LOCAL_RESOURCE_DIR += \
- packages/services/Car/car-support-lib/res
-LOCAL_AAPT_FLAGS += --extra-packages android.support.car.ui
LOCAL_STATIC_JAVA_LIBRARIES += android.support.car
endif
-## Include transitive dependencies below
-
-# Include support-v7-appcompat, if not already included
-ifeq (,$(findstring android-support-v7-appcompat,$(LOCAL_STATIC_JAVA_LIBRARIES)))
-LOCAL_RESOURCE_DIR += frameworks/support/v7/appcompat/res
-LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
-endif
-
-# Include support-v7-recyclerview, if not already included
-ifeq (,$(findstring android-support-v7-recyclerview,$(LOCAL_STATIC_JAVA_LIBRARIES)))
-LOCAL_RESOURCE_DIR += frameworks/support/v7/recyclerview/res
-LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.recyclerview
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
-endif
-
-# Include support-v7-cardview, if not already included
-ifeq (,$(findstring android-support-v7-cardview,$(LOCAL_STATIC_JAVA_LIBRARIES)))
-LOCAL_RESOURCE_DIR += frameworks/support/v7/cardview/res
-LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.cardview
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-cardview
-endif
LOCAL_JAVA_LIBRARIES += android.car
-
diff --git a/car-support-lib/res/anim/car_fab_state_list_animator.xml b/car-support-lib/res/anim/car_fab_state_list_animator.xml
deleted file mode 100644
index a1a2240..0000000
--- a/car-support-lib/res/anim/car_fab_state_list_animator.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" >
- <set>
- <objectAnimator android:propertyName="translationZ"
- android:duration="@integer/car_fab_animation_duration"
- android:valueTo="16dp"
- android:valueType="floatType"/>
- </set>
- </item>
- <!-- base state -->
- <item android:state_focused="false">
- <set>
- <objectAnimator android:propertyName="translationZ"
- android:duration="@integer/car_fab_animation_duration"
- android:valueTo="0dp"
- android:valueType="floatType"/>
- </set>
- </item>
-</selector>
\ No newline at end of file
diff --git a/car-support-lib/res/anim/car_list_in.xml b/car-support-lib/res/anim/car_list_in.xml
deleted file mode 100644
index 29f9764..0000000
--- a/car-support-lib/res/anim/car_list_in.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate
- android:fromYDelta="15%"
- android:toYDelta="0%"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:duration="250"
- android:startOffset="150"/>
-
- <alpha
- android:fromAlpha="0.0"
- android:toAlpha="1.0"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:duration="100"
- android:startOffset="150" />
-</set>
diff --git a/car-support-lib/res/anim/car_list_out.xml b/car-support-lib/res/anim/car_list_out.xml
deleted file mode 100644
index 8675cd8..0000000
--- a/car-support-lib/res/anim/car_list_out.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate
- android:fromXDelta="0%"
- android:toXDelta="-8%"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:duration="150" />
-
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="0.0"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:duration="100"
- android:startOffset="50" />
-</set>
diff --git a/car-support-lib/res/anim/car_list_pop_out.xml b/car-support-lib/res/anim/car_list_pop_out.xml
deleted file mode 100644
index b81d61b..0000000
--- a/car-support-lib/res/anim/car_list_pop_out.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <alpha
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:fromAlpha="1.0"
- android:toAlpha="0.0"
- android:duration="100"
- android:startOffset="50"/>
-
- <scale
- android:fromXScale="1.0"
- android:toXScale="0.9"
- android:fromYScale="1.0"
- android:toYScale="0.9"
- android:pivotX="50%"
- android:pivotY="50%"
- android:duration="150" />
-</set>
\ No newline at end of file
diff --git a/car-support-lib/res/anim/sdk_list_out.xml b/car-support-lib/res/anim/sdk_list_out.xml
deleted file mode 100644
index d8dae80..0000000
--- a/car-support-lib/res/anim/sdk_list_out.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate
- android:fromXDelta="0%"
- android:toXDelta="-8%"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:duration="150"
- android:startOffset="250" />
-
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="0.0"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:duration="100"
- android:startOffset="300" />
-</set>
diff --git a/car-support-lib/res/drawable-hdpi/drawer_shadow.9.png b/car-support-lib/res/drawable-hdpi/drawer_shadow.9.png
deleted file mode 100644
index 8964dc4..0000000
--- a/car-support-lib/res/drawable-hdpi/drawer_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-hdpi/error_illustration.png b/car-support-lib/res/drawable-hdpi/error_illustration.png
deleted file mode 100644
index 61e5145..0000000
--- a/car-support-lib/res/drawable-hdpi/error_illustration.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-hdpi/ic_down.png b/car-support-lib/res/drawable-hdpi/ic_down.png
deleted file mode 100644
index 6f47204..0000000
--- a/car-support-lib/res/drawable-hdpi/ic_down.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-hdpi/ic_launcher.png b/car-support-lib/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 71c6d76..0000000
--- a/car-support-lib/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-hdpi/ic_remove_circle.png b/car-support-lib/res/drawable-hdpi/ic_remove_circle.png
deleted file mode 100644
index 1f40a6f..0000000
--- a/car-support-lib/res/drawable-hdpi/ic_remove_circle.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-hdpi/ic_up.png b/car-support-lib/res/drawable-hdpi/ic_up.png
deleted file mode 100644
index b2b9929..0000000
--- a/car-support-lib/res/drawable-hdpi/ic_up.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-hdpi/ic_up_dark.png b/car-support-lib/res/drawable-hdpi/ic_up_dark.png
deleted file mode 100644
index 3586ac2..0000000
--- a/car-support-lib/res/drawable-hdpi/ic_up_dark.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-mdpi/drawer_shadow.9.png b/car-support-lib/res/drawable-mdpi/drawer_shadow.9.png
deleted file mode 100644
index 1417d1f..0000000
--- a/car-support-lib/res/drawable-mdpi/drawer_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-mdpi/error_illustration.png b/car-support-lib/res/drawable-mdpi/error_illustration.png
deleted file mode 100644
index 1c6ab5c..0000000
--- a/car-support-lib/res/drawable-mdpi/error_illustration.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-mdpi/ic_down.png b/car-support-lib/res/drawable-mdpi/ic_down.png
deleted file mode 100644
index 0f691e2..0000000
--- a/car-support-lib/res/drawable-mdpi/ic_down.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-mdpi/ic_remove_circle.png b/car-support-lib/res/drawable-mdpi/ic_remove_circle.png
deleted file mode 100644
index 795c1ce..0000000
--- a/car-support-lib/res/drawable-mdpi/ic_remove_circle.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-mdpi/ic_up.png b/car-support-lib/res/drawable-mdpi/ic_up.png
deleted file mode 100644
index a3fc019..0000000
--- a/car-support-lib/res/drawable-mdpi/ic_up.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-mdpi/ic_up_dark.png b/car-support-lib/res/drawable-mdpi/ic_up_dark.png
deleted file mode 100644
index 13afaa1..0000000
--- a/car-support-lib/res/drawable-mdpi/ic_up_dark.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-v21/car_pagination_background.xml b/car-support-lib/res/drawable-v21/car_pagination_background.xml
deleted file mode 100644
index 7f72c8b..0000000
--- a/car-support-lib/res/drawable-v21/car_pagination_background.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<ripple
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/car_card_ripple_background" />
\ No newline at end of file
diff --git a/car-support-lib/res/drawable-v21/car_pagination_background_dark.xml b/car-support-lib/res/drawable-v21/car_pagination_background_dark.xml
deleted file mode 100644
index 8ed8c1a..0000000
--- a/car-support-lib/res/drawable-v21/car_pagination_background_dark.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<ripple
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/car_card_ripple_background_dark" />
\ No newline at end of file
diff --git a/car-support-lib/res/drawable-v21/car_pagination_background_light.xml b/car-support-lib/res/drawable-v21/car_pagination_background_light.xml
deleted file mode 100644
index c109739..0000000
--- a/car-support-lib/res/drawable-v21/car_pagination_background_light.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<ripple
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/car_card_ripple_background_light" />
-
diff --git a/car-support-lib/res/drawable-xhdpi/drawer_shadow.9.png b/car-support-lib/res/drawable-xhdpi/drawer_shadow.9.png
deleted file mode 100644
index 46cc273..0000000
--- a/car-support-lib/res/drawable-xhdpi/drawer_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xhdpi/error_illustration.png b/car-support-lib/res/drawable-xhdpi/error_illustration.png
deleted file mode 100644
index 2533a43..0000000
--- a/car-support-lib/res/drawable-xhdpi/error_illustration.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xhdpi/ic_down.png b/car-support-lib/res/drawable-xhdpi/ic_down.png
deleted file mode 100644
index a221d0b..0000000
--- a/car-support-lib/res/drawable-xhdpi/ic_down.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xhdpi/ic_remove_circle.png b/car-support-lib/res/drawable-xhdpi/ic_remove_circle.png
deleted file mode 100644
index 6eda519..0000000
--- a/car-support-lib/res/drawable-xhdpi/ic_remove_circle.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xhdpi/ic_up.png b/car-support-lib/res/drawable-xhdpi/ic_up.png
deleted file mode 100644
index bbbe1fa..0000000
--- a/car-support-lib/res/drawable-xhdpi/ic_up.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xhdpi/ic_up_dark.png b/car-support-lib/res/drawable-xhdpi/ic_up_dark.png
deleted file mode 100644
index ed73bd4..0000000
--- a/car-support-lib/res/drawable-xhdpi/ic_up_dark.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xxhdpi/drawer_shadow.9.png b/car-support-lib/res/drawable-xxhdpi/drawer_shadow.9.png
deleted file mode 100644
index 7b8a58b..0000000
--- a/car-support-lib/res/drawable-xxhdpi/drawer_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xxhdpi/error_illustration.png b/car-support-lib/res/drawable-xxhdpi/error_illustration.png
deleted file mode 100644
index dd4cc28..0000000
--- a/car-support-lib/res/drawable-xxhdpi/error_illustration.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xxhdpi/ic_down.png b/car-support-lib/res/drawable-xxhdpi/ic_down.png
deleted file mode 100644
index 853ce45..0000000
--- a/car-support-lib/res/drawable-xxhdpi/ic_down.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xxhdpi/ic_remove_circle.png b/car-support-lib/res/drawable-xxhdpi/ic_remove_circle.png
deleted file mode 100644
index c4fc1dd..0000000
--- a/car-support-lib/res/drawable-xxhdpi/ic_remove_circle.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xxhdpi/ic_up.png b/car-support-lib/res/drawable-xxhdpi/ic_up.png
deleted file mode 100644
index fb91db9..0000000
--- a/car-support-lib/res/drawable-xxhdpi/ic_up.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable-xxhdpi/ic_up_dark.png b/car-support-lib/res/drawable-xxhdpi/ic_up_dark.png
deleted file mode 100644
index 6c44c5d..0000000
--- a/car-support-lib/res/drawable-xxhdpi/ic_up_dark.png
+++ /dev/null
Binary files differ
diff --git a/car-support-lib/res/drawable/car_empty.xml b/car-support-lib/res/drawable/car_empty.xml
deleted file mode 100644
index b3a4b24..0000000
--- a/car-support-lib/res/drawable/car_empty.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<!-- This is a blank shape, 0x0 in size, that works around the fact that the
- android:textSelectHandle xml property requires a drawable with a defined size. -->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <size
- android:width="0dp"
- android:height="0dp" />
-</shape>
\ No newline at end of file
diff --git a/car-support-lib/res/drawable/car_header_button_background.xml b/car-support-lib/res/drawable/car_header_button_background.xml
deleted file mode 100644
index 120738c..0000000
--- a/car-support-lib/res/drawable/car_header_button_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<inset
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:inset="24dp">
- <ripple android:color="@color/car_card_ripple_background" />
-</inset>
diff --git a/car-support-lib/res/drawable/car_list_item_background.xml b/car-support-lib/res/drawable/car_list_item_background.xml
deleted file mode 100644
index 9f6863f..0000000
--- a/car-support-lib/res/drawable/car_list_item_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!-- 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.
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/car_card_ripple_background">
- <item android:id="@android:id/mask">
- <color android:color="#ffffffff" />
- </item>
-</ripple>
\ No newline at end of file
diff --git a/car-support-lib/res/drawable/car_pagination_background.xml b/car-support-lib/res/drawable/car_pagination_background.xml
deleted file mode 100644
index 7aa4df4..0000000
--- a/car-support-lib/res/drawable/car_pagination_background.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval"
- android:color="@color/car_card_ripple_background" >
- <size
- android:height="0dp"
- android:width="0dp" />
-</shape>
\ No newline at end of file
diff --git a/car-support-lib/res/drawable/car_pagination_background_dark.xml b/car-support-lib/res/drawable/car_pagination_background_dark.xml
deleted file mode 100644
index 7aa4df4..0000000
--- a/car-support-lib/res/drawable/car_pagination_background_dark.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval"
- android:color="@color/car_card_ripple_background" >
- <size
- android:height="0dp"
- android:width="0dp" />
-</shape>
\ No newline at end of file
diff --git a/car-support-lib/res/drawable/car_pagination_background_light.xml b/car-support-lib/res/drawable/car_pagination_background_light.xml
deleted file mode 100644
index 7aa4df4..0000000
--- a/car-support-lib/res/drawable/car_pagination_background_light.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval"
- android:color="@color/car_card_ripple_background" >
- <size
- android:height="0dp"
- android:width="0dp" />
-</shape>
\ No newline at end of file
diff --git a/car-support-lib/res/drawable/ic_chevron_right.xml b/car-support-lib/res/drawable/ic_chevron_right.xml
deleted file mode 100644
index 165fcee..0000000
--- a/car-support-lib/res/drawable/ic_chevron_right.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
-
- <path
- android:fillColor="#212121"
- android:strokeWidth="1"
- android:pathData="M 10 6 L 8.59 7.41 L 13.17 12 L 8.59 16.59 L 10 18 L 16 12 Z" />
- <path
- android:strokeWidth="1"
- android:pathData="M 0 0 L 24 0 L 24 24 L 0 24 Z" />
-</vector>
\ No newline at end of file
diff --git a/car-support-lib/res/drawable/ic_down_button.xml b/car-support-lib/res/drawable/ic_down_button.xml
deleted file mode 100644
index 0565a63..0000000
--- a/car-support-lib/res/drawable/ic_down_button.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<vector android:height="24dp" android:viewportHeight="48.0"
- android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M24,0c-13.3,0 -24,10.7 -24,24s10.7,24 24,24s24,-10.7 24,-24S37.3,0 24,0zM24,46c-12.1,0 -22,-9.9 -22,-22s9.9,-22 22,-22s22,9.9 22,22S36.1,46 24,46z"/>
- <path android:fillColor="#FF000000" android:pathData="M17.1,19.2l6.9,6.9l6.9,-6.9l2.1,2.1l-9,9.1l-9,-9.1z"/>
-</vector>
diff --git a/car-support-lib/res/drawable/ic_up_button.xml b/car-support-lib/res/drawable/ic_up_button.xml
deleted file mode 100644
index 8ac579a..0000000
--- a/car-support-lib/res/drawable/ic_up_button.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<vector android:height="24dp" android:viewportHeight="48.0"
- android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M24,48c13.3,0 24,-10.7 24,-24s-10.7,-24 -24,-24s-24,10.7 -24,24S10.7,48 24,48zM24,2c12.1,0 22,9.9 22,22s-9.9,22 -22,22s-22,-9.9 -22,-22S11.9,2 24,2z"/>
- <path android:fillColor="#FF000000" android:pathData="M30.9,28.8l-6.9,-6.9l-6.9,6.9l-2.1,-2.1l9,-9.1l9,9.1z"/>
-</vector>
diff --git a/car-support-lib/res/drawable/rail_fab.xml b/car-support-lib/res/drawable/rail_fab.xml
deleted file mode 100644
index 8541cec..0000000
--- a/car-support-lib/res/drawable/rail_fab.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
- <size
- android:width="@dimen/rail_height"
- android:height="@dimen/rail_height" />
- <solid android:color="#191f27" />
-</shape>
diff --git a/car-support-lib/res/layout/car_imageview.xml b/car-support-lib/res/layout/car_imageview.xml
deleted file mode 100644
index ac51118..0000000
--- a/car-support-lib/res/layout/car_imageview.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/car_list_item_right_icon_size"
- android:layout_height="@dimen/car_list_item_right_icon_size"
- android:scaleType="fitCenter" />
\ No newline at end of file
diff --git a/car-support-lib/res/layout/car_list_item_1.xml b/car-support-lib/res/layout/car_list_item_1.xml
deleted file mode 100644
index 0bd9ee9..0000000
--- a/car-support-lib/res/layout/car_list_item_1.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_item_height"
- android:focusable="true"
- android:orientation="horizontal"
- android:background="@drawable/car_list_item_background" >
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/car_list_item_icon_size"
- android:layout_height="@dimen/car_list_item_icon_size"
- android:layout_marginRight="@dimen/car_list_item_icon_right_margin"
- android:scaleType="centerCrop"
- android:layout_gravity="center_vertical" />
- <TextView
- android:id="@+id/text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
- style="@style/CarBody1"
- android:singleLine="true" />
- <ImageView
- android:id="@+id/right_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="@dimen/car_list_item_right_icon_margin"
- android:scaleType="center" />
-</LinearLayout>
diff --git a/car-support-lib/res/layout/car_list_item_1_card.xml b/car-support-lib/res/layout/car_list_item_1_card.xml
deleted file mode 100644
index 63ea028..0000000
--- a/car-support-lib/res/layout/car_list_item_1_card.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<android.support.v7.widget.CardView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_item_height"
- android:layout_marginBottom="@dimen/car_card_bottom_margin"
- android:focusable="true"
- android:orientation="horizontal" >
- <!--app:cardBackgroundColor="@color/car_card" -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/car_list_item_background"
- android:duplicateParentState="true"
- android:orientation="horizontal" >
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/car_list_item_icon_size"
- android:layout_height="@dimen/car_list_item_icon_size"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:scaleType="centerCrop"
- android:layout_gravity="center_vertical" />
- <TextView
- android:id="@+id/text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
- style="@style/CarBody1"
- android:singleLine="true" />
- <ImageView
- android:id="@+id/right_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="@dimen/car_list_item_right_icon_margin"
- android:scaleType="center" />
- </LinearLayout>
-</android.support.v7.widget.CardView>
diff --git a/car-support-lib/res/layout/car_list_item_1_small.xml b/car-support-lib/res/layout/car_list_item_1_small.xml
deleted file mode 100644
index 526070e..0000000
--- a/car-support-lib/res/layout/car_list_item_1_small.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_item_height_small"
- android:focusable="true"
- android:orientation="horizontal"
- android:background="@drawable/car_list_item_background" >
- <FrameLayout
- android:id="@+id/icon_container"
- android:layout_width="@dimen/car_list_item_small_icon_size"
- android:layout_height="@dimen/car_list_item_small_icon_size"
- android:layout_marginRight="40dp"
- android:visibility="visible"
- android:layout_gravity="center_vertical">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:visibility="gone"/>
- </FrameLayout>
- <TextView
- android:id="@+id/title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginRight="8dp"
- android:layout_gravity="center_vertical"
- android:ellipsize="end"
- android:singleLine="true"
- style="@style/CarBody1" />
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal" >
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxWidth="275dp"
- android:layout_gravity="center_vertical"
- android:gravity="right"
- style="@style/CarCaption"
- android:ellipsize="end"
- android:singleLine="true" />
- <ImageView
- android:id="@+id/right_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:visibility="gone"
- android:scaleType="center" />
- </LinearLayout>
-
-</LinearLayout>
diff --git a/car-support-lib/res/layout/car_list_item_1_small_card.xml b/car-support-lib/res/layout/car_list_item_1_small_card.xml
deleted file mode 100644
index 2d90f3c..0000000
--- a/car-support-lib/res/layout/car_list_item_1_small_card.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<android.support.v7.widget.CardView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_item_height_small"
- android:focusable="true"
- android:orientation="horizontal"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/car_list_item_background"
- android:duplicateParentState="true"
- android:orientation="horizontal" >
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/car_list_item_small_icon_size"
- android:layout_height="@dimen/car_list_item_small_icon_size"
- android:layout_marginRight="40dp"
- android:layout_gravity="center_vertical"
- android:visibility="gone"
- android:scaleType="centerCrop" />
- <TextView
- android:id="@+id/title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginRight="8dp"
- android:layout_gravity="center_vertical"
- android:ellipsize="end"
- android:singleLine="true"
- style="@style/CarBody1" />
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal" >
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxWidth="275dp"
- android:layout_gravity="center_vertical"
- android:gravity="right"
- style="@style/CarCaption"
- android:ellipsize="end"
- android:singleLine="true" />
- <ImageView
- android:id="@+id/right_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="@dimen/car_list_item_right_icon_margin"
- android:visibility="gone"
- android:scaleType="center" />
- </LinearLayout>
- </LinearLayout>
-</android.support.v7.widget.CardView>
diff --git a/car-support-lib/res/layout/car_list_item_2.xml b/car-support-lib/res/layout/car_list_item_2.xml
deleted file mode 100644
index 9cf7179..0000000
--- a/car-support-lib/res/layout/car_list_item_2.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_item_height"
- android:focusable="true"
- android:orientation="horizontal"
- android:background="@drawable/car_list_item_background" >
- <FrameLayout
- android:id="@+id/icon_container"
- android:layout_width="@dimen/car_list_item_icon_size"
- android:layout_height="@dimen/car_list_item_icon_size"
- android:layout_marginRight="@dimen/car_list_item_icon_right_margin"
- android:layout_gravity="center_vertical">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop" />
- </FrameLayout>
- <LinearLayout
- android:id="@+id/text_container"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/car_text_vertical_margin"
- style="@style/CarBody1"
- android:singleLine="true" />
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/CarBody2"
- android:ellipsize="end"
- android:gravity="end"
- android:singleLine="true" />
- </LinearLayout>
- <ImageView
- android:id="@+id/right_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="@dimen/car_list_item_right_icon_margin"
- android:scaleType="center" />
-</LinearLayout>
diff --git a/car-support-lib/res/layout/car_list_item_2_card.xml b/car-support-lib/res/layout/car_list_item_2_card.xml
deleted file mode 100644
index 88a2c5f..0000000
--- a/car-support-lib/res/layout/car_list_item_2_card.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<android.support.v7.widget.CardView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_item_height"
- android:layout_marginBottom="@dimen/car_card_bottom_margin"
- android:focusable="true"
- android:orientation="horizontal"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/car_list_item_background"
- android:duplicateParentState="true"
- android:orientation="horizontal" >
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/car_list_item_icon_size"
- android:layout_height="@dimen/car_list_item_icon_size"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:scaleType="centerCrop"
- android:layout_gravity="center_vertical" />
- <LinearLayout
- android:id="@+id/text_container"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:layout_gravity="center_vertical" >
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/car_text_vertical_margin"
- style="@style/CarBody1"
- android:singleLine="true" />
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/CarBody2"
- android:singleLine="true" />
- </LinearLayout>
- <ImageView
- android:id="@+id/right_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="@dimen/car_list_item_right_icon_margin"
- android:scaleType="center" />
- </LinearLayout>
-</android.support.v7.widget.CardView>
diff --git a/car-support-lib/res/layout/car_list_item_empty.xml b/car-support-lib/res/layout/car_list_item_empty.xml
deleted file mode 100644
index de0e727..0000000
--- a/car-support-lib/res/layout/car_list_item_empty.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginLeft="16dp"
- android:focusable="false"
- android:orientation="vertical"
- android:background="@drawable/car_list_item_background" >
- <FrameLayout
- android:id="@+id/icon_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="visible">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="48dp"
- android:layout_marginBottom="22dp" />
- </FrameLayout>
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="16dp"
- android:gravity="center"
- style="@style/CarBody1" />
-</LinearLayout>
diff --git a/car-support-lib/res/layout/car_menu_checkbox.xml b/car-support-lib/res/layout/car_menu_checkbox.xml
deleted file mode 100644
index fb9fa07..0000000
--- a/car-support-lib/res/layout/car_menu_checkbox.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<android.support.car.ui.CheckboxWrapperView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:focusable="false"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:buttonTint="@color/car_tint"
- android:buttonTintMode="src_atop" />
\ No newline at end of file
diff --git a/car-support-lib/res/layout/car_menu_list_item.xml b/car-support-lib/res/layout/car_menu_list_item.xml
deleted file mode 100644
index 9cc3ebf..0000000
--- a/car-support-lib/res/layout/car_menu_list_item.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_item_height"
- android:focusable="true"
- android:orientation="horizontal"
- android:background="@drawable/car_list_item_background" >
- <FrameLayout
- android:id="@+id/icon_container"
- android:layout_width="@dimen/car_list_item_icon_size"
- android:layout_height="@dimen/car_list_item_icon_size"
- android:layout_marginRight="@dimen/car_list_item_icon_right_margin"
- android:layout_gravity="center_vertical">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop" />
- </FrameLayout>
- <LinearLayout
- android:id="@+id/text_container"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/car_text_vertical_margin"
- style="@style/CarBody1"
- android:singleLine="true" />
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <FrameLayout
- android:id="@+id/remoteviews"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:visibility="gone" />
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/CarBody2"
- android:ellipsize="end"
- android:gravity="end"
- android:singleLine="true" />
- </LinearLayout>
- </LinearLayout>
- <ViewStub
- android:id="@+id/right_item"
- android:layout_width="@dimen/car_list_item_right_icon_size"
- android:layout_height="@dimen/car_list_item_right_icon_size"
- android:layout_marginEnd="32dp"
- android:layout_gravity="center_vertical" />
-</LinearLayout>
diff --git a/car-support-lib/res/layout/car_paged_recycler_view.xml b/car-support-lib/res/layout/car_paged_recycler_view.xml
deleted file mode 100644
index d53bb46..0000000
--- a/car-support-lib/res/layout/car_paged_recycler_view.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <android.support.car.ui.MaxWidthLayout
- android:id="@+id/max_width_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/car_drawer_button_container_width">
- <android.support.car.ui.CarRecyclerView
- android:id="@+id/recycler_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal"
- android:clipChildren="false"/>
- </android.support.car.ui.MaxWidthLayout>
- <!-- The scroll bar should be drawn ontop of the centered recycler view-->
- <FrameLayout
- android:layout_width="@dimen/car_drawer_button_container_width"
- android:layout_height="match_parent">
- <android.support.car.ui.PagedScrollBarView
- android:id="@+id/paged_scroll_view"
- android:layout_width="@dimen/car_paged_list_view_pagination_width"
- android:layout_height="match_parent"
- android:paddingBottom="16dp"
- android:paddingTop="16dp"
- android:layout_gravity="center_horizontal"
- android:visibility="invisible"/>
- </FrameLayout>
-</merge>
diff --git a/car-support-lib/res/layout/car_paged_scrollbar_buttons.xml b/car-support-lib/res/layout/car_paged_scrollbar_buttons.xml
deleted file mode 100644
index e976f1c..0000000
--- a/car-support-lib/res/layout/car_paged_scrollbar_buttons.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="left"
- android:gravity="center"
- android:orientation="vertical" >
- <ImageView
- android:id="@+id/page_up"
- android:layout_width="@dimen/scroll_button_size"
- android:layout_height="@dimen/scroll_button_size"
- android:scaleType="fitCenter"
- android:background="@drawable/car_pagination_background"
- android:focusable="false"
- android:hapticFeedbackEnabled="false" />
- <FrameLayout
- android:id="@+id/filler"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_marginTop="@dimen/car_paged_list_view_scrollbar_thumb_margin"
- android:layout_marginBottom="@dimen/car_paged_list_view_scrollbar_thumb_margin" >
- <ImageView
- android:id="@+id/scrollbar_thumb"
- android:layout_width="@dimen/scroll_bar_thumb_width"
- android:layout_height="0dp"
- android:layout_gravity="center_horizontal"
- android:src="@color/car_scrollbar_thumb" />
- </FrameLayout>
- <ImageView
- android:id="@+id/page_down"
- android:layout_width="@dimen/scroll_button_size"
- android:layout_height="@dimen/scroll_button_size"
- android:scaleType="fitCenter"
- android:background="@drawable/car_pagination_background"
- android:focusable="false"
- android:hapticFeedbackEnabled="false" />
-</LinearLayout>
diff --git a/car-support-lib/res/layout/car_textview.xml b/car-support-lib/res/layout/car_textview.xml
deleted file mode 100644
index 5810af7..0000000
--- a/car-support-lib/res/layout/car_textview.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/text"
- style="@style/CarCaption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:maxWidth="275dp"
- android:gravity="right"
- android:ellipsize="end"
- android:singleLine="true" />
\ No newline at end of file
diff --git a/car-support-lib/res/layout/car_unavailable_category.xml b/car-support-lib/res/layout/car_unavailable_category.xml
deleted file mode 100644
index d9da5a1..0000000
--- a/car-support-lib/res/layout/car_unavailable_category.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_unavailable_category_item_height"
- android:focusable="true"
- android:orientation="horizontal"
- android:background="@drawable/car_list_item_background"
- android:baselineAligned="false" >
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/car_list_item_icon_size"
- android:layout_height="@dimen/car_list_item_icon_size"
- android:layout_marginRight="@dimen/car_list_item_icon_right_margin"
- android:layout_gravity="center_vertical"
- android:src="@drawable/ic_remove_circle"
- android:scaleType="centerCrop" />
- <RelativeLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical" >
- <TextView
- android:id="@+id/title"
- style="@style/CarUnavailableCategory"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/unavailable_category_first_part" />
- <TextView
- android:id="@+id/text"
- style="@style/CarUnavailableCategory"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/title"
- android:text="@string/unavailable_category_second_part" />
- </RelativeLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/car-support-lib/res/values-h600dp/dimens.xml b/car-support-lib/res/values-h600dp/dimens.xml
deleted file mode 100644
index 5887ab1..0000000
--- a/car-support-lib/res/values-h600dp/dimens.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <dimen name="car_drawer_margin_right">320dp</dimen>
- <dimen name="car_list_item_right_icon_margin">112dp</dimen>
-
- <dimen name="car_list_item_icon_right_margin">60dp</dimen>
-
- <dimen name="car_headline0_size">72sp</dimen>
- <dimen name="car_headline1_size">56sp</dimen>
- <dimen name="car_headline2_size">50sp</dimen>
- <dimen name="car_title_size">32sp</dimen>
- <dimen name="car_body1_size">40sp</dimen>
- <dimen name="car_body2_size">32sp</dimen>
- <dimen name="car_key1_size">50sp</dimen>
- <dimen name="car_key2_size">22sp</dimen>
- <dimen name="car_caption_size">22sp</dimen>
-
- <dimen name="car_card_margin">228dp</dimen>
-
- <dimen name="car_list_item_icon_size">108dp</dimen>
- <dimen name="car_list_item_small_icon_size">56dp</dimen>
- <dimen name="car_list_item_right_icon_size">56dp</dimen>
-
- <dimen name="car_list_item_height">128dp</dimen>
- <dimen name="car_list_item_height_small">128dp</dimen>
-</resources>
diff --git a/car-support-lib/res/values-h600dp/strings.xml b/car-support-lib/res/values-h600dp/strings.xml
deleted file mode 100644
index 1527404..0000000
--- a/car-support-lib/res/values-h600dp/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <string name="car_font" translatable="false">sans-serif</string>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values-h600dp/styles.xml b/car-support-lib/res/values-h600dp/styles.xml
deleted file mode 100644
index 831d3ca..0000000
--- a/car-support-lib/res/values-h600dp/styles.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <style name="CarDrawerArrowDrawable" >
- <item name="carArrowColor">@android:color/white</item>
- <item name="carArrowSpinBars">true</item>
- <item name="carArrowThickness">4dp</item>
- <item name="carArrowDrawableSize">@dimen/car_drawer_header_menu_button_size</item>
- <item name="carArrowTopBottomBarSize">26dp</item>
- <item name="carArrowBarSize">42dp</item>
- <item name="carArrowMiddleBarSize">35dp</item>
- <item name="carArrowGapBetweenBars">7dp</item>
- </style>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values-night-wheel/colors.xml b/car-support-lib/res/values-night-wheel/colors.xml
deleted file mode 100644
index e851246..0000000
--- a/car-support-lib/res/values-night-wheel/colors.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <color name="car_card_ripple_background">@color/car_controller_ripple_light</color>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values-night/colors.xml b/car-support-lib/res/values-night/colors.xml
deleted file mode 100644
index 0ecdb84..0000000
--- a/car-support-lib/res/values-night/colors.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <!-- Aliases for material colors that may be used programmatically -->
- <color name="car_headline0">@color/car_headline0_light</color>
- <color name="car_headline1">@color/car_headline1_light</color>
- <color name="car_headline2">@color/car_headline2_light</color>
- <color name="car_title">@color/car_title_light</color>
- <color name="car_body1">@color/car_body1_light</color>
- <color name="car_body2">@color/car_body2_light</color>
- <color name="car_key1">@color/car_key1_light</color>
- <color name="car_key2">@color/car_key2_light</color>
- <color name="car_caption">@color/car_caption_light</color>
- <color name="car_micro">@color/car_micro_light</color>
- <color name="car_card">@color/car_card_dark</color>
- <color name="car_card_ripple_background">@color/car_card_ripple_background_light</color>
- <color name="car_card_ripple_light_color_background">@color/car_card_ripple_light_color_background_light</color>
- <color name="car_overscroll_glow">@color/car_overscroll_glow_dark</color>
- <color name="car_tint">@color/car_tint_light</color>
- <color name="car_list_divider">@color/car_white_1000</color>
- <color name="car_unavailable_category">@color/car_grey_50</color>
-</resources>
diff --git a/car-support-lib/res/values-notouch/bools.xml b/car-support-lib/res/values-notouch/bools.xml
deleted file mode 100644
index 26f9c76..0000000
--- a/car-support-lib/res/values-notouch/bools.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <bool name="car_true_for_touch">false</bool>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values-w1024dp/dimens.xml b/car-support-lib/res/values-w1024dp/dimens.xml
deleted file mode 100644
index f329135..0000000
--- a/car-support-lib/res/values-w1024dp/dimens.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <dimen name="stream_margin_size">112dp</dimen>
- <dimen name="stream_content_keyline_1">112dp</dimen>
- <dimen name="stream_content_keyline_2">228dp</dimen>
-</resources>
diff --git a/car-support-lib/res/values-w600dp/integers.xml b/car-support-lib/res/values-w600dp/integers.xml
deleted file mode 100644
index e32083d..0000000
--- a/car-support-lib/res/values-w600dp/integers.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <integer name="stream_num_of_columns">8</integer>
- <integer name="stream_card_default_column_span">6</integer>
-</resources>
diff --git a/car-support-lib/res/values-w720dp/dimens.xml b/car-support-lib/res/values-w720dp/dimens.xml
deleted file mode 100644
index d72006e..0000000
--- a/car-support-lib/res/values-w720dp/dimens.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <dimen name="car_paged_list_view_pagination_width">76dp</dimen>
- <dimen name="scroll_button_size">76dp</dimen>
- <dimen name="scroll_bar_thumb_width">16dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values-w840dp/dimens.xml b/car-support-lib/res/values-w840dp/dimens.xml
deleted file mode 100644
index e391d80..0000000
--- a/car-support-lib/res/values-w840dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <dimen name="car_drawer_button_container_width">@dimen/stream_content_keyline_1</dimen>
- <dimen name="stream_margin_size">40dp</dimen>
- <dimen name="stream_gutter_size">24dp</dimen>
- <dimen name="stream_content_keyline_1">40dp</dimen>
- <dimen name="stream_content_keyline_2">144dp</dimen>
- <dimen name="stream_card_keyline_1">32dp</dimen>
- <dimen name="stream_card_keyline_2">140dp</dimen>
-</resources>
diff --git a/car-support-lib/res/values-wheel/bools.xml b/car-support-lib/res/values-wheel/bools.xml
deleted file mode 100644
index 19fe2c4..0000000
--- a/car-support-lib/res/values-wheel/bools.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <bool name="has_wheel">true</bool>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values-wheel/colors.xml b/car-support-lib/res/values-wheel/colors.xml
deleted file mode 100644
index f29b070..0000000
--- a/car-support-lib/res/values-wheel/colors.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <color name="car_card_ripple_background">@color/car_controller_ripple_dark</color>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values/attrs.xml b/car-support-lib/res/values/attrs.xml
deleted file mode 100644
index c887fa0..0000000
--- a/car-support-lib/res/values/attrs.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <declare-styleable name="PagedListView">
- <!-- Fade duration in ms -->
- <attr name="fadeLastItem" format="boolean" />
- <!-- Set to true/false to offset rows as they slide off screen. Defaults to true -->
- <attr name="offsetRows" format="boolean" />
- <attr name="glowColor" format="color" />
- <!-- Whether loading list view in drawer or not -->
- <attr name="rightGutterEnabled" format="boolean" />
- </declare-styleable>
-
- <declare-styleable name="DrawerArrowDrawable">
- <!-- The drawing color for the bars -->
- <attr name="carArrowColor" format="color"/>
- <!-- Whether bars should rotate or not during transition -->
- <attr name="carArrowSpinBars" format="boolean"/>
- <!-- The total size of the drawable -->
- <attr name="carArrowDrawableSize" format="dimension"/>
- <!-- The max gap between the bars when they are parallel to each other -->
- <attr name="carArrowGapBetweenBars" format="dimension"/>
- <!-- The size of the top and bottom bars when they merge to the middle bar to form an arrow -->
- <attr name="carArrowTopBottomBarSize" format="dimension"/>
- <!-- The size of the middle bar when top and bottom bars merge into middle bar to form an arrow -->
- <attr name="carArrowMiddleBarSize" format="dimension"/>
- <!-- The size of the bars when they are parallel to each other -->
- <attr name="carArrowBarSize" format="dimension"/>
- <!-- The thickness (stroke size) for the bar paint -->
- <attr name="carArrowThickness" format="dimension"/>
- </declare-styleable>
-
- <attr name="carDrawerArrowStyle" format="reference" />
-
- <declare-styleable name="MaxWidthLayout">
- <attr name="carMaxWidth" format="dimension" />
- </declare-styleable>
-</resources>
diff --git a/car-support-lib/res/values/bools.xml b/car-support-lib/res/values/bools.xml
deleted file mode 100644
index dd685b3..0000000
--- a/car-support-lib/res/values/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <bool name="car_true_for_touch">true</bool>
- <bool name="has_wheel">false</bool>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values/colors.xml b/car-support-lib/res/values/colors.xml
deleted file mode 100644
index ccf22aa..0000000
--- a/car-support-lib/res/values/colors.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <!-- Copy of material colors. -->
- <!-- These colors are from http://www.google.com/design/spec/style/color.html#color-ui-color-palette -->
- <color name="car_grey_50">#fffafafa</color>
- <color name="car_grey_100">#fff5f5f5</color>
- <color name="car_grey_200">#ffeeeeee</color>
- <color name="car_grey_300">#ffe0e0e0</color>
- <color name="car_grey_400">#ffbdbdbd</color>
- <color name="car_grey_500">#ff9e9e9e</color>
- <color name="car_grey_600">#ff757575</color>
- <color name="car_grey_650">#ff6B6B6B</color>
- <color name="car_grey_700">#ff616161</color>
- <color name="car_grey_800">#ff424242</color>
- <color name="car_grey_900">#ff212121</color>
- <color name="car_grey_1000">#cc000000</color>
- <color name="car_white_1000">#1effffff</color>
- <color name="car_blue_grey_800">#ff37474F</color>
- <color name="car_blue_grey_900">#ff263238</color>
- <color name="car_dark_blue_grey_600">#ff1d272d</color>
- <color name="car_dark_blue_grey_700">#ff172026</color>
- <color name="car_dark_blue_grey_800">#ff11181d</color>
- <color name="car_dark_blue_grey_900">#ff0c1013</color>
- <color name="car_dark_blue_grey_1000">#ff090c0f</color>
- <color name="car_light_blue_300">#ff4fc3f7</color>
- <color name="car_light_blue_500">#ff03A9F4</color>
- <color name="car_light_blue_600">#ff039be5</color>
- <color name="car_light_blue_700">#ff0288d1</color>
- <color name="car_light_blue_800">#ff0277bd</color>
- <color name="car_light_blue_900">#ff01579b</color>
- <color name="car_blue_300">#ff91a7ff</color>
- <color name="car_blue_500">#ff5677fc</color>
- <color name="car_green_500">#ff0f9d58</color>
- <color name="car_green_700">#ff0b8043</color>
- <color name="car_yellow_500">#fff4b400</color>
- <color name="car_yellow_800">#ffee8100</color>
- <color name="car_red_400">#ffe06055</color>
- <color name="car_red_500">#ffdb4437</color>
- <color name="car_red_500a">#ffd50000</color>
- <color name="car_red_700">#ffc53929</color>
- <color name="car_teal_200">#ff80cbc4</color>
- <color name="car_teal_700">#ff00796b</color>
- <color name="car_indigo_800">#ff283593</color>
- <color name="car_700">#ff172026</color>
- <color name="car_900">#ff0c1013</color>
- <color name="car_fab_view_color">#ff009688</color>
- <color name="car_fab_view_clicked_color">#0026a69a</color>
- <color name="car_keygroup_circle">#ff104343</color>
- <!-- Aliases for material colors that may be used programmatically -->
- <color name="car_headline0_light">@color/car_grey_100</color>
- <color name="car_headline0_dark">@color/car_grey_800</color>
- <color name="car_headline0">@color/car_headline0_dark</color>
-
- <color name="car_headline1_light">@color/car_grey_100</color>
- <color name="car_headline1_dark">@color/car_grey_800</color>
- <color name="car_headline1">@color/car_headline1_dark</color>
-
- <color name="car_headline2_light">@color/car_grey_100</color>
- <color name="car_headline2_dark">@color/car_grey_900</color>
- <color name="car_headline2">@color/car_headline2_dark</color>
-
- <color name="car_title_light">@color/car_grey_100</color>
- <color name="car_title_dark">@color/car_grey_900</color>
- <color name="car_title">@color/car_title_dark</color>
-
- <color name="car_body1_light">@color/car_grey_100</color>
- <color name="car_body1_dark">@color/car_grey_900</color>
- <color name="car_body1">@color/car_body1_dark</color>
-
- <color name="car_body2_dark">@color/car_grey_650</color>
- <color name="car_body2_light">@color/car_grey_500</color>
- <color name="car_body2">@color/car_body2_dark</color>
-
- <color name="car_caption_light">@color/car_grey_400</color>
- <color name="car_caption_dark">@color/car_grey_700</color>
- <color name="car_caption">@color/car_caption_dark</color>
-
- <color name="car_key1_light">@color/car_grey_100</color>
- <color name="car_key1_dark">@color/car_light_blue_700</color>
- <color name="car_key1">@color/car_key1_dark</color>
-
- <color name="car_key2_light">@color/car_grey_400</color>
- <color name="car_key2_dark">@color/car_grey_650</color>
- <color name="car_key2">@color/car_key2_dark</color>
-
- <color name="car_micro_light">@color/car_grey_100</color>
- <color name="car_micro_dark">@color/car_grey_700</color>
- <color name="car_micro">@color/car_micro_dark</color>
-
- <color name="car_card_light">@color/car_grey_50</color>
- <color name="car_card_dark">@color/car_dark_blue_grey_700</color>
- <color name="car_card">@color/car_card_light</color>
-
- <color name="car_card_ripple_background_dark">#17000000</color>
- <color name="car_card_ripple_background_light">#27ffffff</color>
- <color name="car_card_ripple_background">@color/car_card_ripple_background_dark</color>
-
- <color name="car_card_ripple_light_color_background_dark">#8F000000</color>
- <color name="car_card_ripple_light_color_background_light">#8F000000</color>
- <color name="car_card_ripple_light_color_background">@color/car_card_ripple_light_color_background_dark</color>
-
- <color name="car_controller_ripple_dark">#b27da9c7</color>
- <color name="car_controller_ripple_light">#66ffffff</color>
-
- <color name="car_overscroll_glow_light">@color/car_grey_400</color>
- <color name="car_overscroll_glow_dark">@color/car_grey_900</color>
- <color name="car_overscroll_glow">@color/car_overscroll_glow_light</color>
-
- <color name="car_tint_light">@color/car_grey_50</color>
- <color name="car_tint_dark">@color/car_grey_900</color>
- <color name="car_tint">@color/car_tint_dark</color>
-
- <color name="car_list_divider">#1f000000</color>
- <color name="car_list_divider_light">#1fffffff</color>
- <color name="car_list_divider_dark">#1f000000</color>
-
- <color name="car_ripple">#20444444</color>
-
- <color name="car_scrollbar_thumb">#80bdbdbd</color>
-
- <color name="car_focused_color">@color/car_teal_200</color>
-
- <color name="car_music_accent_color">#ff9700</color>
-
- <color name="car_error_screen">#ff1e272e</color>
-
- <color name="car_unavailable_category">@color/car_blue_grey_800</color>
-</resources>
diff --git a/car-support-lib/res/values/dimens.xml b/car-support-lib/res/values/dimens.xml
deleted file mode 100644
index ae8b4b2..0000000
--- a/car-support-lib/res/values/dimens.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <!-- Permission -->
- <dimen name="missing_permission_icon_size">208dp</dimen>
-
- <dimen name="car_app_layout_icon_width">42dp</dimen>
- <dimen name="car_app_layout_search_box_small_width">320dp</dimen>
- <dimen name="car_app_layout_search_box_small_margin">16dp</dimen>
-
- <dimen name="car_headline0_size">50sp</dimen>
- <dimen name="car_headline1_size">45sp</dimen>
- <dimen name="car_headline2_size">36sp</dimen>
- <dimen name="car_title_size">26sp</dimen>
- <dimen name="car_body1_size">32sp</dimen>
- <dimen name="car_body2_size">26sp</dimen>
- <dimen name="car_caption_size">18sp</dimen>
- <dimen name="car_key1_size">40sp</dimen>
- <dimen name="car_key2_size">18sp</dimen>
- <dimen name="car_micro_size">20sp</dimen>
-
- <dimen name="car_action_bar_icon_padding">10dp</dimen>
- <dimen name="car_action_bar_margin">15dp</dimen>
-
- <dimen name="car_drawer_header_height">96dp</dimen>
- <dimen name="car_drawer_header_menu_button_size">96dp</dimen>
- <dimen name="car_drawer_standard_width">704dp</dimen>
- <dimen name="car_drawer_max_width">884dp</dimen>
- <dimen name="car_drawer_item_max_width">788dp</dimen>
-
- <dimen name="car_list_item_height">88dp</dimen>
- <dimen name="car_list_item_height_small">64dp</dimen>
- <!-- Sample row height used for scroll bar calculations in the off chance that a view hasn't
- been measured. It's highly unlikely that this value will actually be used for more than
- a frame max. The sample row is a 96dp card + 16dp margin on either side. -->
- <dimen name="car_sample_row_height">128dp</dimen>
- <!-- The amount of space the LayoutManager will make sure the last item on the screen is
- peeking before scrolling down -->
- <dimen name="car_last_card_peek_amount">16dp</dimen>
- <dimen name="car_paged_list_view_pagination_width">48dp</dimen>
- <dimen name="car_paged_list_view_button_background_inset">24dp</dimen>
- <dimen name="car_paged_list_view_scrollbar_thumb_margin">8dp</dimen>
-
- <dimen name="scroll_button_size">48dp</dimen>
- <dimen name="scroll_bar_thumb_width">12dp</dimen>
-
- <!-- height of list dividers -->
- <dimen name="car_divider_height">1dp</dimen>
-
- <dimen name="car_list_item_icon_size">64dp</dimen>
- <dimen name="car_list_item_icon_right_margin">32dp</dimen>
- <dimen name="car_list_item_icon_size_small">32dp</dimen>
- <dimen name="car_list_item_small_icon_size">56dp</dimen>
- <dimen name="car_list_item_right_icon_margin">20dp</dimen>
- <dimen name="car_list_item_right_icon_size">56dp</dimen>
- <dimen name="car_list_unavailable_category_item_height">108dp</dimen>
- <dimen name="car_list_truncated_list_card_height">108dp</dimen>
- <dimen name="car_list_truncated_list_card_elevation">8dp</dimen>
- <dimen name="car_list_truncated_list_icon_size">56dp</dimen>
- <dimen name="car_list_truncated_list_icon_padding">22dp</dimen>
- <dimen name="car_list_truncated_list_drawable_padding">48dp</dimen>
- <dimen name="car_list_truncated_list_padding">10dp</dimen>
- <dimen name="car_text_vertical_margin">2dp</dimen>
- <dimen name="car_card_bottom_margin">8dp</dimen>
-
- <dimen name="car_touch_feedback_radius">32dp</dimen>
- <dimen name="car_card_view_corner_radius">2dp</dimen>
- <dimen name="car_card_view_elevation">8dp</dimen>
- <dimen name="car_fab_focused_stroke_width">8dp</dimen>
- <dimen name="car_fab_focused_growth">1.2dp</dimen>
- <!-- The minimum the scrollbar thumb can shrink to -->
- <dimen name="min_thumb_height">48dp</dimen>
- <!-- The maximum the scrollbar thumb can grow to -->
- <dimen name="max_thumb_height">128dp</dimen>
-
- <dimen name="car_standard_width">800dp</dimen>
- <dimen name="car_card_max_width">768dp</dimen>
- <dimen name="car_card_margin">96dp</dimen>
- <dimen name="car_drawer_margin_right">96dp</dimen>
- <dimen name="rail_height">80dp</dimen>
-
- <dimen name="car_drawer_button_container_width">@dimen/stream_content_keyline_2</dimen>
-
- <!-- The following values should match the values provided by car_stream-ui-lib. Eventually,
- when the components in this library are broken out, then this dependency can be added
- and the values removed. -->
-
- <!-- The margin on both sizes of the screen. This margin limits the amount of space that
- content can take up on screen. -->
- <dimen name="stream_margin_size">16dp</dimen>
-
- <!-- The size of the gutters between each column. -->
- <dimen name="stream_gutter_size">16dp</dimen>
-
- <!-- Keylines for content in the stream. -->
- <dimen name="stream_content_keyline_1">24dp</dimen>
- <dimen name="stream_content_keyline_2">96dp</dimen>
-
- <!-- Keylines for content within a card. -->
- <dimen name="stream_card_keyline_1">24dp</dimen>
- <dimen name="stream_card_keyline_2">96dp</dimen>
-</resources>
diff --git a/car-support-lib/res/values/integers.xml b/car-support-lib/res/values/integers.xml
deleted file mode 100644
index ef63383..0000000
--- a/car-support-lib/res/values/integers.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <integer name="car_fab_animation_duration">150</integer>
-</resources>
\ No newline at end of file
diff --git a/car-support-lib/res/values/strings.xml b/car-support-lib/res/values/strings.xml
index 137f68e..ab697e1 100644
--- a/car-support-lib/res/values/strings.xml
+++ b/car-support-lib/res/values/strings.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 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.
@@ -14,18 +14,5 @@
limitations under the License.
-->
<resources>
- <!-- Format string for the clock. TODO: Use the system preference -->
- <string name="car_clock_24_hours_format" translatable="false">kk:mm</string>
- <!-- Format string for the clock. TODO: Use the system preference -->
- <string name="car_world_clock_12_hours_format" translatable="false">h:mm </string>
-
- <!-- Permission -->
- <skip/>
- <string name="app_name">Test App</string>
- <!-- The first part of text shown when a list has content, but, is empty because we've hit the maximum number of touches allowed -->
- <string name="unavailable_category_first_part">Can\'t show items</string>
- <!-- The second part of text shown when a list has content, but, is empty because we've hit the maximum number of touches allowed -->
- <string name="unavailable_category_second_part">for safety reasons</string>
-
- <string name="truncated_list">Can\'t show more items\nfor safety reasons</string>
+ <string name="resource_warning">Please do not check in any ui resources here.</string>
</resources>
diff --git a/car-support-lib/res/values/styles.xml b/car-support-lib/res/values/styles.xml
deleted file mode 100644
index 67370c9..0000000
--- a/car-support-lib/res/values/styles.xml
+++ /dev/null
@@ -1,189 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android" >
- <style name="CarHeadline0" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_headline0_size</item>
- <item name="android:textColor">@color/car_headline0</item>
- </style>
-
- <style name="CarHeadline0.Dark" >
- <item name="android:textColor">@color/car_headline0_dark</item>
- </style>
-
- <style name="CarHeadline0.Light" >
- <item name="android:textColor">@color/car_headline0_light</item>
- </style>
-
- <style name="CarHeadline1" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_headline1_size</item>
- <item name="android:textColor">@color/car_headline1</item>
- </style>
-
- <style name="CarHeadline1.Dark" >
- <item name="android:textColor">@color/car_headline1_dark</item>
- </style>
-
- <style name="CarHeadline1.Light" >
- <item name="android:textColor">@color/car_headline1_light</item>
- </style>
-
- <style name="CarHeadline2" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_headline2_size</item>
- <item name="android:textColor">@color/car_headline1</item>
-
- </style>
-
- <style name="CarHeadline2.Dark" >
- <item name="android:textColor">@color/car_headline1_dark</item>
- </style>
-
- <style name="CarHeadline2.Light" >
- <item name="android:textColor">@color/car_headline1_light</item>
- </style>
-
- <style name="CarTitle" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_title_size</item>
- <item name="android:textColor">@color/car_title</item>
- <item name="android:includeFontPadding">false</item>
- </style>
-
- <style name="CarTitle.Dark" >
- <item name="android:textColor">@color/car_title_dark</item>
- </style>
-
- <style name="CarTitle.Light" >
- <item name="android:textColor">@color/car_title_light</item>
- </style>
-
- <style name="CarBody1" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_body1_size</item>
- <item name="android:textColor">@color/car_body1</item>
- </style>
-
- <style name="CarBody1.Dark" >
- <item name="android:textColor">@color/car_body1_dark</item>
- </style>
-
- <style name="CarBody1.Light" >
- <item name="android:textColor">@color/car_body1_light</item>
- </style>
-
- <style name="CarBody2" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_body2_size</item>
- <item name="android:textColor">@color/car_body2</item>
- <item name="android:includeFontPadding">false</item>
- </style>
-
- <style name="CarBody2.Light" >
- <item name="android:textColor">@color/car_body2_light</item>
- </style>
-
- <style name="CarKey1" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_key1_size</item>
- <item name="android:textColor">@color/car_key1</item>
- </style>
-
- <style name="CarKey1.Dark" >
- <item name="android:textColor">@color/car_key1_dark</item>
- </style>
-
- <style name="CarKey1.Light" >
- <item name="android:textColor">@color/car_key1_light</item>
- </style>
-
- <style name="CarKey2" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_key2_size</item>
- <item name="android:textColor">@color/car_key2</item>
- </style>
-
- <style name="CarKey2.Dark" >
- <item name="android:textColor">@color/car_key2_dark</item>
- </style>
-
- <style name="CarKey2.Light" >
- <item name="android:textColor">@color/car_key2_light</item>
- </style>
-
- <style name="CarCaption" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_caption_size</item>
- <item name="android:textColor">@color/car_caption</item>
- </style>
-
- <style name="CarCaption.Dark" >
- <item name="android:textColor">@color/car_caption_dark</item>
- </style>
-
- <style name="CarCaption.Light" >
- <item name="android:textColor">@color/car_caption_light</item>
- </style>
-
- <style name="CarMicro" >
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_micro_size</item>
- <item name="android:textColor">@color/car_micro</item>
- </style>
-
- <style name="CarMicro.Dark" >
- <item name="android:textColor">@color/car_micro_dark</item>
- </style>
-
- <style name="CarMicro.Light" >
- <item name="android:textColor">@color/car_micro_light</item>
- </style>
-
- <style name="CarDrawerArrowDrawable" >
- <item name="carArrowColor">@android:color/white</item>
- <item name="carArrowSpinBars">true</item>
- <item name="carArrowThickness">2.5dp</item>
- <item name="carArrowDrawableSize">@dimen/car_drawer_header_menu_button_size</item>
- <item name="carArrowTopBottomBarSize">18dp</item>
- <item name="carArrowBarSize">28dp</item>
- <item name="carArrowMiddleBarSize">24dp</item>
- <item name="carArrowGapBetweenBars">6dp</item>
- </style>
-
- <style name="Toast" >
- <item name="android:fontFamily">@string/car_font</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_title_size</item>
- <item name="android:textColor">@color/car_grey_100</item>
- <item name="android:includeFontPadding">false</item>
- </style>
-
- <style name="CarUnavailableCategory" >
- <item name="android:fontFamily">@string/car_font</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_body2_size</item>
- <item name="android:textColor">@color/car_unavailable_category</item>
- </style>
-
- <style name="CarTruncatedList" >
- <item name="android:fontFamily">@string/car_font</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">@dimen/car_body2_size</item>
- <item name="android:textColor">@color/car_grey_50</item>
- <item name="android:drawableTint">@color/car_grey_50</item>
- </style>
-</resources>
diff --git a/car-support-lib/src/android/support/car/Car.java b/car-support-lib/src/android/support/car/Car.java
index 50ddf84..c47b650 100644
--- a/car-support-lib/src/android/support/car/Car.java
+++ b/car-support-lib/src/android/support/car/Car.java
@@ -44,8 +44,9 @@
* {@link CarConnectionCallback} will respond with an {@link CarConnectionCallback#onConnected(Car)}
* or {@link CarConnectionCallback#onDisconnected(Car)} message. Nothing can be done with the
* car until onConnected is called. When the car disconnects then reconnects you may still use
- * the Car object but any manages retried from it should be considered invalid and will need to
- * be retrieved.
+ * the Car object but any manages retrieved from it should be considered invalid and will need to
+ * be retried. Also, you must call {@link #disconnect} before an instance of Car goes out of scope
+ * to avoid leaking resources.
*
* <p/>
* Once connected, {@link #getCarManager(String)} or {@link #getCarManager(Class)} can be used to
@@ -366,9 +367,6 @@
*/
public void disconnect() {
synchronized (this) {
- if (mConnectionState == STATE_DISCONNECTED) {
- return;
- }
tearDownCarManagers();
mConnectionState = STATE_DISCONNECTED;
mCarServiceLoader.disconnect();
diff --git a/car-support-lib/src/android/support/car/app/CarActivity.java b/car-support-lib/src/android/support/car/app/CarActivity.java
deleted file mode 100644
index e4b5567..0000000
--- a/car-support-lib/src/android/support/car/app/CarActivity.java
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * 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 android.support.car.app;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.annotation.LayoutRes;
-import android.support.car.Car;
-import android.support.car.app.menu.CarDrawerActivity;
-import android.support.car.input.CarInputManager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.Window;
-
-/**
- * A car specific activity class. It allows an application to run on both "projected" and
- * "native" platforms. For the phone-only mode we only support media and messaging apps at this
- * time. Please see our guides for writing
- * <a href="https://developer.android.com/training/auto/index.html#media">media</a> and
- * <a href="https://developer.android.com/training/auto/index.html#messaging">messaging</a> apps.
- * <ul>
- * <li>
- * For "native" systems you'll additionally need to implement a
- * {@link CarProxyActivity} and add it to your application's manifest (see
- * {@link CarProxyActivity}) for details).
- * </li>
- * <li>
- * For "projected" systems you'll need to implement a
- * {@link com.google.android.apps.auto.sdk.activity.CarProxyProjectionActivityService}
- * </li>
- * </ul>
- *
- * Applications wishing to write Android Auto applications will need to extend this class or one of
- * it's sub classes. You'll most likely want to use {@link CarFragmentActivity} or
- * {@link CarDrawerActivity} instead or this class as this one does not support fragments.
- * <p/>
- * This class has the look and feel of {@link Activity} however, it does not extend {@link Activity}
- * or {@link Context}. Applications should use {@link #getContext()} to access the {@link Context}.
- *
- * @hide
- */
-public abstract class CarActivity {
- private static final String TAG = "CarActivity";
- public interface RequestPermissionsRequestCodeValidator {
- public void validateRequestPermissionsRequestCode(int requestCode);
- }
- /**
- * Interface to connect {@link CarActivity} to {@link android.app.Activity} or other app model.
- * This interface provides utility for {@link CarActivity} to do things like manipulating view,
- * handling menu, and etc.
- */
- public abstract static class Proxy {
- abstract public void setIntent(Intent i);
- abstract public void setContentView(View view);
- abstract public void setContentView(int layoutResID);
- abstract public Resources getResources();
- abstract public View findViewById(int id);
- abstract public LayoutInflater getLayoutInflater();
- abstract public Intent getIntent();
- abstract public void finish();
- abstract public CarInputManager getCarInputManager();
- abstract public boolean isFinishing();
- abstract public MenuInflater getMenuInflater();
- abstract public void finishAfterTransition();
- abstract public Window getWindow();
- abstract public void setResult(int resultCode);
- abstract public void setResult(int resultCode, Intent data);
-
- public void requestPermissions(String[] permissions, int requestCode) {
- Log.w(TAG, "No support for requestPermissions");
- }
- public boolean shouldShowRequestPermissionRationale(String permission) {
- Log.w(TAG, "No support for shouldShowRequestPermissionRationale");
- return false;
- }
- public void startActivityForResult(Intent intent, int requestCode) {
- Log.w(TAG, "No support for startActivityForResult");
- };
- }
-
- /** @hide */
- public static final int CMD_ON_CREATE = 0;
- /** @hide */
- public static final int CMD_ON_START = 1;
- /** @hide */
- public static final int CMD_ON_RESTART = 2;
- /** @hide */
- public static final int CMD_ON_RESUME = 3;
- /** @hide */
- public static final int CMD_ON_PAUSE = 4;
- /** @hide */
- public static final int CMD_ON_STOP = 5;
- /** @hide */
- public static final int CMD_ON_DESTROY = 6;
- /** @hide */
- public static final int CMD_ON_BACK_PRESSED = 7;
- /** @hide */
- public static final int CMD_ON_SAVE_INSTANCE_STATE = 8;
- /** @hide */
- public static final int CMD_ON_RESTORE_INSTANCE_STATE = 9;
- /** @hide */
- public static final int CMD_ON_CONFIG_CHANGED = 10;
- /** @hide */
- public static final int CMD_ON_REQUEST_PERMISSIONS_RESULT = 11;
- /** @hide */
- public static final int CMD_ON_NEW_INTENT = 12;
- /** @hide */
- public static final int CMD_ON_ACTIVITY_RESULT = 13;
- /** @hide */
- public static final int CMD_ON_POST_RESUME = 14;
- /** @hide */
- public static final int CMD_ON_LOW_MEMORY = 15;
-
- private final Proxy mProxy;
- private final Context mContext;
- private final Car mCar;
- private final Handler mHandler = new Handler();
-
- public CarActivity(Proxy proxy, Context context, Car car) {
- mProxy = proxy;
- mContext = context;
- mCar = car;
- }
-
- /**
- * Returns a standard app {@link Context} object since this class does not extend {@link
- * Context} link {@link Activity} does.
- */
- public Context getContext() {
- return mContext;
- }
-
- /**
- * Returns an instance of the {@link Car} object. This is the main entry point to interact
- * with the car and it's data.
- *
- * <p/>
- * Note: For "native" platform uses cases you'll need to construct the CarProxy activity with
- * the createCar boolean set to true if you want to use the getCar() method.
- * <pre>
- * {@code
- *
- * class FooProxyActivity extends CarProxyActivity {
- * public FooProxyActivity() {
- * super(FooActivity.class, true);
- * }
- * }
- * }
- * </pre>
- *
- * "Projected" use cases will create a Car instance by default.
- *
- * @throws IllegalStateException if the Car object is not available.
- */
- public Car getCar() {
- if (mCar == null) {
- throw new IllegalStateException("The default Car is not available. You can either " +
- "create a Car by yourself or indicate the need of the default Car in the " +
- "CarProxyActivity's constructor.");
- }
- return mCar;
- }
-
- /**
- * Returns the input manager for car activities.
- */
- public CarInputManager getInputManager() {
- return mProxy.getCarInputManager();
- }
-
- /**
- * See {@link Activity#getResources()}.
- */
- public Resources getResources() {
- return mProxy.getResources();
- }
-
- /**
- * See {@link Activity#setContentView(View)}.
- */
- public void setContentView(View view) {
- mProxy.setContentView(view);
- }
-
- /**
- * See {@link Activity#setContentView(int)}.
- */
- public void setContentView(@LayoutRes int resourceId) {
- mProxy.setContentView(resourceId);
- }
-
- /**
- * See {@link Activity#getLayoutInflater()}.
- */
- public LayoutInflater getLayoutInflater() {
- return mProxy.getLayoutInflater();
- }
-
- /**
- * See {@link Activity#getIntent()}
- */
- public Intent getIntent() {
- return mProxy.getIntent();
- }
-
- /**
- * See {@link Activity#setResult(int)} }
- */
- public void setResult(int resultCode){
- mProxy.setResult(resultCode);
- }
-
-
- /**
- * See {@link Activity#setResult(int, Intent)} }
- */
- public void setResult(int resultCode, Intent data) {
- mProxy.setResult(resultCode, data);
- }
-
- /**
- * See {@link Activity#findViewById(int)} }
- */
- public View findViewById(int id) {
- return mProxy.findViewById(id);
- }
-
- /**
- * See {@link Activity#finish()}
- */
- public void finish() {
- mProxy.finish();
- }
-
- /**
- * See {@link Activity#isFinishing()}
- */
- public boolean isFinishing() {
- return mProxy.isFinishing();
- }
-
- /**
- * See {@link Activity#shouldShowRequestPermissionRationale(String)}
- */
- public boolean shouldShowRequestPermissionRationale(String permission) {
- return mProxy.shouldShowRequestPermissionRationale(permission);
- }
-
- /**
- * See {@link Activity#requestPermissions(String[], int)}
- */
- public void requestPermissions(String[] permissions, int requestCode) {
- if (this instanceof RequestPermissionsRequestCodeValidator) {
- ((RequestPermissionsRequestCodeValidator) this)
- .validateRequestPermissionsRequestCode(requestCode);
- }
- mProxy.requestPermissions(permissions, requestCode);
- }
-
- /**
- * See {@link Activity#setIntent(Intent)}
- */
- public void setIntent(Intent i) {
- mProxy.setIntent(i);
- }
-
- /**
- * See {@link Activity#finishAfterTransition()}
- */
- public void finishAfterTransition() {
- mProxy.finishAfterTransition();
- }
-
- /**
- * See {@link Activity#getMenuInflater()}
- */
- public MenuInflater getMenuInflater() {
- return mProxy.getMenuInflater();
- }
-
- /**
- * See {@link Activity#getWindow()}
- */
- public Window getWindow() {
- return mProxy.getWindow();
- }
-
- /**
- * See {@link Activity#getLastNonConfigurationInstance()}
- */
- public Object getLastNonConfigurationInstance() {
- return null;
- }
-
- /**
- * See {@link Activity#startActivityForResult(Intent, int)}
- */
- public void startActivityForResult(Intent intent, int requestCode) {
- mProxy.startActivityForResult(intent, requestCode);
- }
-
- /**
- * See {@link Activity#runOnUiThread(Runnable)}
- */
- public void runOnUiThread(Runnable runnable) {
- if (Thread.currentThread() == mHandler.getLooper().getThread()) {
- runnable.run();
- } else {
- mHandler.post(runnable);
- }
- }
-
- /** @hide */
- public void dispatchCmd(int cmd, Object... args) {
-
- switch (cmd) {
- case CMD_ON_CREATE:
- assertArgsLength(1, args);
- onCreate((Bundle) args[0]);
- break;
- case CMD_ON_START:
- onStart();
- break;
- case CMD_ON_RESTART:
- onRestart();
- break;
- case CMD_ON_RESUME:
- onResume();
- break;
- case CMD_ON_POST_RESUME:
- onPostResume();
- break;
- case CMD_ON_PAUSE:
- onPause();
- break;
- case CMD_ON_STOP:
- onStop();
- break;
- case CMD_ON_DESTROY:
- onDestroy();
- break;
- case CMD_ON_BACK_PRESSED:
- onBackPressed();
- break;
- case CMD_ON_SAVE_INSTANCE_STATE:
- assertArgsLength(1, args);
- onSaveInstanceState((Bundle) args[0]);
- break;
- case CMD_ON_RESTORE_INSTANCE_STATE:
- assertArgsLength(1, args);
- onRestoreInstanceState((Bundle) args[0]);
- break;
- case CMD_ON_REQUEST_PERMISSIONS_RESULT:
- assertArgsLength(3, args);
- onRequestPermissionsResult(((Integer) args[0]).intValue(),
- (String[]) args[1], convertArray((Integer[]) args[2]));
- break;
- case CMD_ON_CONFIG_CHANGED:
- assertArgsLength(1, args);
- onConfigurationChanged((Configuration) args[0]);
- break;
- case CMD_ON_NEW_INTENT:
- assertArgsLength(1, args);
- onNewIntent((Intent) args[0]);
- break;
- case CMD_ON_ACTIVITY_RESULT:
- assertArgsLength(3, args);
- onActivityResult(((Integer) args[0]).intValue(), ((Integer) args[1]).intValue(),
- (Intent) args[2]);
- break;
- case CMD_ON_LOW_MEMORY:
- onLowMemory();
- break;
- default:
- throw new RuntimeException("Unknown dispatch cmd for CarActivity, " + cmd);
- }
-
- }
-
- /**
- * See {@link Activity#onCreate(Bundle)}
- */
- protected void onCreate(Bundle savedInstanceState) {
- }
-
- /**
- * See {@link Activity#onStart()}
- */
- protected void onStart() {
- }
-
- /**
- * See {@link Activity#onRestart()}
- */
- protected void onRestart() {
- }
-
- /**
- * See {@link Activity#onResume()}
- */
- protected void onResume() {
- }
-
- /**
- * See {@link Activity#onPostResume()}
- */
- protected void onPostResume() {
- }
-
- /**
- * See {@link Activity#onPause()}
- */
- protected void onPause() {
- }
-
- /**
- * See {@link Activity#onStop()}
- */
- protected void onStop() {
- }
-
- /**
- * See {@link Activity#onDestroy()}
- */
- protected void onDestroy() {
- }
-
- /**
- * See {@link Activity#onRestoreInstanceState(Bundle)}
- */
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- }
-
- /**
- * See {@link Activity#onSaveInstanceState(Bundle)}
- */
- protected void onSaveInstanceState(Bundle outState) {
- }
-
- /**
- * See {@link Activity#onBackPressed()}
- */
- protected void onBackPressed() {
- }
-
- /**
- * See {@link Activity#onConfigurationChanged(Configuration)}
- */
- protected void onConfigurationChanged(Configuration newConfig) {
- }
-
- /**
- * See {@link Activity#onNewIntent(Intent)}
- */
- protected void onNewIntent(Intent intent) {
- }
-
- /**
- * See {@link Activity#onRequestPermissionsResult(int, String[], int[])}
- */
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- }
-
- /**
- * See {@link Activity#onActivityResult(int, int, Intent)}
- */
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- }
-
- /**
- * See {@link Activity#onRetainNonConfigurationInstance()}
- */
- public Object onRetainNonConfigurationInstance() {
- return null;
- }
-
- // TODO: hook up panel menu if it's needed in any apps.
- /**
- * Currently always returns false.
- * See {@link Activity#onCreatePanelMenu(int, Menu)}
- */
- public boolean onCreatePanelMenu(int featureId, Menu menu) {
- return false; // default menu will not be displayed.
- }
-
- /**
- * See {@link Activity#onCreateView(View, String, Context, AttributeSet)}
- */
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
- // CarFragmentActivity can override this to dispatch onCreateView to fragments
- return null;
- }
-
- /**
- * See {@link Activity#onLowMemory()}
- */
- public void onLowMemory() {
- }
-
- private void assertArgsLength(int length, Object... args) {
- if (args == null || args.length != length) {
- throw new IllegalArgumentException(
- String.format("Wrong number of parameters. Expected: %d Actual: %d",
- length, args == null ? 0 : args.length));
- }
- }
-
- private static int[] convertArray(Integer[] array) {
- int[] results = new int[array.length];
- for(int i = 0; i < results.length; i++) {
- results[i] = array[i].intValue();
- }
- return results;
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/CarAppUtil.java b/car-support-lib/src/android/support/car/app/CarAppUtil.java
deleted file mode 100644
index 2371025..0000000
--- a/car-support-lib/src/android/support/car/app/CarAppUtil.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 android.support.car.app;
-
-import android.content.Context;
-
-/**
- * @hide
- */
-public final class CarAppUtil {
-
- /**
- * PackageManager.FEATURE_AUTOMOTIVE from M. But redefine here to support L.
- */
- private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
-
- public static boolean isEmbeddedCar(Context context) {
- return context.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE);
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/CarFragmentActivity.java b/car-support-lib/src/android/support/car/app/CarFragmentActivity.java
deleted file mode 100644
index 2a6b631..0000000
--- a/car-support-lib/src/android/support/car/app/CarFragmentActivity.java
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * 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 android.support.car.app;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.car.Car;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentController;
-import android.support.v4.app.FragmentHostCallback;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.LoaderManager;
-import android.support.v4.util.SimpleArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Base class for CarActivities that want to use the support-based Fragment and Loader APIs.
- * <p/>
- * This is mostly a copy of {@link android.support.v4.app.FragmentActivity} retro fitted for use
- * with {@link CarActivity}.
- *
- * @hide
- */
-public class CarFragmentActivity extends CarActivity implements
- CarActivity.RequestPermissionsRequestCodeValidator {
-
- public CarFragmentActivity(Proxy proxy, Context context, Car car) {
- super(proxy, context, car);
- }
-
- private static final String TAG = "CarFragmentActivity";
-
- static final String FRAGMENTS_TAG = "android:support:car:fragments";
-
- // This is the SDK API version of Honeycomb (3.0).
- private static final int HONEYCOMB = 11;
-
- static final int MSG_REALLY_STOPPED = 1;
- static final int MSG_RESUME_PENDING = 2;
-
- final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_REALLY_STOPPED:
- if (mStopped) {
- doReallyStop(false);
- }
- break;
- case MSG_RESUME_PENDING:
- onResumeFragments();
- mFragments.execPendingActions();
- break;
- default:
- super.handleMessage(msg);
- }
- }
-
- };
- final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
-
- boolean mCreated;
- boolean mResumed;
- boolean mStopped;
- boolean mReallyStopped;
- boolean mRetaining;
-
- boolean mRequestedPermissionsFromFragment;
-
- static final class NonConfigurationInstances {
- Object custom;
- List<Fragment> fragments;
- SimpleArrayMap<String, LoaderManager> loaders;
- }
-
- public void setContentFragment(Fragment fragment, int containerId) {
- getSupportFragmentManager().beginTransaction()
- .replace(containerId, fragment)
- .commit();
- }
-
- // ------------------------------------------------------------------------
- // HOOKS INTO ACTIVITY
- // ------------------------------------------------------------------------
-
- /**
- * See {@link FragmentActivity#onActivityResult(int, int, Intent)}
- */
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- mFragments.noteStateNotSaved();
- int index = requestCode>>16;
- if (index > 0) {
- index--;
- final int activeFragmentsCount = mFragments.getActiveFragmentsCount();
- if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) {
- Log.w(TAG, "Activity result fragment index out of range: 0x"
- + Integer.toHexString(requestCode));
- return;
- }
- final List<Fragment> activeFragments =
- mFragments.getActiveFragments(new ArrayList<Fragment>(activeFragmentsCount));
- Fragment frag = activeFragments.get(index);
- if (frag == null) {
- Log.w(TAG, "Activity result no fragment exists for index: 0x"
- + Integer.toHexString(requestCode));
- } else {
- frag.onActivityResult(requestCode&0xffff, resultCode, data);
- }
- return;
- }
-
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- /**
- * See {@link FragmentActivity#startActivityFromFragment(android.app.Fragment, Intent, int)}
- */
- public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
- if (requestCode == -1) {
- startActivityForResult(intent, -1);
- return;
- }
- if ((requestCode&0xffff0000) != 0) {
- throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
- }
- super.startActivityForResult(intent,
- ((getFragmentIndex(fragment)+1)<<16) + (requestCode&0xffff));
- }
-
- /**
- * See {@link FragmentActivity#startActivityForResult(Intent, int)}
- */
- @Override
- public void startActivityForResult(Intent intent, int requestCode) {
- if (requestCode != -1 && (requestCode&0xffff0000) != 0) {
- throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
- }
- super.startActivityForResult(intent, requestCode);
- }
-
- /**
- * See {@link FragmentActivity#onBackPressed()}
- */
- @Override
- public void onBackPressed() {
- if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
- supportFinishAfterTransition();
- }
- }
-
- /**
- * See {@link FragmentActivity#supportFinishAfterTransition()}
- */
- public void supportFinishAfterTransition() {
- super.finishAfterTransition();
- }
-
- /**
- * See {@link FragmentActivity#onConfigurationChanged(Configuration)}
- */
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mFragments.dispatchConfigurationChanged(newConfig);
- }
-
- /**
- * Perform initialization of all fragments and loaders.
- */
- @SuppressWarnings("deprecation")
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- mFragments.attachHost(null /*parent*/);
-
- super.onCreate(savedInstanceState);
-
- NonConfigurationInstances nc =
- (NonConfigurationInstances) getLastNonConfigurationInstance();
- if (nc != null) {
- mFragments.restoreLoaderNonConfig(nc.loaders);
- }
- if (savedInstanceState != null) {
- Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
- mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
- }
- mFragments.dispatchCreate();
- }
-
- /**
- * See {@link FragmentActivity#onCreatePanelMenu(int, Menu)}
- */
- @Override
- public boolean onCreatePanelMenu(int featureId, Menu menu) {
- if (featureId == Window.FEATURE_OPTIONS_PANEL && getMenuInflater() != null) {
- boolean show = super.onCreatePanelMenu(featureId, menu);
- show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
- if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) {
- return show;
- }
- // Prior to Honeycomb, the framework can't invalidate the options
- // menu, so we must always say we have one in case the app later
- // invalidates it and needs to have it shown.
- return true;
- }
- return super.onCreatePanelMenu(featureId, menu);
- }
-
- /**
- * See
- * {@link FragmentActivity#dispatchFragmentsOnCreateView(View, String, Context, AttributeSet)}
- */
- final View dispatchFragmentsOnCreateView(View parent, String name, Context context,
- AttributeSet attrs) {
- return mFragments.onCreateView(parent, name, context, attrs);
- }
-
- @Override
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
- if (!"fragment".equals(name)) {
- return super.onCreateView(parent, name, context, attrs);
- }
-
- final View v = dispatchFragmentsOnCreateView(parent, name, context, attrs);
- if (v == null) {
- return super.onCreateView(parent, name, context, attrs);
- }
- return v;
- }
-
- /**
- * See {@link FragmentActivity#onDestroy()}
- */
- @Override
- protected void onDestroy() {
- super.onDestroy();
-
- doReallyStop(false);
-
- mFragments.dispatchDestroy();
- mFragments.doLoaderDestroy();
- }
-
- /**
- * See {@link FragmentActivity#onLowMemory()}
- */
- @Override
- public void onLowMemory() {
- mFragments.dispatchLowMemory();
- }
-
- /**
- * See {@link FragmentActivity#onPause()}
- */
- @Override
- protected void onPause() {
- super.onPause();
- mResumed = false;
- if (mHandler.hasMessages(MSG_RESUME_PENDING)) {
- mHandler.removeMessages(MSG_RESUME_PENDING);
- onResumeFragments();
- }
- mFragments.dispatchPause();
- }
-
- /**
- * See {@link FragmentActivity#onNewIntent(Intent)}
- */
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- mFragments.noteStateNotSaved();
- }
-
- /**
- * See {@link FragmentActivity#onStateNotSaved()}
- */
- public void onStateNotSaved() {
- mFragments.noteStateNotSaved();
- }
-
- /**
- * See {@link FragmentActivity#onResume()}
- */
- @Override
- protected void onResume() {
- super.onResume();
- mHandler.sendEmptyMessage(MSG_RESUME_PENDING);
- mResumed = true;
- mFragments.execPendingActions();
- }
-
- /**
- * See {@link FragmentActivity#onPostResume()}
- */
- @Override
- protected void onPostResume() {
- super.onPostResume();
- mHandler.removeMessages(MSG_RESUME_PENDING);
- onResumeFragments();
- mFragments.execPendingActions();
- }
-
- /**
- * See {@link FragmentActivity#onResumeFragments()}
- */
- protected void onResumeFragments() {
- mFragments.dispatchResume();
- }
-
- /**
- * See {@link FragmentActivity#onRetainNonConfigurationInstance()}
- */
- @Override
- public final Object onRetainNonConfigurationInstance() {
- if (mStopped) {
- doReallyStop(true);
- }
-
- Object custom = onRetainCustomNonConfigurationInstance();
-
- List<Fragment> fragments = mFragments.retainNonConfig();
- SimpleArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
-
- if (fragments == null && loaders == null && custom == null) {
- return null;
- }
-
- NonConfigurationInstances nci = new NonConfigurationInstances();
- nci.custom = custom;
- nci.fragments = fragments;
- nci.loaders = loaders;
- return nci;
- }
-
- /**
- * See {@link FragmentActivity#onSaveInstanceState(Bundle)}
- */
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- Parcelable p = mFragments.saveAllState();
- if (p != null) {
- outState.putParcelable(FRAGMENTS_TAG, p);
- }
- }
-
- /**
- * See {@link FragmentActivity#onStart()}
- */
- @Override
- protected void onStart() {
- super.onStart();
-
- mStopped = false;
- mReallyStopped = false;
- mHandler.removeMessages(MSG_REALLY_STOPPED);
-
- if (!mCreated) {
- mCreated = true;
- mFragments.dispatchActivityCreated();
- }
-
- mFragments.noteStateNotSaved();
- mFragments.execPendingActions();
-
- mFragments.doLoaderStart();
-
- // NOTE: HC onStart goes here.
-
- mFragments.dispatchStart();
- mFragments.reportLoaderStart();
- }
-
- /**
- * See {@link FragmentActivity#onStop()}
- */
- @Override
- protected void onStop() {
- super.onStop();
-
- mStopped = true;
- mHandler.sendEmptyMessage(MSG_REALLY_STOPPED);
-
- mFragments.dispatchStop();
- }
-
- // ------------------------------------------------------------------------
- // NEW METHODS
- // ------------------------------------------------------------------------
-
- /**
- * See {@link FragmentActivity#onRetainNonConfigurationInstance()}
- */
- public Object onRetainCustomNonConfigurationInstance() {
- return null;
- }
-
- /**
- * See {@link FragmentActivity#getLastNonConfigurationInstance()}
- */
- @SuppressWarnings("deprecation")
- public Object getLastCustomNonConfigurationInstance() {
- NonConfigurationInstances nc = (NonConfigurationInstances)
- getLastNonConfigurationInstance();
- return nc != null ? nc.custom : null;
- }
-
- /**
- * See {@link FragmentActivity#dump(String, FileDescriptor, PrintWriter, String[])}
- */
- public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) {
- // XXX This can only work if we can call the super-class impl. :/
- //ActivityCompatHoneycomb.dump(this, prefix, fd, writer, args);
- }
- writer.print(prefix); writer.print("Local FragmentActivity ");
- writer.print(Integer.toHexString(System.identityHashCode(this)));
- writer.println(" State:");
- String innerPrefix = prefix + " ";
- writer.print(innerPrefix); writer.print("mCreated=");
- writer.print(mCreated); writer.print("mResumed=");
- writer.print(mResumed); writer.print(" mStopped=");
- writer.print(mStopped); writer.print(" mReallyStopped=");
- writer.println(mReallyStopped);
- mFragments.dumpLoaders(innerPrefix, fd, writer, args);
- mFragments.getSupportFragmentManager().dump(prefix, fd, writer, args);
- writer.print(prefix); writer.println("View Hierarchy:");
- dumpViewHierarchy(prefix + " ", writer, getWindow().getDecorView());
- }
-
- private static String viewToString(View view) {
- StringBuilder out = new StringBuilder(128);
- out.append(view.getClass().getName());
- out.append('{');
- out.append(Integer.toHexString(System.identityHashCode(view)));
- out.append(' ');
- switch (view.getVisibility()) {
- case View.VISIBLE: out.append('V'); break;
- case View.INVISIBLE: out.append('I'); break;
- case View.GONE: out.append('G'); break;
- default: out.append('.'); break;
- }
- out.append(view.isFocusable() ? 'F' : '.');
- out.append(view.isEnabled() ? 'E' : '.');
- out.append(view.willNotDraw() ? '.' : 'D');
- out.append(view.isHorizontalScrollBarEnabled()? 'H' : '.');
- out.append(view.isVerticalScrollBarEnabled() ? 'V' : '.');
- out.append(view.isClickable() ? 'C' : '.');
- out.append(view.isLongClickable() ? 'L' : '.');
- out.append(' ');
- out.append(view.isFocused() ? 'F' : '.');
- out.append(view.isSelected() ? 'S' : '.');
- out.append(view.isPressed() ? 'P' : '.');
- out.append(' ');
- out.append(view.getLeft());
- out.append(',');
- out.append(view.getTop());
- out.append('-');
- out.append(view.getRight());
- out.append(',');
- out.append(view.getBottom());
- final int id = view.getId();
- if (id != View.NO_ID) {
- out.append(" #");
- out.append(Integer.toHexString(id));
- final Resources r = view.getResources();
- if (id != 0 && r != null) {
- try {
- String pkgname;
- switch (id&0xff000000) {
- case 0x7f000000:
- pkgname="app";
- break;
- case 0x01000000:
- pkgname="android";
- break;
- default:
- pkgname = r.getResourcePackageName(id);
- break;
- }
- String typename = r.getResourceTypeName(id);
- String entryname = r.getResourceEntryName(id);
- out.append(" ");
- out.append(pkgname);
- out.append(":");
- out.append(typename);
- out.append("/");
- out.append(entryname);
- } catch (Resources.NotFoundException e) {
- }
- }
- }
- out.append("}");
- return out.toString();
- }
-
- private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) {
- writer.print(prefix);
- if (view == null) {
- writer.println("null");
- return;
- }
- writer.println(viewToString(view));
- if (!(view instanceof ViewGroup)) {
- return;
- }
- ViewGroup grp = (ViewGroup)view;
- final int N = grp.getChildCount();
- if (N <= 0) {
- return;
- }
- prefix = prefix + " ";
- for (int i=0; i<N; i++) {
- dumpViewHierarchy(prefix, writer, grp.getChildAt(i));
- }
- }
-
- void doReallyStop(boolean retaining) {
- if (!mReallyStopped) {
- mReallyStopped = true;
- mRetaining = retaining;
- mHandler.removeMessages(MSG_REALLY_STOPPED);
- onReallyStop();
- }
- }
-
- /**
- * Pre-HC, we didn't have a way to determine whether an activity was
- * being stopped for a config change or not until we saw
- * onRetainNonConfigurationInstance() called after onStop(). However
- * we need to know this, to know whether to retain fragments. This will
- * tell us what we need to know.
- */
- void onReallyStop() {
- mFragments.doLoaderStop(mRetaining);
-
- mFragments.dispatchReallyStop();
- }
-
- // ------------------------------------------------------------------------
- // FRAGMENT SUPPORT
- // ------------------------------------------------------------------------
-
- /**
- * Returns the index of a fragment inside {@link #mFragments}.
- *
- * This is a workaround for getting {@link android.support.v4.app.Fragment}'s internal index,
- * which is package visible.
- */
- @SuppressWarnings("ReferenceEquality")
- private int getFragmentIndex(Fragment f) {
- List<Fragment> fragments = mFragments.getActiveFragments(null);
- int index = 0;
- boolean found = false;
- for (Fragment frag : fragments) {
- if (frag == f) {
- found = true;
- break;
- }
- index++;
- }
- if (found) {
- return index;
- }
- return -1;
- }
-
- @Override
- public final void validateRequestPermissionsRequestCode(int requestCode) {
- // We use 8 bits of the request code to encode the fragment id when
- // requesting permissions from a fragment. Hence, requestPermissions()
- // should validate the code against that but we cannot override it as
- // we can not then call super and also the ActivityCompat would call
- // back to this override. To handle this we use dependency inversion
- // where we are the validator of request codes when requesting
- // permissions in ActivityCompat.
- if (mRequestedPermissionsFromFragment) {
- mRequestedPermissionsFromFragment = false;
- } else if ((requestCode & 0xffffff00) != 0) {
- throw new IllegalArgumentException("Can only use lower 8 bits for requestCode");
- }
- }
-
- /**
- * See {@link FragmentActivity#onRequestPermissionsResult(int, String[], int[])}
- */
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
- int index = (requestCode>>8)&0xff;
- if (index != 0) {
- index--;
- final int activeFragmentsCount = mFragments.getActiveFragmentsCount();
- if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) {
- Log.w(TAG, "Activity result fragment index out of range: 0x"
- + Integer.toHexString(requestCode));
- return;
- }
- final List<Fragment> activeFragments =
- mFragments.getActiveFragments(new ArrayList<Fragment>(activeFragmentsCount));
- Fragment frag = activeFragments.get(index);
- if (frag == null) {
- Log.w(TAG, "Activity result no fragment exists for index: 0x"
- + Integer.toHexString(requestCode));
- } else {
- frag.onRequestPermissionsResult(requestCode&0xff, permissions, grantResults);
- }
- }
- }
-
- /**
- * Called by Fragment.requestPermissions() to implement its behavior.
- */
- private void requestPermissionsFromFragment(Fragment fragment, String[] permissions,
- int requestCode) {
- if (requestCode == -1) {
- super.requestPermissions(permissions, requestCode);
- return;
- }
-
- if ((requestCode&0xffffff00) != 0) {
- throw new IllegalArgumentException("Can only use lower 8 bits for requestCode");
- }
- mRequestedPermissionsFromFragment = true;
- super.requestPermissions(permissions,
- ((getFragmentIndex(fragment) + 1) << 8) + (requestCode & 0xff));
- }
-
- /**
- * See {@link FragmentActivity#getSupportFragmentManager()}
- */
- public FragmentManager getSupportFragmentManager() {
- return mFragments.getSupportFragmentManager();
- }
-
- class HostCallbacks extends FragmentHostCallback<CarFragmentActivity> {
- public HostCallbacks() {
- super(CarFragmentActivity.this.getContext(), mHandler, 0 /*window animation*/);
- }
-
- @Override
- public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- CarFragmentActivity.this.dump(prefix, fd, writer, args);
- }
-
- @Override
- public boolean onShouldSaveFragmentState(Fragment fragment) {
- return !isFinishing();
- }
-
- @Override
- public LayoutInflater onGetLayoutInflater() {
- return CarFragmentActivity.this.getLayoutInflater()
- .cloneInContext(CarFragmentActivity.this.getContext());
- }
-
- @Override
- public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
- CarFragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
- }
-
- @Override
- public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
- @NonNull String[] permissions, int requestCode) {
- CarFragmentActivity.this.requestPermissionsFromFragment(fragment,
- permissions, requestCode);
- }
-
- @Override
- public CarFragmentActivity onGetHost() {
- return CarFragmentActivity.this;
- }
-
- @Override
- public boolean onHasWindowAnimations() {
- return getWindow() != null;
- }
-
- @Override
- public int onGetWindowAnimations() {
- final Window w = getWindow();
- return (w == null) ? 0 : w.getAttributes().windowAnimations;
- }
-
- @Nullable
- @Override
- public View onFindViewById(int id) {
- return CarFragmentActivity.this.findViewById(id);
- }
-
- @Override
- public boolean onHasView() {
- final Window w = getWindow();
- return (w != null && w.peekDecorView() != null);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/CarProxyActivity.java b/car-support-lib/src/android/support/car/app/CarProxyActivity.java
deleted file mode 100644
index c1c3405..0000000
--- a/car-support-lib/src/android/support/car/app/CarProxyActivity.java
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- * 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 android.support.car.app;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.support.car.Car;
-import android.support.car.CarConnectionCallback;
-import android.support.car.input.CarInputManager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * android Activity controlling / proxying {@link CarActivity}. Applications should have its own
- * {@link android.app.Activity} overriding only constructor.
- *
- * @hide
- */
-public class CarProxyActivity extends Activity {
- private static final String TAG = "CarProxyActivity";
-
- private final Class<? extends CarActivity> mCarActivityClass;
- private final boolean mNeedConnectedCar;
- private Car mCar;
- // no synchronization, but main thread only
- private CarActivity mCarActivity;
- // no synchronization, but main thread only
- private CarInputManager mInputManager;
-
- private final CopyOnWriteArrayList<Pair<Integer, Object[]>> mCmds =
- new CopyOnWriteArrayList<>();
- private final CarConnectionCallback mConnectionListener= new CarConnectionCallback() {
-
- @Override
- public void onDisconnected(Car car) {
- Log.w(TAG, "Car service disconnected");
- }
-
- @Override
- public void onConnected(Car car) {
- for (Pair<Integer, Object[]> cmd: mCmds) {
- mCarActivity.dispatchCmd(cmd.first, cmd.second);
- }
- mCmds.clear();
- }
- };
-
- private final CarActivity.Proxy mCarActivityProxy = new CarActivity.Proxy() {
- @Override
- public void setContentView(View view) {
- CarProxyActivity.this.setContentView(view);
- }
-
- @Override
- public void setContentView(int layoutResID) {
- CarProxyActivity.this.setContentView(layoutResID);
- }
-
- @Override
- public View findViewById(int id) {
- return CarProxyActivity.this.findViewById(id);
- }
-
- @Override
- public Resources getResources() {
- return CarProxyActivity.this.getResources();
- }
-
- @Override
- public void finish() {
- CarProxyActivity.this.finish();
- }
-
- @Override
- public LayoutInflater getLayoutInflater() {
- return CarProxyActivity.this.getLayoutInflater();
- }
-
- @Override
- public Intent getIntent() {
- return CarProxyActivity.this.getIntent();
- }
-
- @Override
- public CarInputManager getCarInputManager() {
- return CarProxyActivity.this.mInputManager;
- }
-
- @Override
- public void requestPermissions(String[] permissions, int requestCode) {
- CarProxyActivity.this.requestPermissions(permissions, requestCode);
- }
-
- @Override
- public boolean shouldShowRequestPermissionRationale(String permission) {
- return CarProxyActivity.this.shouldShowRequestPermissionRationale(permission);
- }
-
- @Override
- public void setIntent(Intent i) {
- CarProxyActivity.this.setIntent(i);
- }
-
- @Override
- public void setResult(int resultCode) {
- CarProxyActivity.this.setResult(resultCode);
- }
-
- @Override
- public void setResult(int resultCode, Intent data) {
- CarProxyActivity.this.setResult(resultCode, data);
- }
-
- @Override
- public MenuInflater getMenuInflater() {
- return CarProxyActivity.this.getMenuInflater();
- }
-
- @Override
- public void finishAfterTransition() {
- CarProxyActivity.this.finishAfterTransition();
- }
-
- @Override
- public void startActivityForResult(Intent intent, int requestCode) {
- CarProxyActivity.this.startActivityForResult(intent, requestCode);
- }
-
- @Override
- public boolean isFinishing() {
- return CarProxyActivity.this.isFinishing();
- }
-
- @Override
- public Window getWindow() {
- return CarProxyActivity.this.getWindow();
- }
- };
-
- public CarProxyActivity(Class<? extends CarActivity> carActivityClass) {
- this(carActivityClass, false);
- }
-
- public CarProxyActivity(Class<? extends CarActivity> carActivityClass, boolean needCar) {
- mCarActivityClass = carActivityClass;
- mNeedConnectedCar = needCar;
- }
-
- private void createCarActivity() {
- if (mNeedConnectedCar) {
- mCar = Car.createCar(this, mConnectionListener);
- mCar.connect();
- }
- Constructor<? extends CarActivity> ctor;
- try {
- ctor = mCarActivityClass.getDeclaredConstructor(CarActivity.Proxy.class,
- Context.class, Car.class);
- } catch (NoSuchMethodException e) {
- StringBuilder msg = new StringBuilder(
- "Cannot construct given CarActivity, no constructor for ");
- msg.append(mCarActivityClass.getName());
- msg.append("\nAvailable constructors are [");
- final Constructor<?>[] others = mCarActivityClass.getConstructors();
- for (int i=0; i<others.length; i++ ) {
- msg.append("\n ");
- msg.append(others[i].toString());
- }
- msg.append("\n]");
- throw new RuntimeException(msg.toString(), e);
- }
- try {
- mCarActivity = ctor.newInstance(mCarActivityProxy, this, mCar);
- } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
- | InvocationTargetException e) {
- throw new RuntimeException("Cannot construct given CarActivity, constructor failed for "
- + mCarActivityClass.getName(), e);
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- createCarActivity();
- super.onCreate(savedInstanceState);
- mInputManager = new EmbeddedInputManager(this);
- handleCmd(CarActivity.CMD_ON_CREATE, savedInstanceState);
- }
-
- @Override
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
- final View view = mCarActivity.onCreateView(parent, name, context, attrs);
- if (view != null) {
- return view;
- }
- return super.onCreateView(parent, name, context, attrs);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- handleCmd(CarActivity.CMD_ON_START);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- handleCmd(CarActivity.CMD_ON_ACTIVITY_RESULT, requestCode, resultCode, data);
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
- handleCmd(CarActivity.CMD_ON_RESTART);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- handleCmd(CarActivity.CMD_ON_RESUME);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- handleCmd(CarActivity.CMD_ON_PAUSE);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- handleCmd(CarActivity.CMD_ON_STOP);
- }
-
- @Override
- public Object onRetainNonConfigurationInstance() {
- return mCarActivity.onRetainNonConfigurationInstance();
- }
-
- @Override
- public void onBackPressed() {
- handleCmd(CarActivity.CMD_ON_BACK_PRESSED);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- handleCmd(CarActivity.CMD_ON_DESTROY);
- if (mCar != null) {
- mCar.disconnect();
- mCar = null;
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- handleCmd(CarActivity.CMD_ON_SAVE_INSTANCE_STATE, outState);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedState) {
- super.onRestoreInstanceState(savedState);
- handleCmd(CarActivity.CMD_ON_RESTORE_INSTANCE_STATE, savedState);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- handleCmd(CarActivity.CMD_ON_CONFIG_CHANGED, newConfig);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
- handleCmd(CarActivity.CMD_ON_REQUEST_PERMISSIONS_RESULT, requestCode, permissions,
- convertArray(results));
- }
-
- @Override
- protected void onNewIntent(Intent i) {
- handleCmd(CarActivity.CMD_ON_NEW_INTENT, i);
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- handleCmd(CarActivity.CMD_ON_LOW_MEMORY);
- }
-
- private static final class EmbeddedInputManager extends CarInputManager {
- private static final String TAG = "EmbeddedInputManager";
-
- private final InputMethodManager mInputManager;
- private final WeakReference<CarProxyActivity> mActivity;
-
- public EmbeddedInputManager(CarProxyActivity activity) {
- mActivity = new WeakReference<>(activity);
- mInputManager = (InputMethodManager) mActivity.get()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- }
-
- @Override
- public void startInput(EditText view) {
- view.requestFocus();
- mInputManager.showSoftInput(view, 0);
- }
-
- @Override
- public void stopInput() {
- if (mActivity.get() == null) {
- return;
- }
-
- View view = mActivity.get().getCurrentFocus();
- if (view != null) {
- mInputManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
- } else {
- Log.e(TAG, "stopInput called, but no view is accepting input");
- }
- }
-
- @Override
- public boolean isValid() {
- return mActivity.get() != null;
- }
-
- @Override
- public boolean isInputActive() {
- return mInputManager.isActive();
- }
-
- @Override
- public boolean isCurrentCarEditable(EditText view) {
- return mInputManager.isActive(view);
- }
- }
-
- private void handleCmd(int cmd, Object... args) {
- if (!mNeedConnectedCar || (mCar != null && mCar.isConnected())) {
- mCarActivity.dispatchCmd(cmd, args);
- } else {
- // not connected yet. queue it and return.
- Pair<Integer, Object[]> cmdToQ = new Pair<>(Integer.valueOf(cmd), args);
- mCmds.add(cmdToQ);
- }
- }
-
- private static Integer[] convertArray(int[] array) {
- Integer[] grantResults = new Integer[array.length];
- for (int i = 0; i < array.length; i++) {
- grantResults[i] = array[i];
- }
- return grantResults;
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java b/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java
deleted file mode 100644
index 2588a81..0000000
--- a/car-support-lib/src/android/support/car/app/menu/CarDrawerActivity.java
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * 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 android.support.car.app.menu;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.annotation.LayoutRes;
-import android.support.car.Car;
-import android.support.car.app.CarFragmentActivity;
-import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
-import android.support.car.input.CarInputManager;
-import android.support.v4.app.Fragment;
-import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.EditText;
-
-/**
- * Base class for a car app which wants to use a drawer.
- * @hide
- */
-public abstract class CarDrawerActivity extends CarFragmentActivity {
- private static final String TAG = "CarDrawerActivity";
-
- private static final String KEY_DRAWERSHOWING =
- "android.support.car.app.CarDrawerActivity.DRAWER_SHOWING";
- private static final String KEY_INPUTSHOWING =
- "android.support.car.app.CarDrawerActivity.INPUT_SHOWING";
- private static final String KEY_SEARCHBOXENABLED =
- "android.support.car.app.CarDrawerActivity.SEARCH_BOX_ENABLED";
-
- private final Handler mHandler = new Handler();
- private final CarUiController mUiController;
-
- private CarMenuCallbacks mMenuCallbacks;
- private OnMenuClickListener mMenuClickListener;
- private boolean mDrawerShowing;
- private boolean mShowingSearchBox;
- private boolean mSearchBoxEnabled;
- private boolean mOnCreateCalled = false;
- private View.OnClickListener mSearchBoxOnClickListener;
-
- private CarInputManager mInputManager;
- private EditText mSearchBoxView;
-
- public interface OnMenuClickListener {
- /**
- * Called when the menu button is clicked.
- *
- * @return True if event was handled. This will prevent the drawer from executing its
- * default action (opening/closing/going back). False if the event was not handled
- * so the drawer will execute the default action.
- */
- boolean onClicked();
- }
-
- public CarDrawerActivity(Proxy proxy, Context context, Car car) {
- super(proxy, context, car);
- mUiController = createCarUiController();
- }
-
- /**
- * Create a {@link android.support.car.app.menu.CarUiController}.
- *
- * Derived class can override this function to return a customized ui controller.
- */
- protected CarUiController createCarUiController() {
- return CarUiController.createCarUiController(this);
- }
-
- @Override
- public void setContentView(View view) {
- ViewGroup parent = (ViewGroup) findViewById(mUiController.getFragmentContainerId());
- parent.addView(view);
- }
-
- @Override
- public void setContentView(@LayoutRes int resourceId) {
- ViewGroup parent = (ViewGroup) findViewById(mUiController.getFragmentContainerId());
- LayoutInflater inflater = getLayoutInflater();
- inflater.inflate(resourceId, parent, true);
- }
-
- @Override
- public View findViewById(@LayoutRes int id) {
- return super.findViewById(mUiController.getFragmentContainerId()).findViewById(id);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- super.setContentView(mUiController.getContentView());
- mInputManager = getInputManager();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mMenuCallbacks != null) {
- mMenuCallbacks.registerOnChildrenChangedListener(mMenuListener);
- }
- mOnCreateCalled = true;
- }
- });
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mMenuCallbacks != null) {
- mMenuCallbacks.unregisterOnChildrenChangedListener(mMenuListener);
- mMenuCallbacks = null;
- }
- }
- });
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- mDrawerShowing = savedInstanceState.getBoolean(KEY_DRAWERSHOWING);
- mUiController.onRestoreInstanceState(savedInstanceState);
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(KEY_DRAWERSHOWING, mDrawerShowing);
- mUiController.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
- mUiController.onStart();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mUiController.onResume();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mUiController.onPause();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- mUiController.onStop();
- }
-
- /**
- * Set the fragment in the main fragment container.
- */
- public void setContentFragment(Fragment fragment) {
- super.setContentFragment(fragment, mUiController.getFragmentContainerId());
- }
-
- /**
- * Return the main fragment container id for the app.
- */
- public int getFragmentContainerId() {
- return mUiController.getFragmentContainerId();
- }
-
- /**
- * Set the callbacks for car menu interactions.
- */
- public void setCarMenuCallbacks(final CarMenuCallbacks callbacks) {
- if (mOnCreateCalled) {
- throw new IllegalStateException(
- "Cannot call setCarMenuCallbacks after onCreate has been called.");
- }
- mMenuCallbacks = callbacks;
- mUiController.registerCarMenuCallbacks(callbacks);
- }
-
- /**
- * Listener that listens for when the menu button is pressed.
- *
- * @param listener {@link OnMenuClickListener} that will listen for menu button clicks.
- */
- public void setOnMenuClickedListener(OnMenuClickListener listener) {
- mMenuClickListener = listener;
- }
-
- /**
- * Restore the menu button drawable
- */
- public void restoreMenuButtonDrawable() {
- mUiController.restoreMenuButtonDrawable();
- }
-
- /**
- * Sets the menu button bitmap
- *
- * @param bitmap Bitmap to the menu button to.
- */
- public void setMenuButtonBitmap(Bitmap bitmap) {
- mUiController.setMenuButtonBitmap(bitmap);
- }
-
- /**
- * Set the title of the menu.
- */
- public void setTitle(CharSequence title) {
- mUiController.setTitle(title);
- }
-
- /**
- * Set the System UI to be light.
- */
- public void setLightMode() {
- mUiController.setLightMode();
- }
-
- /**
- * Set the System UI to be dark.
- */
- public void setDarkMode() {
- mUiController.setDarkMode();
- }
-
- /**
- * Set the System UI to be dark during day mode and light during night mode.
- */
- public void setAutoLightDarkMode() {
- mUiController.setAutoLightDarkMode();
- }
-
- /**
- * Sets the application background to the given {@link android.graphics.Bitmap}.
- *
- * @param bitmap to use as background.
- */
- public void setBackground(Bitmap bitmap) {
- mUiController.setBackground(bitmap);
- }
-
- /**
- * Sets the color of the scrim to the right of the car menu drawer.
- */
- public void setScrimColor(int color) {
- mUiController.setScrimColor(color);
- }
-
- /**
- * Show the menu associated with the given id in the drawer.
- *
- * @param id Id of the menu to link to.
- * @param title Title that should be displayed.
- */
- public void showMenu(String id, String title) {
- mUiController.showMenu(id, title);
- }
-
- public boolean onMenuClicked() {
- if (mMenuClickListener != null) {
- return mMenuClickListener.onClicked();
- }
- return false;
- }
-
- public void restoreSearchBox() {
- if (isSearchBoxEnabled()) {
- mUiController.showSearchBox(mSearchBoxOnClickListener);
- mShowingSearchBox = true;
- }
- }
-
- private final CarMenuCallbacks.OnChildrenChangedListener mMenuListener =
- new CarMenuCallbacks.OnChildrenChangedListener() {
- @Override
- public void onChildrenChanged(String parentId) {
- if (mOnCreateCalled) {
- mUiController.onChildrenChanged(parentId);
- }
- }
-
- @Override
- public void onChildChanged(String parentId, Bundle item,
- Drawable leftIcon, Drawable rightIcon) {
- DisplayMetrics metrics = getResources().getDisplayMetrics();
- if (leftIcon != null) {
- item.putParcelable(MenuItemConstants.KEY_LEFTICON,
- Utils.snapshot(metrics, leftIcon));
- }
-
- if (rightIcon != null) {
- item.putParcelable(MenuItemConstants.KEY_RIGHTICON,
- Utils.snapshot(metrics, rightIcon));
- }
- if (mOnCreateCalled) {
- mUiController.onChildChanged(parentId, item);
- }
- }
- };
-
- public void closeDrawer() {
- mUiController.closeDrawer();
- }
-
- public void openDrawer() {
- mUiController.openDrawer();
- }
-
- public boolean isDrawerShowing() {
- return mDrawerShowing;
- }
-
- public void setDrawerShowing(boolean showing) {
- mDrawerShowing = showing;
- }
-
- public boolean isSearchBoxEnabled() {
- return mSearchBoxEnabled;
- }
-
- public boolean isShowingSearchBox() {
- return mShowingSearchBox;
- }
-
- /**
- * Shows a small clickable {@link android.widget.EditText}.
- *
- * {@link View} will be {@code null} in {@link View.OnClickListener#onClick(View)}.
- *
- * @param listener {@link View.OnClickListener} that is called when user selects the
- * {@link android.widget.EditText}.
- */
- public void showSearchBox(View.OnClickListener listener) {
- if (!isDrawerShowing()) {
- mUiController.showSearchBox(listener);
- mShowingSearchBox = true;
- }
- mSearchBoxEnabled = true;
- mSearchBoxOnClickListener = listener;
- }
-
- public void showSearchBox() {
- showSearchBox(mSearchBoxOnClickListener);
- }
-
- public void hideSearchBox() {
- if (isShowingSearchBox()) {
- stopInput();
- }
- mSearchBoxEnabled = false;
- }
-
- public void setSearchBoxEditListener(SearchBoxEditListener listener) {
- mUiController.setSearchBoxEditListener(listener);
- }
-
- public void stopInput() {
- // STOPSHIP: sometimes focus is lost and we are not able to hide the keyboard.
- // properly fix this before we ship.
- if (mSearchBoxView != null) {
- mSearchBoxView.requestFocusFromTouch();
- }
- mUiController.stopInput();
- mInputManager.stopInput();
- mShowingSearchBox = false;
- }
-
- /**
- * Start input on the search box that is provided by a car ui provider.
- * TODO: Migrate to use the new input/search api once it becomes stable (b/27108311).
- * @param hint Search hint
- */
- public void startInput(String hint) {
- startInput(hint, mSearchBoxOnClickListener);
- }
-
- /**
- * Start input on the search box that is provided by a car ui provider.
- * TODO: Migrate to use the new input/search api once it becomes stable (b/27108311).
- * @param hint Search hint
- * @param onClickListener Listener for the search box clicks.
- */
- public void startInput(final String hint, final View.OnClickListener onClickListener) {
- mInputManager = getInputManager();
- EditText inputView = mUiController.startInput(hint, onClickListener);
- getInputManager().startInput(inputView);
- mSearchBoxView = inputView;
- mShowingSearchBox = true;
- }
-
- public void setSearchBoxColors(int backgroundColor, int searchLogoColor, int textColor,
- int hintTextColor) {
- mUiController.setSearchBoxColors(backgroundColor, searchLogoColor,
- textColor, hintTextColor);
- }
-
- public void setSearchBoxEndView(View endView) {
- mUiController.setSearchBoxEndView(endView);
- }
-
- public void showToast(String text, int duration) {
- mUiController.showToast(text, duration);
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/CarMenu.java b/car-support-lib/src/android/support/car/app/menu/CarMenu.java
deleted file mode 100644
index 59c4ea7..0000000
--- a/car-support-lib/src/android/support/car/app/menu/CarMenu.java
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * 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 android.support.car.app.menu;
-
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
-import android.util.DisplayMetrics;
-import android.widget.RemoteViews;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * CarMenu is used to pass back the menu items of a sublevel to the CarMenu subscriber.
- * Use the {@link Builder} to populate the contents of the sublevel.
- * @hide
- */
-public class CarMenu {
- private boolean mDetachCalled;
- private boolean mSendResultCalled;
- private final DisplayMetrics mMetrics;
-
- public CarMenu(DisplayMetrics metrics) {
- mMetrics = metrics;
- }
-
- /**
- * Send the result back to the caller.
- */
- public void sendResult(List<Item> results) {
- if (mSendResultCalled) {
- throw new IllegalStateException("sendResult() called twice.");
- }
- mSendResultCalled = true;
-
- List<Bundle> resultBundle = new ArrayList<>();
- for (Item item : results) {
- ItemImpl impl = (ItemImpl) item;
- if (impl.mIcon != null) {
- impl.mBundle.putParcelable(MenuItemConstants.KEY_LEFTICON, snapshot(impl.mIcon));
- }
- if (impl.mRightIcon != null) {
- impl.mBundle.putParcelable(
- MenuItemConstants.KEY_RIGHTICON, snapshot(impl.mRightIcon));
- }
- resultBundle.add(impl.mBundle);
- }
- onResultReady(resultBundle);
- }
-
- private Bitmap snapshot(Drawable drawable) {
- return Utils.snapshot(mMetrics, drawable);
- }
-
- /**
- * Detach this message from the current thread and allow the {@link #sendResult}
- * call to happen later. This stops blocking the current thread.
- */
- public void detach() {
- if (mDetachCalled) {
- throw new IllegalStateException("detach() called when detach() had already"
- + " been called.");
- }
- if (mSendResultCalled) {
- throw new IllegalStateException("detach() called when sendResult() had already"
- + " been called");
- }
- mDetachCalled = true;
- }
-
- /**
- * Returns whether results were actually sent.
- *
- * @return {@code true} if {@link #sendResult(java.util.List)} or {@link #detach()} has been called.
- * @hide
- */
- public boolean isDone() {
- return mDetachCalled || mSendResultCalled;
- }
-
- /**
- * Called when the result is sent, after assertions about not being called twice
- * have happened.
- * @hide
- */
- protected void onResultReady(List<Bundle> result) {
- }
-
- /**
- * An individual item in a menu.
- */
- public interface Item {
- /**
- * Gets the id of the menu item.
- *
- * @return The id of the menu item.
- */
- String getId();
-
- /**
- * Gets the title of the menu item.
- *
- * @return The title of the menu item. {@code null} if there is no title.
- */
- String getTitle();
-
- /**
- * Gets the text of the menu item.
- *
- * @return The text of the menu item. {@code null} if there is no text.
- */
- String getText();
-
- /**
- * Gets the integer constant for the widget.
- *
- * @return Either {@link MenuItemConstants.WidgetTypes#WIDGET_CHECKBOX} or -1,
- * if no widget was set.
- */
- int getWidget();
-
- /**
- * Gets the widget state. The return value is only valid if a widget was set.
- *
- * @return {@code true} if the widget is enabled, {@code false} if the widget is disabled.
- */
- boolean getWidgetState();
-
- /**
- * Gets the flags set for this menu item.
- *
- * @return The flags set.
- */
- int getFlags();
- }
-
- /** @hide */
- static class ItemImpl implements Item {
- final Bundle mBundle;
- final Drawable mIcon;
- final Drawable mRightIcon;
-
- ItemImpl(Bundle bundle, Drawable icon, Drawable rightIcon) {
- mBundle = bundle;
- mIcon = icon;
- mRightIcon = rightIcon;
- }
-
- @Override
- public String getId() {
- return mBundle.getString(MenuItemConstants.KEY_ID);
- }
-
- @Override
- public String getTitle() {
- return mBundle.getString(MenuItemConstants.KEY_TITLE);
- }
-
- @Override
- public String getText() {
- return mBundle.getString(MenuItemConstants.KEY_TEXT);
- }
-
- @Override
- public int getWidget() {
- return mBundle.getInt(MenuItemConstants.KEY_WIDGET, -1);
- }
-
- @Override
- public boolean getWidgetState() {
- return mBundle.getBoolean(MenuItemConstants.KEY_WIDGET_STATE);
- }
-
- @Override
- public int getFlags() {
- return mBundle.getInt(MenuItemConstants.KEY_FLAGS);
- }
- }
-
- /**
- * Builder to build an {@link Item}. Calls to the builder can be chained.
- */
- public static class Builder {
- private final Bundle mBundle = new Bundle();
- // Drawable icons that will later be turned into Bitmaps and inserted into the Bundle
- private Drawable mIcon;
- private Drawable mRightIcon;
-
- /**
- * Construct a Builder with a specific id.
- *
- * @param id Unique id used to identify this menu item. If it is browsable, then it
- * will also be used to fetch this item's submenu.
- */
- public Builder(String id) {
- if (id == null) {
- throw new IllegalStateException("Cannot pass a null id to the Builder.");
- }
- mBundle.putString(MenuItemConstants.KEY_ID, id);
- }
-
- /**
- * Sets the title.
- *
- * @param title Title to set
- * @return This to chain calls
- */
- public Builder setTitle(String title) {
- mBundle.putString(MenuItemConstants.KEY_TITLE, title);
- return this;
- }
-
- /**
- * Sets the body text.
- *
- * @param text Text to set
- * @return This {@link Builder} to chain calls
- */
- public Builder setText(String text) {
- mBundle.putString(MenuItemConstants.KEY_TEXT, text);
- return this;
- }
-
- /**
- * Sets the icon.
- *
- * @param bitmap Icon to set
- * @return This {@link Builder} to chain calls
- */
- public Builder setIcon(Bitmap bitmap) {
- mBundle.putParcelable(MenuItemConstants.KEY_LEFTICON, bitmap);
- return this;
- }
-
- /**
- * Sets the icon.
- *
- * A snapshot of the {@link android.graphics.drawable.Drawable} is captured at the time the {@link Item} obtained
- * from this Builder is passed to {@link #sendResult(java.util.List)}. Any changes that are
- * made to the Drawable after that point will not affect what is displayed by the menu.
- *
- * @param drawable Icon to set
- * @return This {@link Builder} to chain calls
- */
- public Builder setIconFromSnapshot(Drawable drawable) {
- mIcon = drawable;
- return this;
- }
-
- /**
- * Sets the right icon.
- *
- * @param bitmap Icon to set
- * @return This {@link Builder} to chain calls
- */
- public Builder setRightIcon(Bitmap bitmap) {
- mBundle.putParcelable(MenuItemConstants.KEY_RIGHTICON, bitmap);
- return this;
- }
-
- /**
- * Sets the right icon.
- *
- * A snapshot of the {@link android.graphics.drawable.Drawable} is captured at the time the
- * {@link Item} obtained
- * from this Builder is passed to {@link #sendResult(java.util.List)}. Any changes that are
- * made to the Drawable after that point will not affect what is displayed by the menu.
- *
- * @param drawable Icon to set
- * @return This {@link Builder} to chain calls
- */
- public Builder setRightIconFromSnapshot(Drawable drawable) {
- mRightIcon = drawable;
- return this;
- }
-
- /**
- * The widget to set.
- * It can be anyone of the following: button, checkbox, toggle
- *
- * @param widget
- * @return This {@link Builder} to chain calls
- */
- public Builder setWidget(int widget) {
- mBundle.putInt(MenuItemConstants.KEY_WIDGET, widget);
- return this;
- }
-
- /**
- * If a widget is set, the state the widget is in.
- * This is only applicable if the widget is "checkbox" or "toggle"
- *
- * @param on If true, a checkbox is checked and a toggle is set to "on".
- * If false, a checkbox will be unchecked and a toggle will be set to "off".
- * @return This {@link Builder} to chain calls
- */
- public Builder setWidgetState(boolean on) {
- mBundle.putBoolean(MenuItemConstants.KEY_WIDGET_STATE, on);
- return this;
- }
-
- /**
- * Indicates that this is an empty placeholder menu item.
- * Only title and icon will be available in this situation.
- *
- * @param isEmptyPlaceHolder If true, this CarMenu will be a placeholder item for no data
- * in menu list.
- * @return This {@link Builder} to chain calls
- */
- public Builder setIsEmptyPlaceHolder(boolean isEmptyPlaceHolder) {
- mBundle.putBoolean(MenuItemConstants.KEY_EMPTY_PLACEHOLDER, isEmptyPlaceHolder);
- return this;
- }
-
- /**
- * If the widget is {@link MenuItemConstants#WIDGET_TEXT_VIEW}, then this will allow setting
- * the right text.
- *
- * @param text The text to set
- * @return this {@link Builder} to chain calls
- */
- public Builder setRightText(String text) {
- mBundle.putString(MenuItemConstants.KEY_RIGHTTEXT, text);
- return this;
- }
-
- public Builder setRemoteViews(RemoteViews views) {
- mBundle.putParcelable(MenuItemConstants.KEY_REMOTEVIEWS, views);
- return this;
- }
-
- /**
- * Sets additional flags for this item.
- * {@link MenuItemConstants#FLAG_BROWSABLE} is the only one that can be currently set
- *
- * @param flags flags to set
- * @return This {@link Builder} to chain calls
- */
- public Builder setFlags(@MenuItemConstants.MenuItemFlags int flags) {
- mBundle.putInt(MenuItemConstants.KEY_FLAGS, flags);
- return this;
- }
-
- /**
- * Add this item to the list of items to be sent when {@link CarMenu#sendResult(java.util.List)}
- * is called
- */
- public Item build() {
- return new ItemImpl(mBundle, mIcon, mRightIcon);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java b/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java
deleted file mode 100644
index 29226b5..0000000
--- a/car-support-lib/src/android/support/car/app/menu/CarMenuCallbacks.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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 android.support.car.app.menu;
-
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-
-/**
- * Class that the CarMenu communicates with to fetch the contents of the CarMenu
- * @hide
- */
-public abstract class CarMenuCallbacks {
- /**
- * Listens for calls to notifyChildrenChanged and onChildrenChanged
- * @hide
- */
- public interface OnChildrenChangedListener {
- /** Called when app wants to notify that contents of an entire menu have changed. */
- void onChildrenChanged(String parentId);
- /**
- * Called when app wants to notify that contents of a single item has changed.
- *
- * @param item Contents of {@link android.os.Bundle} are used to update the contents of the existing
- * item.
- * @param leftIcon Drawable to convert to bitmap
- * @param rightIcon Drawable to convert to bitmap
- */
- void onChildChanged(String parentId, Bundle item, Drawable leftIcon, Drawable rightIcon);
- }
-
- private OnChildrenChangedListener mListener;
-
- /**
- * Called when the CarMenu wants to get the root
- *
- * @param hints Hints that the Drawer can use to modify behavior. It can be null.
- * @return The {@link RootMenu} which contains the root id and any hints
- */
- public abstract RootMenu onGetRoot(Bundle hints);
-
- /**
- * Called when the CarMenu subscribes to to a certain id
- *
- * @param parentId ID to subscribe to
- * @param result {@link CarMenu} that is used to communicate back the results
- */
- public abstract void onLoadChildren(String parentId,
- CarMenu result);
-
- /**
- * Notify the CarMenu that the menu defined by the id has changed. This will cause the CarMenu
- * to fetch the menu items.
- *
- * @param parentId The id which identifies the menu that changed
- */
- public void notifyChildrenChanged(String parentId) {
- if (mListener != null) {
- mListener.onChildrenChanged(parentId);
- }
- }
-
- /**
- * Register an OnChildrenChangedListener to detect when a menu has changed
- *
- * @param listener listener to register
- * @hide
- */
- public void registerOnChildrenChangedListener(OnChildrenChangedListener listener) {
- mListener = listener;
- }
-
- /**
- * Unregister an OnChildrenChangedListener to detect when a menu has changed.
- *
- * @param listener listener to unregister
- * @hide
- */
- public void unregisterOnChildrenChangedListener(OnChildrenChangedListener listener) {
- if (listener != mListener) {
- throw new IllegalStateException(
- "Trying to unregister a listener that was not registered!");
- }
- mListener = null;
- }
-
- /**
- * Called when the CarMenu is opened
- */
- public void onCarMenuOpened() {}
-
- /**
- * Called when the CarMenu is closed
- */
- public void onCarMenuClosed() {}
-
- /**
- * Called when the CarMenu is opening
- */
- public void onCarMenuOpening() {}
-
- /**
- * Called when the CarMenu is closing
- */
- public void onCarMenuClosing() {}
-
- /**
- * Called when an item is clicked
- *
- * @param id Id of the item that is clicked
- */
- public void onItemClicked(String id) {}
-
- /**
- * Called when an item is long clicked
- *
- * @param id Id of the item that is long clicked
- *
- * @return Return true if handled, false if not. Returning false also means that the
- * onItemClicked handler will be called.
- */
- public boolean onItemLongClicked(String id) {
- return false;
- }
-
- /**
- * Called when the state of the CarMenu has changed.
- * TODO: Describe the state. This may be removed moving forward depending on if it is useful
- *
- * @param newState The new state of the CarMenu
- */
- public void onStateChanged(int newState) {}
-
- /**
- * Notify that an item has changed. Use a {@link CarMenu.Builder} to build the item and pu the
- * updated contents inside. Note that this cannot be used to change an item's layout, but to
- * modify existing contents.
- *
- * @param parentId parentId of the item.
- * @param item Updated contents of the item.
- */
- public void notifyChildChanged(String parentId, CarMenu.Item item) {
- if (mListener != null) {
- CarMenu.ItemImpl realItem = (CarMenu.ItemImpl) item;
- mListener.onChildChanged(parentId,
- realItem.mBundle,
- realItem.mIcon,
- realItem.mRightIcon);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/CarUiController.java b/car-support-lib/src/android/support/car/app/menu/CarUiController.java
deleted file mode 100644
index afb91ff..0000000
--- a/car-support-lib/src/android/support/car/app/menu/CarUiController.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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 android.support.car.app.menu;
-
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.support.car.app.CarAppUtil;
-import android.view.View;
-import android.widget.EditText;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * A controller for a {@link android.support.car.app.CarActivity} to manipulate its car UI, and
- * under the hood it talks to a car ui provider.
- * @hide
- */
-public abstract class CarUiController {
- static final String PROJECTED_UI_CONTROLLER =
- "com.google.android.car.ProjectedCarUiController";
-
- protected final CarDrawerActivity mActivity;
-
- public CarUiController(CarDrawerActivity activity) {
- mActivity = activity;
- validateCarUiPackage();
- }
-
- public static CarUiController createCarUiController(CarDrawerActivity activity) {
- if (CarAppUtil.isEmbeddedCar(activity.getContext())) {
- return new EmbeddedCarUiController(activity);
- } else {
- return getProjectedCarUiController(PROJECTED_UI_CONTROLLER, activity);
- }
- }
-
- private static CarUiController getProjectedCarUiController(String className,
- CarDrawerActivity activity) {
- Class<? extends CarUiController> uiControllerClass = null;
- try {
- uiControllerClass = Class.forName(className).asSubclass(CarUiController.class);
- } catch (ClassNotFoundException e) {
- throw new IllegalArgumentException("Cannot find ProjectedCarUiController:" + className,
- e);
- }
- Constructor<? extends CarUiController> ctor;
- try {
- ctor = uiControllerClass.getDeclaredConstructor(CarDrawerActivity.class);
- } catch (NoSuchMethodException e) {
- throw new IllegalArgumentException("Cannot construct ProjectedCarUiController," +
- " no constructor: " + className, e);
- }
- try {
- return ctor.newInstance(activity);
- } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
- | InvocationTargetException e) {
- throw new IllegalArgumentException(
- "Cannot construct ProjectedCarUiController, constructor failed for "
- + uiControllerClass.getName(), e);
- }
- }
-
- public abstract void validateCarUiPackage();
-
- public abstract int getFragmentContainerId();
-
- public abstract void setTitle(CharSequence title);
-
- public abstract void setScrimColor(int color);
-
- public abstract View getContentView();
-
- public abstract void registerCarMenuCallbacks(CarMenuCallbacks callbacks);
-
- public abstract void restoreMenuButtonDrawable();
-
- public abstract void setMenuButtonBitmap(Bitmap bitmap);
-
- public abstract void setLightMode();
-
- /**
- * Set the System UI to be dark.
- */
- public abstract void setDarkMode();
-
- /**
- * Set the System UI to be dark during day mode and light during night mode.
- */
- public abstract void setAutoLightDarkMode();
-
- /**
- * Sets the application background to the given {@link android.graphics.Bitmap}.
- *
- * @param bitmap to use as background.
- */
- public abstract void setBackground(Bitmap bitmap);
-
- public abstract void onRestoreInstanceState(Bundle savedState);
-
- public abstract void onSaveInstanceState(Bundle outState);
-
- public abstract void closeDrawer();
-
- public abstract void openDrawer();
-
- public abstract void showMenu(String id, String title);
-
- public abstract void onStart();
-
- public abstract void onResume();
-
- public abstract void onPause();
-
- public abstract void onStop();
-
- public abstract void showSearchBox(View.OnClickListener listener);
-
- public abstract void setSearchBoxColors(int backgroundColor, int googleLogoColor, int textColor,
- int hintTextColor);
-
- public abstract void setSearchBoxEditListener(SearchBoxEditListener listener);
-
- public abstract EditText startInput(
- String hint, View.OnClickListener searchBoxClickListener);
-
- public abstract CharSequence getText();
-
- public abstract void stopInput();
-
- public abstract void setSearchBoxEndView(View view);
-
- public abstract void onChildrenChanged(String parentId);
-
- public abstract void onChildChanged(String parentId, Bundle item);
-
- public abstract void showToast(String msg, int duration);
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/EmbeddedCarUiController.java b/car-support-lib/src/android/support/car/app/menu/EmbeddedCarUiController.java
deleted file mode 100644
index 248245d..0000000
--- a/car-support-lib/src/android/support/car/app/menu/EmbeddedCarUiController.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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 android.support.car.app.menu;
-
-import android.car.app.menu.CarUiEntry;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.support.car.app.menu.compat.EmbeddedCarMenuCallbacksCompat;
-import android.support.car.app.menu.compat.EmbeddedSearchBoxEditListenerCompat;
-import android.view.View;
-import android.widget.EditText;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * A {@link android.support.car.app.menu.CarUiController} that talks to embedded car ui provider.
- * @hide
- */
-public class EmbeddedCarUiController extends CarUiController {
-
- private static final String TAG = "EmbeddedCarUiController";
- // TODO: load the package name and class name from resources
- private static final String UI_ENTRY_CLASS_NAME = ".CarUiEntry";
- private static final String CAR_UI_PROVIDER_PKG = "android.car.ui.provider";
- private static final String CAR_SERVICE_PKG = "com.android.car";
-
- private CarUiEntry mCarUiEntry;
- private EmbeddedCarMenuCallbacksCompat mCallback;
-
- public EmbeddedCarUiController(CarDrawerActivity activity) {
- super(activity);
- try {
- Context carUiContext = mActivity.getContext().createPackageContext(
- CAR_UI_PROVIDER_PKG,
- Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
-
- ClassLoader classLoader = carUiContext.getClassLoader();
- Class<?> loadedClass = classLoader.loadClass(CAR_UI_PROVIDER_PKG + UI_ENTRY_CLASS_NAME);
- mCarUiEntry = (CarUiEntry) loadedClass.getConstructor(Context.class, Context.class)
- .newInstance(carUiContext, mActivity.getContext());
- } catch (PackageManager.NameNotFoundException | ClassNotFoundException e) {
- throw new RuntimeException("Cannot find CarUiEntry from " + CAR_UI_PROVIDER_PKG + "/"
- + UI_ENTRY_CLASS_NAME, e);
- } catch (IllegalAccessException | InvocationTargetException | InstantiationException
- | NoSuchMethodException e) {
- throw new RuntimeException("Cannot cast CarUiEntry.", e);
- }
- }
-
- @Override
- public void validateCarUiPackage() {
- try {
- PackageManager packageManager = mActivity.getContext().getPackageManager();
- int flag = packageManager.getApplicationInfo(CAR_UI_PROVIDER_PKG, 0).flags;
- if ((flag & ApplicationInfo.FLAG_SYSTEM) == 0
- && (flag & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
- throw new SecurityException("CarUiProvider is not a system app!");
- }
-
- // Do not change the order of the two packages as it need to be in sync with
- // the error message.
- int signatureMatchResult =
- packageManager.checkSignatures(CAR_SERVICE_PKG, CAR_UI_PROVIDER_PKG);
- if (signatureMatchResult != PackageManager.SIGNATURE_MATCH) {
- throw new SecurityException("CarUiProvider and CarService signature check" +
- " failed. " + getSignatureFailureMessage(signatureMatchResult));
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException("Cannot find CarUiProvider" + CAR_UI_PROVIDER_PKG, e);
- }
- }
-
- @Override
- public int getFragmentContainerId() {
- return mCarUiEntry.getFragmentContainerId();
- }
-
- @Override
- public void setTitle(CharSequence title) {
- mCarUiEntry.setTitle(title);
- }
-
- @Override
- public void setScrimColor(int color) {
- mCarUiEntry.setScrimColor(color);
- }
-
- @Override
- public View getContentView() {
- return mCarUiEntry.getContentView();
- }
-
- @Override
- public void registerCarMenuCallbacks(final CarMenuCallbacks callbacks) {
- mCallback = new EmbeddedCarMenuCallbacksCompat(mActivity, callbacks);
- mCarUiEntry.setCarMenuCallbacks(mCallback);
- }
-
- @Override
- public void restoreMenuButtonDrawable() {
- mCarUiEntry.restoreMenuDrawable();
- }
-
- @Override
- public void setMenuButtonBitmap(Bitmap bitmap) {
- mCarUiEntry.setMenuButtonBitmap(bitmap);
- }
-
- @Override
- public void setLightMode() {
- mCarUiEntry.setLightMode();
- }
-
- @Override
- public void setDarkMode() {
- mCarUiEntry.setDarkMode();
- }
-
- @Override
- public void setAutoLightDarkMode() {
- mCarUiEntry.setAutoLightDarkMode();
- }
-
- @Override
- public void setBackground(Bitmap bitmap) {
- mCarUiEntry.setBackground(bitmap);
- }
-
- @Override
- public void onRestoreInstanceState(Bundle savedState) {
- mCarUiEntry.onRestoreInstanceState(savedState);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- mCarUiEntry.onSaveInstanceState(outState);
- }
-
- @Override
- public void closeDrawer() {
- mCarUiEntry.closeDrawer();
- }
-
- @Override
- public void openDrawer() {
- mCarUiEntry.openDrawer();
- }
-
- @Override
- public void showMenu(String id, String title) {
- mCarUiEntry.showMenu(id, title);
- }
-
- @Override
- public void onStart() {
- mCarUiEntry.onStart();
- }
-
- @Override
- public void onResume() {
- mCarUiEntry.onResume();
- }
-
- @Override
- public void onPause() {
- mCarUiEntry.onPause();
- }
-
- @Override
- public void onStop() {
- mCarUiEntry.onStop();
- }
-
- @Override
- public void showSearchBox(View.OnClickListener listener) {
- mCarUiEntry.showSearchBox(listener);
- }
-
- @Override
- public void setSearchBoxColors(int backgroundColor, int searchLogocolor, int textColor, int hintTextColor) {
- mCarUiEntry.setSearchBoxColors(backgroundColor, searchLogocolor, textColor, hintTextColor);
- }
-
- @Override
- public void setSearchBoxEditListener(SearchBoxEditListener listener) {
- mCarUiEntry.setSearchBoxEditListener(new EmbeddedSearchBoxEditListenerCompat(listener));
- }
-
- @Override
- public CharSequence getText() {
- return mCarUiEntry.getSearchBoxText();
- }
-
- @Override
- public void stopInput() {
- mCarUiEntry.stopInput();
- }
-
- @Override
- public EditText startInput(String hint, View.OnClickListener listener) {
- return mCarUiEntry.startInput(hint, listener);
- }
-
- @Override
- public void setSearchBoxEndView(View view) {
- mCarUiEntry.setSearchBoxEndView(view);
- }
-
- @Override
- public void onChildChanged(String parentId, Bundle item) {
- mCallback.onChildChanged(parentId, item);
- }
-
- @Override
- public void onChildrenChanged(String parentId) {
- mCallback.onChildrenChanged(parentId);
- }
-
- @Override
- public void showToast(String msg, int duration) {
- mCarUiEntry.showToast(msg, duration);
- }
-
- /**
- * Return more informative error message from the PackageManager's signature check result.
- */
- private static final String getSignatureFailureMessage(int code) {
- switch (code) {
- case PackageManager.SIGNATURE_NEITHER_SIGNED:
- return "Both CarService and CarUiProvider are not signed";
- case PackageManager.SIGNATURE_FIRST_NOT_SIGNED:
- return "CarService not signed";
- case PackageManager.SIGNATURE_SECOND_NOT_SIGNED:
- return "CarUiProvider not signed";
- case PackageManager.SIGNATURE_NO_MATCH:
- return "Signatures do not match";
- case PackageManager.SIGNATURE_UNKNOWN_PACKAGE:
- return "CarService not found";
- default:
- return "Unknown error code";
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/RootMenu.java b/car-support-lib/src/android/support/car/app/menu/RootMenu.java
deleted file mode 100644
index 64193ab..0000000
--- a/car-support-lib/src/android/support/car/app/menu/RootMenu.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 android.support.car.app.menu;
-
-import android.os.Bundle;
-import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
-
-/**
- * Stores the root id for the menu. The RootMenu is the main menu.
- * Also allows passing hints through bundles. Hints allow the
- * the recipient to alter its behavior based on the hints.
- * @hide
- */
-public class RootMenu {
- private final Bundle mBundle;
-
- /**
- * Create a root with no extra hints.
- *
- * @param id Root id
- */
- public RootMenu(String id) {
- this(id, null);
- }
-
- /**
- * Create a root with hints
- *
- * @param id Root id
- * @param extras Hints to pass along
- */
- public RootMenu(String id, Bundle extras) {
- mBundle = new Bundle();
- mBundle.putString(MenuItemConstants.KEY_ID, id);
- if (extras != null) {
- mBundle.putAll(extras);
- }
- }
-
- /**
- * Get the root id
- *
- * @return The root id
- */
- public String getId() {
- return mBundle.getString(MenuItemConstants.KEY_ID);
- }
-
- /**
- * Get any hints
- *
- * @return A bundle if there are hints; null otherwise.
- */
- public Bundle getBundle() {
- return new Bundle(mBundle);
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java b/car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java
deleted file mode 100644
index c8e67bc..0000000
--- a/car-support-lib/src/android/support/car/app/menu/SearchBoxEditListener.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 android.support.car.app.menu;
-
-/**
- * A listener that listens the user input to the search box.
- * @hide
- */
-public abstract class SearchBoxEditListener {
- /**
- * The user hit enter on the keyboard.
- */
- public abstract void onSearch(String text);
-
- /**
- * The user changed the text in the search box with the keyboard.
- */
- public abstract void onEdit(String text);
-}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/app/menu/Utils.java b/car-support-lib/src/android/support/car/app/menu/Utils.java
deleted file mode 100644
index f27bc2f..0000000
--- a/car-support-lib/src/android/support/car/app/menu/Utils.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 android.support.car.app.menu;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.util.DisplayMetrics;
-
-/**
- * @hide
- */
-public class Utils {
- public static Bitmap snapshot(DisplayMetrics metrics, Drawable drawable) {
- int width = drawable.getIntrinsicWidth();
- int height = drawable.getIntrinsicHeight();
- Bitmap bitmap = Bitmap.createBitmap(metrics, width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- return bitmap;
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/compat/CarMenuConstantsComapt.java b/car-support-lib/src/android/support/car/app/menu/compat/CarMenuConstantsComapt.java
deleted file mode 100644
index d107260..0000000
--- a/car-support-lib/src/android/support/car/app/menu/compat/CarMenuConstantsComapt.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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 android.support.car.app.menu.compat;
-
-import android.support.annotation.IntDef;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Contains keys to the metadata of car menu, such as id, title, icon, etc.
- * @hide
- */
-public class CarMenuConstantsComapt {
- public static class MenuItemConstants {
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {FLAG_BROWSABLE, FLAG_FIRSTITEM})
- public @interface MenuItemFlags {}
-
- /**
- * Flag: Indicates that the item has children of its own
- */
- public static final int FLAG_BROWSABLE = 0x1;
-
- /**
- * Flag: Indicates that the menu should scroll to this item
- */
- public static final int FLAG_FIRSTITEM = 0x2;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {WIDGET_CHECKBOX, WIDGET_TEXT_VIEW})
- public @interface WidgetTypes {}
-
- /**
- * Use a checkbox widget.
- */
- public static final int WIDGET_CHECKBOX = 0x1;
-
- /**
- * Use a TextView widget
- */
- public static final int WIDGET_TEXT_VIEW = 0x2;
-
- /**
- * Key for the car menu title.
- */
- public static final String KEY_TITLE = "android.car.app.menu.title";
-
- /**
- * Key for the item title.
- */
- public static final String KEY_TEXT = "android.car.app.menu.text";
-
- /**
- * Key for the left icon.
- */
- public static final String KEY_LEFTICON = "android.car.app.menu.leftIcon";
-
- /**
- * Key for the right icon.
- */
- public static final String KEY_RIGHTICON = "android.car.app.menu.rightIcon";
-
- /**
- * Key for the text to be shown to the right of the item.
- */
- public static final String KEY_RIGHTTEXT = "android.car.app.menu.rightText";
-
- /**
- * Key for the widget type.
- */
- public static final String KEY_WIDGET = "android.car.app.menu.widget";
-
- /**
- * Key for the widget state.
- */
- public static final String KEY_WIDGET_STATE = "android.car.app.menu.widget_state";
-
- /**
- * Key for the value of whether the item is a place holder.
- */
- public static final String KEY_EMPTY_PLACEHOLDER = "android.car.app.menu.empty_placeholder";
-
- /**
- * Key for the flags.
- */
- public static final String KEY_FLAGS = "android.car.app.menu.flags";
-
- /**
- * Key for the menu item id.
- */
- public static final String KEY_ID = "android.car.app.menu.id";
-
- /**
- * Key for the remote views.
- */
- public static final String KEY_REMOTEVIEWS = "android.car.app.menu.remoteViews";
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedCarMenuCallbacksCompat.java b/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedCarMenuCallbacksCompat.java
deleted file mode 100644
index cad2848..0000000
--- a/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedCarMenuCallbacksCompat.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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 android.support.car.app.menu.compat;
-
-import android.car.app.menu.RootMenu;
-import android.car.app.menu.SubscriptionCallbacks;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.car.app.menu.CarDrawerActivity;
-import android.support.car.app.menu.CarMenu;
-import android.support.car.app.menu.CarMenuCallbacks;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @hide
- */
-public class EmbeddedCarMenuCallbacksCompat extends android.car.app.menu.CarMenuCallbacks {
-
- private final CarMenuCallbacks mCallbacks;
- private final CarDrawerActivity mActivity;
-
- // Map of subscribed ids to their respective callbacks.
- // @GuardedBy("this")
- private final Map<String, List<SubscriptionCallbacks>> mSubscriptionMap =
- new HashMap<String, List<SubscriptionCallbacks>>();
-
- private final Handler mHandler = new Handler();
-
- public EmbeddedCarMenuCallbacksCompat(CarDrawerActivity activity,
- CarMenuCallbacks callbacks) {
- mActivity = activity;
- mCallbacks = callbacks;
- }
-
- @Override
- public RootMenu getRootMenu(Bundle hint) {
- android.support.car.app.menu.RootMenu rootMenu = mCallbacks.onGetRoot(hint);
- return new RootMenu(rootMenu.getId(), rootMenu.getBundle());
- }
-
- @Override
- public void subscribe(String parentId, SubscriptionCallbacks callbacks) {
- synchronized (this) {
- if (!mSubscriptionMap.containsKey(parentId)) {
- mSubscriptionMap.put(parentId, new ArrayList<SubscriptionCallbacks>());
- }
- mSubscriptionMap.get(parentId).add(callbacks);
- loadResultsForClient(parentId, callbacks);
- }
- }
-
- @Override
- public void unsubscribe(String parentId, SubscriptionCallbacks callbacks) {
- synchronized (this) {
- mSubscriptionMap.get(parentId).remove(callbacks);
- }
- }
-
- @Override
- public void onCarMenuOpened() {
- mActivity.setDrawerShowing(true);
- mCallbacks.onCarMenuOpened();
- }
-
- @Override
- public void onCarMenuClosed() {
- mActivity.setDrawerShowing(false);
- mActivity.restoreSearchBox();
- }
-
- @Override
- public void onItemClicked(String id) {
- mCallbacks.onItemClicked(id);
- mActivity.stopInput();
- }
-
- @Override
- public boolean onItemLongClicked(String id) {
- return mCallbacks.onItemLongClicked(id);
- }
-
- @Override
- public boolean onMenuClicked() {
-
- return mActivity.onMenuClicked();
- }
-
- @Override
- public void onCarMenuOpening() {
- mActivity.stopInput();
- }
-
- @Override
- public void onCarMenuClosing() {
- mActivity.restoreSearchBox();
- }
-
- public void onChildrenChanged(final String parentId) {
- synchronized (this) {
- if (mSubscriptionMap.containsKey(parentId)) {
- final List<SubscriptionCallbacks> callbacks = new ArrayList<>();
- callbacks.addAll(mSubscriptionMap.get(parentId));
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- loadResultsForAllClients(parentId, callbacks);
- }
- });
- }
- }
- }
-
- public void onChildChanged(final String parentId, final Bundle item) {
- synchronized (this) {
- if (mSubscriptionMap.containsKey(parentId)) {
- final List<SubscriptionCallbacks> callbacks = new ArrayList<>();
- callbacks.addAll(mSubscriptionMap.get(parentId));
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (SubscriptionCallbacks callback : callbacks) {
- callback.onChildChanged(parentId, item);
- }
- }
- });
-
- }
- }
- }
-
- private void loadResultsForAllClients(final String parentId,
- @NonNull final List<SubscriptionCallbacks> callbacks) {
- final CarMenu result = new CarMenu(mActivity.getResources().getDisplayMetrics()) {
- @Override
- protected void onResultReady(List<Bundle> list) {
- for (SubscriptionCallbacks callback : callbacks) {
- callback.onChildrenLoaded(parentId, list);
- }
- }
- };
-
- mCallbacks.onLoadChildren(parentId, result);
- if (!result.isDone()) {
- throw new IllegalStateException("You must either call sendResult() or detach() " +
- "before returning!");
- }
- }
-
- private void loadResultsForClient(final String parentId,
- final SubscriptionCallbacks callbacks) {
- final CarMenu result = new CarMenu(mActivity.getResources().getDisplayMetrics()) {
- @Override
- protected void onResultReady(List<Bundle> list) {
- callbacks.onChildrenLoaded(parentId, list);
- }
- };
-
- mCallbacks.onLoadChildren(parentId, result);
- if (!result.isDone()) {
- throw new IllegalStateException("You must either call sendResult() or detach() " +
- "before returning!");
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedSearchBoxEditListenerCompat.java b/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedSearchBoxEditListenerCompat.java
deleted file mode 100644
index 20b9616..0000000
--- a/car-support-lib/src/android/support/car/app/menu/compat/EmbeddedSearchBoxEditListenerCompat.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 android.support.car.app.menu.compat;
-
-import android.support.car.app.menu.SearchBoxEditListener;
-
-/**
- * @hide
- */
-public class EmbeddedSearchBoxEditListenerCompat extends
- android.car.app.menu.SearchBoxEditListener {
- private final SearchBoxEditListener mListener;
- public EmbeddedSearchBoxEditListenerCompat(SearchBoxEditListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onSearch(String text) {
- mListener.onSearch(text);
- }
-
- @Override
- public void onEdit(String text) {
- mListener.onEdit(text);
- }
-}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java b/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
index 633768a..a0eb482 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
@@ -127,17 +127,17 @@
synchronized (CarSensorsProxy.this) {
switch (type) {
case Sensor.TYPE_GYROSCOPE:
- System.arraycopy(event.values, 0, mLastGyroscopeData, 0, 3);
+ System.arraycopy((Object) event.values, 0, (Object) mLastGyroscopeData, 0, 3);
mLastGyroscopeDataTime = System.nanoTime();
pushSensorChanges(CarSensorManager.SENSOR_TYPE_GYROSCOPE);
break;
case Sensor.TYPE_MAGNETIC_FIELD:
- System.arraycopy(event.values, 0, mLastMagneticFieldData, 0, 3);
+ System.arraycopy((Object) event.values, 0, (Object) mLastMagneticFieldData, 0, 3);
mLastMagneticFieldDataTime = System.nanoTime();
pushSensorChanges(CarSensorManager.SENSOR_TYPE_COMPASS);
break;
case Sensor.TYPE_ACCELEROMETER:
- System.arraycopy(event.values, 0, mLastAccelerometerData, 0, 3);
+ System.arraycopy((Object) event.values, 0, (Object) mLastAccelerometerData, 0, 3);
mLastAccelerometerDataTime = System.nanoTime();
pushSensorChanges(CarSensorManager.SENSOR_TYPE_ACCELEROMETER);
pushSensorChanges(CarSensorManager.SENSOR_TYPE_COMPASS);
diff --git a/car-support-lib/src/android/support/car/ui/AnimationListenerAdapter.java b/car-support-lib/src/android/support/car/ui/AnimationListenerAdapter.java
deleted file mode 100644
index 33fee8b..0000000
--- a/car-support-lib/src/android/support/car/ui/AnimationListenerAdapter.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.view.animation.Animation;
-
-/**
- * Provides empty implementations of the methods in
- * {@link android.view.animation.Animation.AnimationListener} for convenience reasons.
- * @hide
- */
-public class AnimationListenerAdapter implements Animation.AnimationListener {
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAnimationEnd(Animation animation) {
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/CarActionExtender.java b/car-support-lib/src/android/support/car/ui/CarActionExtender.java
deleted file mode 100644
index 898a0bf..0000000
--- a/car-support-lib/src/android/support/car/ui/CarActionExtender.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.app.NotificationCompat;
-
-/**
- * Android Auto {@link android.app.Notification.Action} extender.
- * NOTE: this will move into the platform and support-lib when the API stabilizes.
- * @hide
- */
-public class CarActionExtender implements NotificationCompat.Action.Extender {
- private static final String EXTRA_AUTO_EXTENDER = "android.auto.EXTENSIONS";
- private static final String EXTRA_INTENT = "intent";
-
- private Intent mIntent;
-
- public CarActionExtender() {
- }
-
- public CarActionExtender(NotificationCompat.Action action) {
- Bundle autoBundle = action.getExtras();
-
- if (autoBundle != null) {
- mIntent = autoBundle.getParcelable(EXTRA_INTENT);
- }
- }
-
- @Override
- public NotificationCompat.Action.Builder extend(NotificationCompat.Action.Builder builder) {
- Bundle autoBundle = new Bundle();
-
- autoBundle.putParcelable(EXTRA_INTENT, mIntent);
-
- builder.getExtras().putBundle(EXTRA_AUTO_EXTENDER, autoBundle);
- return builder;
- }
-
- public void setIntent(Intent intent) {
- mIntent = intent;
- }
-
- public Intent getIntent() {
- return mIntent;
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/CarItemAnimator.java b/car-support-lib/src/android/support/car/ui/CarItemAnimator.java
deleted file mode 100644
index 9e819c4..0000000
--- a/car-support-lib/src/android/support/car/ui/CarItemAnimator.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.support.v7.widget.DefaultItemAnimator;
-import android.support.v7.widget.RecyclerView;
-
-/**
- * {@link DefaultItemAnimator} with a few minor changes where it had undesired behavior.
- * @hide
- */
-public class CarItemAnimator extends DefaultItemAnimator {
-
- private final CarLayoutManager mLayoutManager;
-
- public CarItemAnimator(CarLayoutManager layoutManager) {
- mLayoutManager = layoutManager;
- }
-
- @Override
- public boolean animateChange(RecyclerView.ViewHolder oldHolder,
- RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
- // The default behavior will cross fade the old view and the new one. However, if we
- // have a card on a colored background, it will make it appear as if a changing card
- // fades in and out.
- float alpha = 0f;
- if (newHolder != null) {
- alpha = newHolder.itemView.getAlpha();
- }
- boolean ret = super.animateChange(oldHolder, newHolder, fromX, fromY, toX, toY);
- if (newHolder != null) {
- newHolder.itemView.setAlpha(alpha);
- }
- return ret;
- }
-
- @Override
- public void onMoveFinished(RecyclerView.ViewHolder item) {
- // The item animator uses translation heavily internally. However, we also use translation
- // to create the paging affect. When an item's move is animated, it will mess up the
- // translation we have set on it so we must re-offset the rows once the animations finish.
-
- // isRunning(ItemAnimationFinishedListener) is the awkward API used to determine when all
- // animations have finished.
- isRunning(mFinishedListener);
- }
-
- private final ItemAnimatorFinishedListener mFinishedListener =
- new ItemAnimatorFinishedListener() {
- @Override
- public void onAnimationsFinished() {
- mLayoutManager.offsetRows();
- }
- };
-}
diff --git a/car-support-lib/src/android/support/car/ui/CarLayoutManager.java b/car-support-lib/src/android/support/car/ui/CarLayoutManager.java
deleted file mode 100644
index 3195afe..0000000
--- a/car-support-lib/src/android/support/car/ui/CarLayoutManager.java
+++ /dev/null
@@ -1,1528 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.LinearSmoothScroller;
-import android.support.v7.widget.RecyclerView;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * Custom {@link RecyclerView.LayoutManager} that behaves similar to LinearLayoutManager except that
- * it has a few tricks up its sleeve.
- * <ol>
- * <li>In a normal ListView, when views reach the top of the list, they are clipped. In
- * CarLayoutManager, views have the option of flying off of the top of the screen as the
- * next row settles in to place. This functionality can be enabled or disabled with
- * {@link #setOffsetRows(boolean)}.
- * <li>Standard list physics is disabled. Instead, when the user scrolls, it will settle
- * on the next page. {@link #FLING_THRESHOLD_TO_PAGINATE} and
- * {@link #DRAG_DISTANCE_TO_PAGINATE} can be set to have the list settle on the next item
- * instead of the next page for small gestures.
- * <li>Items can scroll past the bottom edge of the screen. This helps with pagination so that
- * the last page can be properly aligned.
- * </ol>
- *
- * This LayoutManger should be used with {@link CarRecyclerView}.
- * @hide
- */
-public class CarLayoutManager extends RecyclerView.LayoutManager {
- private static final String TAG = "CarLayoutManager";
- private static final boolean DEBUG = false;
-
- /**
- * Any fling below the threshold will just scroll to the top fully visible row. The units is
- * whatever {@link android.widget.Scroller} would return.
- *
- * A reasonable value is ~200
- *
- * This can be disabled by setting the threshold to -1.
- */
- private static final int FLING_THRESHOLD_TO_PAGINATE = -1;
-
- /**
- * Any fling shorter than this threshold (in px) will just scroll to the top fully visible row.
- *
- * A reasonable value is 15.
- *
- * This can be disabled by setting the distance to -1.
- */
- private static final int DRAG_DISTANCE_TO_PAGINATE = -1;
-
- /**
- * If you scroll really quickly, you can hit the end of the laid out rows before Android has a
- * chance to layout more. To help counter this, we can layout a number of extra rows past
- * wherever the focus is if necessary.
- */
- private static final int NUM_EXTRA_ROWS_TO_LAYOUT_PAST_FOCUS = 2;
-
- /**
- * Scroll bar calculation is a bit complicated. This basically defines the granularity we want
- * our scroll bar to move. Set this to 1 means our scrollbar will have really jerky movement.
- * Setting it too big will risk an overflow (although there is no performance impact). Ideally
- * we want to set this higher than the height of our list view. We can't use our list view
- * height directly though because we might run into situations where getHeight() returns 0, for
- * example, when the view is not yet measured.
- */
- private static final int SCROLL_RANGE = 1000;
-
- @ScrollStyle private final int SCROLL_TYPE = MARIO;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({MARIO, SUPER_MARIO})
- private @interface ScrollStyle {}
- private static final int MARIO = 0;
- private static final int SUPER_MARIO = 1;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({BEFORE, AFTER})
- private @interface LayoutDirection {}
- private static final int BEFORE = 0;
- private static final int AFTER = 1;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ROW_OFFSET_MODE_INDIVIDUAL, ROW_OFFSET_MODE_PAGE})
- public @interface RowOffsetMode {}
- public static final int ROW_OFFSET_MODE_INDIVIDUAL = 0;
- public static final int ROW_OFFSET_MODE_PAGE = 1;
-
- public interface OnItemsChangedListener {
- void onItemsChanged();
- }
-
- private final AccelerateInterpolator mDanglingRowInterpolator = new AccelerateInterpolator(2);
- private final Context mContext;
-
- /** Determines whether or not rows will be offset as they slide off screen **/
- private boolean mOffsetRows = false;
- /** Determines whether rows will be offset individually or a page at a time **/
- @RowOffsetMode private int mRowOffsetMode = ROW_OFFSET_MODE_PAGE;
-
- /**
- * The LayoutManager only gets {@link #onScrollStateChanged(int)} updates. This enables the
- * scroll state to be used anywhere.
- */
- private int mScrollState = RecyclerView.SCROLL_STATE_IDLE;
- /**
- * Used to inspect the current scroll state to help with the various calculations.
- **/
- private CarSmoothScroller mSmoothScroller;
- private OnItemsChangedListener mItemsChangedListener;
-
- /** The distance that the list has actually scrolled in the most recent drag gesture **/
- private int mLastDragDistance = 0;
- /** True if the current drag was limited/capped because it was at some boundary **/
- private boolean mReachedLimitOfDrag;
- /**
- * The values are continuously updated to keep track of where the current page boundaries are
- * on screen. The anchor page break is the page break that is currently within or at the
- * top of the viewport. The Upper page break is the page break before it and the lower page
- * break is the page break after it.
- *
- * A page break will be set to -1 if it is unknown or n/a.
- * @see #updatePageBreakPositions()
- */
- private int mItemCountDuringLastPageBreakUpdate;
- // The index of the first item on the current page
- private int mAnchorPageBreakPosition = 0;
- // The index of the first item on the previous page
- private int mUpperPageBreakPosition = -1;
- // The index of the first item on the next page
- private int mLowerPageBreakPosition = -1;
- /** Used in the bookkeeping of mario style scrolling to prevent extra calculations. **/
- private int mLastChildPositionToRequestFocus = -1;
- private int mSampleViewHeight = -1;
-
- /**
- * Set the anchor to the following position on the next layout pass.
- */
- private int mPendingScrollPosition = -1;
-
- public CarLayoutManager(Context context) {
- mContext = context;
- }
-
- @Override
- public RecyclerView.LayoutParams generateDefaultLayoutParams() {
- return new RecyclerView.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- }
-
- @Override
- public boolean canScrollVertically() {
- return true;
- }
-
- /**
- * onLayoutChildren is sort of like a "reset" for the layout state. At a high level, it should:
- * <ol>
- * <li>Check the current views to get the current state of affairs
- * <li>Detach all views from the window (a lightweight operation) so that rows
- * not re-added will be removed after onLayoutChildren.
- * <li>Re-add rows as necessary.
- * </ol>
- *
- * @see super#onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)
- */
- @Override
- public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
- /**
- * The anchor view is the first fully visible view on screen at the beginning
- * of onLayoutChildren (or 0 if there is none). This row will be laid out first. After that,
- * layoutNextRow will layout rows above and below it until the boundaries of what should
- * be laid out have been reached. See {@link #shouldLayoutNextRow(View, int)} for
- * more information.
- */
- int anchorPosition = 0;
- int anchorTop = -1;
- if (mPendingScrollPosition == -1) {
- View anchor = getFirstFullyVisibleChild();
- if (anchor != null) {
- anchorPosition = getPosition(anchor);
- anchorTop = getDecoratedTop(anchor);
- }
- } else {
- anchorPosition = mPendingScrollPosition;
- mPendingScrollPosition = -1;
- mAnchorPageBreakPosition = anchorPosition;
- mUpperPageBreakPosition = -1;
- mLowerPageBreakPosition = -1;
- }
-
- if (DEBUG) {
- Log.v(TAG, String.format(
- ":: onLayoutChildren anchorPosition:%s, anchorTop:%s,"
- + " mPendingScrollPosition: %s, mAnchorPageBreakPosition:%s,"
- + " mUpperPageBreakPosition:%s, mLowerPageBreakPosition:%s",
- anchorPosition, anchorTop, mPendingScrollPosition, mAnchorPageBreakPosition,
- mUpperPageBreakPosition, mLowerPageBreakPosition));
- }
-
- /**
- * Detach all attached view for 2 reasons:
- * <ol>
- * <li> So that views are put in the scrap heap. This enables us to call
- * {@link RecyclerView.Recycler#getViewForPosition(int)} which will either return
- * one of these detached views if it is in the scrap heap, one from the
- * recycled pool (will only call onBind in the adapter), or create an entirely new
- * row if needed (will call onCreate and onBind in the adapter).
- * <li> So that views are automatically removed if they are not manually re-added.
- * </ol>
- */
- detachAndScrapAttachedViews(recycler);
-
- // Layout new rows.
- View anchor = layoutAnchor(recycler, anchorPosition, anchorTop);
- if (anchor != null) {
- View adjacentRow = anchor;
- while (shouldLayoutNextRow(state, adjacentRow, BEFORE)) {
- adjacentRow = layoutNextRow(recycler, adjacentRow, BEFORE);
- }
- adjacentRow = anchor;
- while (shouldLayoutNextRow(state, adjacentRow, AFTER)) {
- adjacentRow = layoutNextRow(recycler, adjacentRow, AFTER);
- }
- }
-
- updatePageBreakPositions();
- offsetRows();
-
- if (DEBUG&& getChildCount() > 1) {
- Log.v(TAG, "Currently showing " + getChildCount() + " views " +
- getPosition(getChildAt(0)) + " to " +
- getPosition(getChildAt(getChildCount() - 1)) + " anchor " + anchorPosition);
- }
- }
-
- /**
- * scrollVerticallyBy does the work of what should happen when the list scrolls in addition
- * to handling cases where the list hits the end. It should be lighter weight than
- * onLayoutChildren. It doesn't have to detach all views. It only looks at the end of the list
- * and removes views that have gone out of bounds and lays out new ones that scroll in.
- *
- * @param dy The amount that the list is supposed to scroll.
- * > 0 means the list is scrolling down.
- * < 0 means the list is scrolling up.
- * @param recycler The recycler that enables views to be reused or created as they scroll in.
- * @param state Various information about the current state of affairs.
- * @return The amount the list actually scrolled.
- *
- * @see super#scrollVerticallyBy(int, RecyclerView.Recycler, RecyclerView.State)
- */
- @Override
- public int scrollVerticallyBy(
- int dy, @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) {
- // If the list is empty, we can prevent the overscroll glow from showing by just
- // telling RecycerView that we scrolled.
- if (getItemCount() == 0) {
- return dy;
- }
-
- // Prevent redundant computations if there is definitely nowhere to scroll to.
- if (getChildCount() <= 1 || dy == 0) {
- return 0;
- }
-
- View firstChild = getChildAt(0);
- if (firstChild == null) {
- return 0;
- }
- int firstChildPosition = getPosition(firstChild);
- RecyclerView.LayoutParams firstChildParams = getParams(firstChild);
- int firstChildTopWithMargin = getDecoratedTop(firstChild) - firstChildParams.topMargin;
-
- View lastFullyVisibleView = getChildAt(getLastFullyVisibleChildIndex());
- if (lastFullyVisibleView == null) {
- return 0;
- }
- boolean isLastViewVisible = getPosition(lastFullyVisibleView) == getItemCount() - 1;
-
- View firstFullyVisibleChild = getFirstFullyVisibleChild();
- if (firstFullyVisibleChild == null) {
- return 0;
- }
- int firstFullyVisiblePosition = getPosition(firstFullyVisibleChild);
- RecyclerView.LayoutParams firstFullyVisibleChildParams = getParams(firstFullyVisibleChild);
- int topRemainingSpace = getDecoratedTop(firstFullyVisibleChild)
- - firstFullyVisibleChildParams.topMargin - getPaddingTop();
-
- if (isLastViewVisible && firstFullyVisiblePosition == mAnchorPageBreakPosition
- && dy > topRemainingSpace && dy > 0) {
- // Prevent dragging down more than 1 page. As a side effect, this also prevents you
- // from dragging past the bottom because if you are on the second to last page, it
- // prevents you from dragging past the last page.
- dy = topRemainingSpace;
- mReachedLimitOfDrag = true;
- } else if (dy < 0 && firstChildPosition == 0
- && firstChildTopWithMargin + Math.abs(dy) > getPaddingTop()) {
- // Prevent scrolling past the beginning
- dy = firstChildTopWithMargin - getPaddingTop();
- mReachedLimitOfDrag = true;
- } else {
- mReachedLimitOfDrag = false;
- }
-
- boolean isDragging = mScrollState == RecyclerView.SCROLL_STATE_DRAGGING;
- if (isDragging) {
- mLastDragDistance += dy;
- }
- // We offset by -dy because the views translate in the opposite direction that the
- // list scrolls (think about it.)
- offsetChildrenVertical(-dy);
-
- // The last item in the layout should never scroll above the viewport
- View view = getChildAt(getChildCount() - 1);
- if (view.getTop() < 0) {
- view.setTop(0);
- }
-
- // This is the meat of this function. We remove views on the trailing edge of the scroll
- // and add views at the leading edge as necessary.
- View adjacentRow;
- if (dy > 0) {
- recycleChildrenFromStart(recycler);
- adjacentRow = getChildAt(getChildCount() - 1);
- while (shouldLayoutNextRow(state, adjacentRow, AFTER)) {
- adjacentRow = layoutNextRow(recycler, adjacentRow, AFTER);
- }
- } else {
- recycleChildrenFromEnd(recycler);
- adjacentRow = getChildAt(0);
- while (shouldLayoutNextRow(state, adjacentRow, BEFORE)) {
- adjacentRow = layoutNextRow(recycler, adjacentRow, BEFORE);
- }
- }
- // Now that the correct views are laid out, offset rows as necessary so we can do whatever
- // fancy animation we want such as having the top view fly off the screen as the next one
- // settles in to place.
- updatePageBreakPositions();
- offsetRows();
-
- if (getChildCount() > 1) {
- if (DEBUG) {
- Log.v(TAG, String.format("Currently showing %d views (%d to %d)",
- getChildCount(), getPosition(getChildAt(0)),
- getPosition(getChildAt(getChildCount() - 1))));
- }
- }
-
- return dy;
- }
-
- @Override
- public void scrollToPosition(int position) {
- mPendingScrollPosition = position;
- requestLayout();
- }
-
- @Override
- public void smoothScrollToPosition(
- RecyclerView recyclerView, RecyclerView.State state, int position) {
- /**
- * startSmoothScroll will handle stopping the old one if there is one.
- * We only keep a copy of it to handle the translation of rows as they slide off the screen
- * in {@link #offsetRowsWithPageBreak()}
- */
- mSmoothScroller = new CarSmoothScroller(mContext, position);
- mSmoothScroller.setTargetPosition(position);
- startSmoothScroll(mSmoothScroller);
- }
-
- /**
- * Miscellaneous bookkeeping.
- */
- @Override
- public void onScrollStateChanged(int state) {
- if (DEBUG) {
- Log.v(TAG, ":: onScrollStateChanged " + state);
- }
- if (state == RecyclerView.SCROLL_STATE_IDLE) {
- // If the focused view is off screen, give focus to one that is.
- // If the first fully visible view is first in the list, focus the first item.
- // Otherwise, focus the second so that you have the first item as scrolling context.
- View focusedChild = getFocusedChild();
- if (focusedChild != null
- && (getDecoratedTop(focusedChild) >= getHeight() - getPaddingBottom()
- || getDecoratedBottom(focusedChild) <= getPaddingTop())) {
- focusedChild.clearFocus();
- requestLayout();
- }
-
- } else if (state == RecyclerView.SCROLL_STATE_DRAGGING) {
- mLastDragDistance = 0;
- }
-
- if (state != RecyclerView.SCROLL_STATE_SETTLING) {
- mSmoothScroller = null;
- }
-
- mScrollState = state;
- updatePageBreakPositions();
- }
-
- @Override
- public void onItemsChanged(RecyclerView recyclerView) {
- super.onItemsChanged(recyclerView);
- if (mItemsChangedListener != null) {
- mItemsChangedListener.onItemsChanged();
- }
- // When item changed, our sample view height is no longer accurate, and need to be
- // recomputed.
- mSampleViewHeight = -1;
- }
-
- /**
- * Gives us the opportunity to override the order of the focused views.
- * By default, it will just go from top to bottom. However, if there is no focused views, we
- * take over the logic and start the focused views from the middle of what is visible and move
- * from there until the end of the laid out views in the specified direction.
- */
- @Override
- public boolean onAddFocusables(
- RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode) {
- View focusedChild = getFocusedChild();
- if (focusedChild != null) {
- // If there is a view that already has focus, we can just return false and the normal
- // Android addFocusables will work fine.
- return false;
- }
-
- // Now we know that there isn't a focused view. We need to set up focusables such that
- // instead of just focusing the first item that has been laid out, it focuses starting
- // from a visible item.
-
- int firstFullyVisibleChildIndex = getFirstFullyVisibleChildIndex();
- if (firstFullyVisibleChildIndex == -1) {
- // Somehow there is a focused view but there is no fully visible view. There shouldn't
- // be a way for this to happen but we'd better stop here and return instead of
- // continuing on with -1.
- Log.w(TAG, "There is a focused child but no first fully visible child.");
- return false;
- }
- View firstFullyVisibleChild = getChildAt(firstFullyVisibleChildIndex);
- int firstFullyVisibleChildPosition = getPosition(firstFullyVisibleChild);
-
- int firstFocusableChildIndex = firstFullyVisibleChildIndex;
- if (firstFullyVisibleChildPosition > 0 && firstFocusableChildIndex + 1 < getItemCount()) {
- // We are somewhere in the middle of the list. Instead of starting focus on the first
- // item, start focus on the second item to give some context that we aren't at
- // the beginning.
- firstFocusableChildIndex++;
- }
-
- if (direction == View.FOCUS_FORWARD) {
- // Iterate from the first focusable view to the end.
- for (int i = firstFocusableChildIndex; i < getChildCount(); i++) {
- views.add(getChildAt(i));
- }
- return true;
- } else if (direction == View.FOCUS_BACKWARD) {
- // Iterate from the first focusable view to the beginning.
- for (int i = firstFocusableChildIndex; i >= 0; i--) {
- views.add(getChildAt(i));
- }
- return true;
- }
- return false;
- }
-
- @Override
- public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
- RecyclerView.State state) {
- return null;
- }
-
- /**
- * This is the function that decides where to scroll to when a new view is focused.
- * You can get the position of the currently focused child through the child parameter.
- * Once you have that, determine where to smooth scroll to and scroll there.
- *
- * @param parent The RecyclerView hosting this LayoutManager
- * @param state Current state of RecyclerView
- * @param child Direct child of the RecyclerView containing the newly focused view
- * @param focused The newly focused view. This may be the same view as child or it may be null
- * @return true if the default scroll behavior should be suppressed
- */
- @Override
- public boolean onRequestChildFocus(RecyclerView parent, RecyclerView.State state,
- View child, View focused) {
- if (child == null) {
- Log.w(TAG, "onRequestChildFocus with a null child!");
- return true;
- }
-
- if (DEBUG) {
- Log.v(TAG, String.format(":: onRequestChildFocus child: %s, focused: %s", child,
- focused));
- }
-
- // We have several distinct scrolling methods. Each implementation has been delegated
- // to its own method.
- if (SCROLL_TYPE == MARIO) {
- return onRequestChildFocusMarioStyle(parent, child);
- } else if (SCROLL_TYPE == SUPER_MARIO) {
- return onRequestChildFocusSuperMarioStyle(parent, state, child);
- } else {
- throw new IllegalStateException("Unknown scroll type (" + SCROLL_TYPE + ")");
- }
- }
-
- /**
- * Goal: the scrollbar maintains the same size throughout scrolling and that the scrollbar
- * reaches the bottom of the screen when the last item is fully visible. This is because
- * there are multiple points that could be considered the bottom since the last item can scroll
- * past the bottom edge of the screen.
- *
- * To find the extent, we divide the number of items that can fit on screen by the number of
- * items in total.
- */
- @Override
- public int computeVerticalScrollExtent(RecyclerView.State state) {
- if (getChildCount() <= 1) {
- return 0;
- }
-
- int sampleViewHeight = getSampleViewHeight();
- int availableHeight = getAvailableHeight();
- int sampleViewsThatCanFitOnScreen = availableHeight / sampleViewHeight;
-
- if (state.getItemCount() <= sampleViewsThatCanFitOnScreen) {
- return SCROLL_RANGE;
- } else {
- return SCROLL_RANGE * sampleViewsThatCanFitOnScreen / state.getItemCount();
- }
- }
-
- /**
- * The scrolling offset is calculated by determining what position is at the top of the list.
- * However, instead of using fixed integer positions for each row, the scroll position is
- * factored in and the position is recalculated as a float that takes in to account the
- * current scroll state. This results in a smooth animation for the scrollbar when the user
- * scrolls the list.
- */
- @Override
- public int computeVerticalScrollOffset(RecyclerView.State state) {
- View firstChild = getFirstFullyVisibleChild();
- if (firstChild == null) {
- return 0;
- }
-
- RecyclerView.LayoutParams params = getParams(firstChild);
- int firstChildPosition = getPosition(firstChild);
-
- // Assume the previous view is the same height as the current one.
- float percentOfPreviousViewShowing = (getDecoratedTop(firstChild) - params.topMargin)
- / (float) (getDecoratedMeasuredHeight(firstChild)
- + params.topMargin + params.bottomMargin);
- // If the previous view is actually larger than the current one then this the percent
- // can be greater than 1.
- percentOfPreviousViewShowing = Math.min(percentOfPreviousViewShowing, 1);
-
- float currentPosition = (float) firstChildPosition - percentOfPreviousViewShowing;
-
- int sampleViewHeight = getSampleViewHeight();
- int availableHeight = getAvailableHeight();
- int numberOfSampleViewsThatCanFitOnScreen = availableHeight / sampleViewHeight;
- int positionWhenLastItemIsVisible =
- state.getItemCount() - numberOfSampleViewsThatCanFitOnScreen;
-
- if (positionWhenLastItemIsVisible <= 0) {
- return 0;
- }
-
- if (currentPosition >= positionWhenLastItemIsVisible) {
- return SCROLL_RANGE;
- }
-
- return (int) (SCROLL_RANGE * currentPosition / positionWhenLastItemIsVisible);
- }
-
- /**
- * The range of the scrollbar can be understood as the granularity of how we want the
- * scrollbar to scroll.
- */
- @Override
- public int computeVerticalScrollRange(RecyclerView.State state) {
- return SCROLL_RANGE;
- }
-
- /**
- * @return The first view that starts on screen. It assumes that it fully fits on the screen
- * though. If the first fully visible child is also taller than the screen then it will
- * still be returned. However, since the LayoutManager snaps to view starts, having
- * a row that tall would lead to a broken experience anyways.
- */
- public int getFirstFullyVisibleChildIndex() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- RecyclerView.LayoutParams params = getParams(child);
- if (getDecoratedTop(child) - params.topMargin >= getPaddingTop()) {
- return i;
- }
- }
- return -1;
- }
-
- public View getFirstFullyVisibleChild() {
- int firstFullyVisibleChildIndex = getFirstFullyVisibleChildIndex();
- View firstChild = null;
- if (firstFullyVisibleChildIndex != -1) {
- firstChild = getChildAt(firstFullyVisibleChildIndex);
- }
- return firstChild;
- }
-
- /**
- * @return The last view that ends on screen. It assumes that the start is also on screen
- * though. If the last fully visible child is also taller than the screen then it will
- * still be returned. However, since the LayoutManager snaps to view starts, having
- * a row that tall would lead to a broken experience anyways.
- */
- public int getLastFullyVisibleChildIndex() {
- for (int i = getChildCount() - 1; i >= 0; i--) {
- View child = getChildAt(i);
- RecyclerView.LayoutParams params = getParams(child);
- int childBottom = getDecoratedBottom(child) + params.bottomMargin;
- int listBottom = getHeight() - getPaddingBottom();
- if (childBottom <= listBottom) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * @return Whether or not the first view is fully visible.
- */
- public boolean isAtTop() {
- // getFirstFullyVisibleChildIndex() can return -1 which indicates that there are no views
- // and also means that the list is at the top.
- return getFirstFullyVisibleChildIndex() <= 0;
- }
-
- /**
- * @return Whether or not the last view is fully visible.
- */
- public boolean isAtBottom() {
- int lastFullyVisibleChildIndex = getLastFullyVisibleChildIndex();
- if (lastFullyVisibleChildIndex == -1) {
- return true;
- }
- View lastFullyVisibleChild = getChildAt(lastFullyVisibleChildIndex);
- return getPosition(lastFullyVisibleChild) == getItemCount() - 1;
- }
-
- public void setOffsetRows(boolean offsetRows) {
- mOffsetRows = offsetRows;
- if (offsetRows) {
- offsetRows();
- } else {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- getChildAt(i).setTranslationY(0);
- }
- }
- }
-
- public void setRowOffsetMode(@RowOffsetMode int mode) {
- if (mode == mRowOffsetMode) {
- return;
- }
- mRowOffsetMode = mode;
- offsetRows();
- }
-
- public void setItemsChangedListener(OnItemsChangedListener listener) {
- mItemsChangedListener = listener;
- }
-
- /**
- * Finish the pagination taking into account where the gesture started (not where we are now).
- *
- * @return Whether the list was scrolled as a result of the fling.
- */
- public boolean settleScrollForFling(RecyclerView parent, int flingVelocity) {
- if (getChildCount() == 0) {
- return false;
- }
-
- if (mReachedLimitOfDrag) {
- return false;
- }
-
- // If the fling was too slow or too short, settle on the first fully visible row instead.
- if (Math.abs(flingVelocity) <= FLING_THRESHOLD_TO_PAGINATE
- || Math.abs(mLastDragDistance) <= DRAG_DISTANCE_TO_PAGINATE) {
- int firstFullyVisibleChildIndex = getFirstFullyVisibleChildIndex();
- if (firstFullyVisibleChildIndex != -1) {
- int scrollPosition = getPosition(getChildAt(firstFullyVisibleChildIndex));
- parent.smoothScrollToPosition(scrollPosition);
- return true;
- }
- return false;
- }
-
- // Finish the pagination taking into account where the gesture
- // started (not where we are now).
- boolean isDownGesture = flingVelocity > 0
- || (flingVelocity == 0 && mLastDragDistance >= 0);
- boolean isUpGesture = flingVelocity < 0
- || (flingVelocity == 0 && mLastDragDistance < 0);
- if (isDownGesture && mLowerPageBreakPosition != -1) {
- // If the last view is fully visible then only settle on the first fully visible view
- // instead of the original page down position. However, don't page down if the last
- // item has come fully into view.
- parent.smoothScrollToPosition(mAnchorPageBreakPosition);
- return true;
- } else if (isUpGesture && mUpperPageBreakPosition != -1) {
- parent.smoothScrollToPosition(mUpperPageBreakPosition);
- return true;
- } else {
- Log.e(TAG, "Error setting scroll for fling! flingVelocity: \t" + flingVelocity +
- "\tlastDragDistance: " + mLastDragDistance + "\tpageUpAtStartOfDrag: " +
- mUpperPageBreakPosition + "\tpageDownAtStartOfDrag: " +
- mLowerPageBreakPosition);
- // As a last resort, at the last smooth scroller target position if there is one.
- if (mSmoothScroller != null) {
- parent.smoothScrollToPosition(mSmoothScroller.getTargetPosition());
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return The position that paging up from the current position would settle at.
- */
- public int getPageUpPosition() {
- return mUpperPageBreakPosition;
- }
-
- /**
- * @return The position that paging down from the current position would settle at.
- */
- public int getPageDownPosition() {
- return mLowerPageBreakPosition;
- }
-
- /**
- * Layout the anchor row. The anchor row is the first fully visible row.
- *
- * @param anchorTop The decorated top of the anchor. If it is not known or should be reset
- * to the top, pass -1.
- */
- private View layoutAnchor(RecyclerView.Recycler recycler, int anchorPosition, int anchorTop) {
- if (anchorPosition > getItemCount() - 1) {
- return null;
- }
- View anchor = recycler.getViewForPosition(anchorPosition);
- RecyclerView.LayoutParams params = getParams(anchor);
- measureChildWithMargins(anchor, 0, 0);
- int left = getPaddingLeft() + params.leftMargin;
- int top = (anchorTop == -1) ? params.topMargin : anchorTop;
- int right = left + getDecoratedMeasuredWidth(anchor);
- int bottom = top + getDecoratedMeasuredHeight(anchor);
- layoutDecorated(anchor, left, top, right, bottom);
- addView(anchor);
- return anchor;
- }
-
- /**
- * Lays out the next row in the specified direction next to the specified adjacent row.
- *
- * @param recycler The recycler from which a new view can be created.
- * @param adjacentRow The View of the adjacent row which will be used to position the new one.
- * @param layoutDirection The side of the adjacent row that the new row will be laid out on.
- *
- * @return The new row that was laid out.
- */
- private View layoutNextRow(RecyclerView.Recycler recycler, View adjacentRow,
- @LayoutDirection int layoutDirection) {
-
- int adjacentRowPosition = getPosition(adjacentRow);
- int newRowPosition = adjacentRowPosition;
- if (layoutDirection == BEFORE) {
- newRowPosition = adjacentRowPosition - 1;
- } else if (layoutDirection == AFTER) {
- newRowPosition = adjacentRowPosition + 1;
- }
-
- // Because we detach all rows in onLayoutChildren, this will often just return a view from
- // the scrap heap.
- View newRow = recycler.getViewForPosition(newRowPosition);
-
- measureChildWithMargins(newRow, 0, 0);
- RecyclerView.LayoutParams newRowParams =
- (RecyclerView.LayoutParams) newRow.getLayoutParams();
- RecyclerView.LayoutParams adjacentRowParams =
- (RecyclerView.LayoutParams) adjacentRow.getLayoutParams();
- int left = getPaddingLeft() + newRowParams.leftMargin;
- int right = left + getDecoratedMeasuredWidth(newRow);
- int top, bottom;
- if (layoutDirection == BEFORE) {
- bottom = adjacentRow.getTop() - adjacentRowParams.topMargin - newRowParams.bottomMargin;
- top = bottom - getDecoratedMeasuredHeight(newRow);
- } else {
- top = getDecoratedBottom(adjacentRow) +
- adjacentRowParams.bottomMargin + newRowParams.topMargin;
- bottom = top + getDecoratedMeasuredHeight(newRow);
- }
- layoutDecorated(newRow, left, top, right, bottom);
-
- if (layoutDirection == BEFORE) {
- addView(newRow, 0);
- } else {
- addView(newRow);
- }
-
- return newRow;
- }
-
- /**
- * @return Whether another row should be laid out in the specified direction.
- */
- private boolean shouldLayoutNextRow(RecyclerView.State state, View adjacentRow,
- @LayoutDirection int layoutDirection) {
- int adjacentRowPosition = getPosition(adjacentRow);
-
- if (layoutDirection == BEFORE) {
- if (adjacentRowPosition == 0) {
- // We already laid out the first row.
- return false;
- }
- } else if (layoutDirection == AFTER) {
- if (adjacentRowPosition >= state.getItemCount() - 1) {
- // We already laid out the last row.
- return false;
- }
- }
-
- // If we are scrolling layout views until the target position.
- if (mSmoothScroller != null) {
- if (layoutDirection == BEFORE
- && adjacentRowPosition >= mSmoothScroller.getTargetPosition()) {
- return true;
- } else if (layoutDirection == AFTER
- && adjacentRowPosition <= mSmoothScroller.getTargetPosition()) {
- return true;
- }
- }
-
- View focusedRow = getFocusedChild();
- if (focusedRow != null) {
- int focusedRowPosition = getPosition(focusedRow);
- if (layoutDirection == BEFORE && adjacentRowPosition
- >= focusedRowPosition - NUM_EXTRA_ROWS_TO_LAYOUT_PAST_FOCUS) {
- return true;
- } else if (layoutDirection == AFTER && adjacentRowPosition
- <= focusedRowPosition + NUM_EXTRA_ROWS_TO_LAYOUT_PAST_FOCUS) {
- return true;
- }
- }
-
- RecyclerView.LayoutParams params = getParams(adjacentRow);
- int adjacentRowTop = getDecoratedTop(adjacentRow) - params.topMargin;
- int adjacentRowBottom = getDecoratedBottom(adjacentRow) - params.bottomMargin;
- if (layoutDirection == BEFORE
- && adjacentRowTop < getPaddingTop() - getHeight()) {
- // View is more than 1 page past the top of the screen and also past where the user has
- // scrolled to. We want to keep one page past the top to make the scroll up calculation
- // easier and scrolling smoother.
- return false;
- } else if (layoutDirection == AFTER
- && adjacentRowBottom > getHeight() - getPaddingBottom()) {
- // View is off of the bottom and also past where the user has scrolled to.
- return false;
- }
-
- return true;
- }
-
- /**
- * Remove and recycle views that are no longer needed.
- */
- private void recycleChildrenFromStart(RecyclerView.Recycler recycler) {
- // Start laying out children one page before the top of the viewport.
- int childrenStart = getPaddingTop() - getHeight();
-
- int focusedChildPosition = Integer.MAX_VALUE;
- View focusedChild = getFocusedChild();
- if (focusedChild != null) {
- focusedChildPosition = getPosition(focusedChild);
- }
-
- // Count the number of views that should be removed.
- int detachedCount = 0;
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- int childEnd = getDecoratedBottom(child);
- int childPosition = getPosition(child);
-
- if (childEnd >= childrenStart || childPosition >= focusedChildPosition - 1) {
- break;
- }
-
- detachedCount++;
- }
-
- // Remove the number of views counted above. Done by removing the first child n times.
- while (--detachedCount >= 0) {
- final View child = getChildAt(0);
- removeAndRecycleView(child, recycler);
- }
- }
-
- /**
- * Remove and recycle views that are no longer needed.
- */
- private void recycleChildrenFromEnd(RecyclerView.Recycler recycler) {
- // Layout views until the end of the viewport.
- int childrenEnd = getHeight();
-
- int focusedChildPosition = Integer.MIN_VALUE + 1;
- View focusedChild = getFocusedChild();
- if (focusedChild != null) {
- focusedChildPosition = getPosition(focusedChild);
- }
-
- // Count the number of views that should be removed.
- int firstDetachedPos = 0;
- int detachedCount = 0;
- int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- final View child = getChildAt(i);
- int childStart = getDecoratedTop(child);
- int childPosition = getPosition(child);
-
- if (childStart <= childrenEnd || childPosition <= focusedChildPosition - 1) {
- break;
- }
-
- firstDetachedPos = i;
- detachedCount++;
- }
-
- while (--detachedCount >= 0) {
- final View child = getChildAt(firstDetachedPos);
- removeAndRecycleView(child, recycler);
- }
- }
-
- /**
- * Offset rows to do fancy animations. If {@link #mOffsetRows} is false, this will do nothing.
- *
- * @see #offsetRowsIndividually()
- * @see #offsetRowsByPage()
- */
- public void offsetRows() {
- if (!mOffsetRows) {
- return;
- }
-
- if (mRowOffsetMode == ROW_OFFSET_MODE_PAGE) {
- offsetRowsByPage();
- } else if (mRowOffsetMode == ROW_OFFSET_MODE_INDIVIDUAL) {
- offsetRowsIndividually();
- }
- }
-
- /**
- * Offset the single row that is scrolling off the screen such that by the time the next row
- * reaches the top, it will have accelerated completely off of the screen.
- */
- private void offsetRowsIndividually() {
- if (getChildCount() == 0) {
- if (DEBUG) {
- Log.d(TAG, ":: offsetRowsIndividually getChildCount=0");
- }
- return;
- }
-
- // Identify the dangling row. It will be the first row that is at the top of the
- // list or above.
- int danglingChildIndex = -1;
- for (int i = getChildCount() - 1; i >= 0; i--) {
- View child = getChildAt(i);
- if (getDecoratedTop(child) - getParams(child).topMargin <= getPaddingTop()) {
- danglingChildIndex = i;
- break;
- }
- }
-
- mAnchorPageBreakPosition = danglingChildIndex;
-
- if (DEBUG) {
- Log.v(TAG, ":: offsetRowsIndividually danglingChildIndex: " + danglingChildIndex);
- }
-
- // Calculate the total amount that the view will need to scroll in order to go completely
- // off screen.
- RecyclerView rv = (RecyclerView) getChildAt(0).getParent();
- int[] locs = new int[2];
- rv.getLocationInWindow(locs);
- int listTopInWindow = locs[1] + rv.getPaddingTop();
- int maxDanglingViewTranslation;
-
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- RecyclerView.LayoutParams params = getParams(child);
-
- maxDanglingViewTranslation = listTopInWindow;
- // If the child has a negative margin, we'll actually need to translate the view a
- // little but further to get it completely off screen.
- if (params.topMargin < 0) {
- maxDanglingViewTranslation -= params.topMargin;
- }
- if (params.bottomMargin < 0) {
- maxDanglingViewTranslation -= params.bottomMargin;
- }
-
- if (i < danglingChildIndex) {
- child.setAlpha(0f);
- } else if (i > danglingChildIndex) {
- child.setAlpha(1f);
- child.setTranslationY(0);
- } else {
- int totalScrollDistance = getDecoratedMeasuredHeight(child) +
- params.topMargin + params.bottomMargin;
-
- int distanceLeftInScroll = getDecoratedBottom(child) +
- params.bottomMargin - getPaddingTop();
- float percentageIntoScroll = 1 - distanceLeftInScroll / (float) totalScrollDistance;
- float interpolatedPercentage =
- mDanglingRowInterpolator.getInterpolation(percentageIntoScroll);
-
- child.setAlpha(1f);
- child.setTranslationY(-(maxDanglingViewTranslation * interpolatedPercentage));
- }
- }
- }
-
- /**
- * When the list scrolls, the entire page of rows will offset in one contiguous block. This
- * significantly reduces the amount of extra motion at the top of the screen.
- */
- private void offsetRowsByPage() {
- View anchorView = findViewByPosition(mAnchorPageBreakPosition);
- if (anchorView == null) {
- if (DEBUG) {
- Log.d(TAG, ":: offsetRowsByPage anchorView null");
- }
- return;
- }
- int anchorViewTop = getDecoratedTop(anchorView) - getParams(anchorView).topMargin;
-
- View upperPageBreakView = findViewByPosition(mUpperPageBreakPosition);
- int upperViewTop = getDecoratedTop(upperPageBreakView)
- - getParams(upperPageBreakView).topMargin;
-
- int scrollDistance = upperViewTop - anchorViewTop;
-
- int distanceLeft = anchorViewTop - getPaddingTop();
- float scrollPercentage = (Math.abs(scrollDistance) - distanceLeft)
- / (float) Math.abs(scrollDistance);
-
- if (DEBUG) {
- Log.d(TAG, String.format(
- ":: offsetRowsByPage scrollDistance:%s, distanceLeft:%s, scrollPercentage:%s",
- scrollDistance, distanceLeft, scrollPercentage));
- }
-
- // Calculate the total amount that the view will need to scroll in order to go completely
- // off screen.
- RecyclerView rv = (RecyclerView) getChildAt(0).getParent();
- int[] locs = new int[2];
- rv.getLocationInWindow(locs);
- int listTopInWindow = locs[1] + rv.getPaddingTop();
-
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- int position = getPosition(child);
- if (position < mUpperPageBreakPosition) {
- child.setAlpha(0f);
- child.setTranslationY(-listTopInWindow);
- } else if (position < mAnchorPageBreakPosition) {
- // If the child has a negative margin, we need to offset the row by a little bit
- // extra so that it moves completely off screen.
- RecyclerView.LayoutParams params = getParams(child);
- int extraTranslation = 0;
- if (params.topMargin < 0) {
- extraTranslation -= params.topMargin;
- }
- if (params.bottomMargin < 0) {
- extraTranslation -= params.bottomMargin;
- }
- int translation = (int) ((listTopInWindow + extraTranslation)
- * mDanglingRowInterpolator.getInterpolation(scrollPercentage));
- child.setAlpha(1f);
- child.setTranslationY(-translation);
- } else {
- child.setAlpha(1f);
- child.setTranslationY(0);
- }
- }
- }
-
- /**
- * Update the page break positions based on the position of the views on screen. This should
- * be called whenever view move or change such as during a scroll or layout.
- */
- private void updatePageBreakPositions() {
- if (getChildCount() == 0) {
- if (DEBUG) {
- Log.d(TAG, ":: updatePageBreakPosition getChildCount: 0");
- }
- return;
- }
-
- if (DEBUG) {
- Log.v(TAG, String.format(":: #BEFORE updatePageBreakPositions " +
- "mAnchorPageBreakPosition:%s, mUpperPageBreakPosition:%s, "
- + "mLowerPageBreakPosition:%s",
- mAnchorPageBreakPosition, mUpperPageBreakPosition, mLowerPageBreakPosition));
- }
-
- // If the item count has changed, our page boundaries may no longer be accurate. This will
- // force the page boundaries to reset around the current view that is closest to the top.
- if (getItemCount() != mItemCountDuringLastPageBreakUpdate) {
- if (DEBUG) {
- Log.d(TAG, "Item count changed. Resetting page break positions.");
- }
- mAnchorPageBreakPosition = getPosition(getFirstFullyVisibleChild());
- }
- mItemCountDuringLastPageBreakUpdate = getItemCount();
-
- if (mAnchorPageBreakPosition == -1) {
- Log.w(TAG, "Unable to update anchor positions. There is no anchor position.");
- return;
- }
-
- View anchorPageBreakView = findViewByPosition(mAnchorPageBreakPosition);
- if (anchorPageBreakView == null) {
- return;
- }
- int topMargin = getParams(anchorPageBreakView).topMargin;
- int anchorTop = getDecoratedTop(anchorPageBreakView) - topMargin;
- View upperPageBreakView = findViewByPosition(mUpperPageBreakPosition);
- int upperPageBreakTop = upperPageBreakView == null ? Integer.MIN_VALUE :
- getDecoratedTop(upperPageBreakView) - getParams(upperPageBreakView).topMargin;
-
- if (DEBUG) {
- Log.v(TAG, String.format(":: #MID updatePageBreakPositions topMargin:%s, anchorTop:%s"
- + " mAnchorPageBreakPosition:%s, mUpperPageBreakPosition:%s, "
- + "mLowerPageBreakPosition:%s", topMargin, anchorTop,
- mAnchorPageBreakPosition, mUpperPageBreakPosition, mLowerPageBreakPosition));
- }
-
- if (anchorTop < getPaddingTop()) {
- // The anchor has moved above the viewport. We are now on the next page. Shift the page
- // break positions and calculate a new lower one.
- mUpperPageBreakPosition = mAnchorPageBreakPosition;
- mAnchorPageBreakPosition = mLowerPageBreakPosition;
- mLowerPageBreakPosition = calculateNextPageBreakPosition(mAnchorPageBreakPosition);
- } else if (mAnchorPageBreakPosition > 0 && upperPageBreakTop >= getPaddingTop()) {
- // The anchor has moved below the viewport. We are now on the previous page. Shift
- // the page break positions and calculate a new upper one.
- mLowerPageBreakPosition = mAnchorPageBreakPosition;
- mAnchorPageBreakPosition = mUpperPageBreakPosition;
- mUpperPageBreakPosition = calculatePreviousPageBreakPosition(mAnchorPageBreakPosition);
- } else {
- mUpperPageBreakPosition = calculatePreviousPageBreakPosition(mAnchorPageBreakPosition);
- mLowerPageBreakPosition = calculateNextPageBreakPosition(mAnchorPageBreakPosition);
- }
-
- if (DEBUG) {
- Log.v(TAG, String.format(":: #AFTER updatePageBreakPositions " +
- "mAnchorPageBreakPosition:%s, mUpperPageBreakPosition:%s, "
- + "mLowerPageBreakPosition:%s",
- mAnchorPageBreakPosition, mUpperPageBreakPosition, mLowerPageBreakPosition));
- }
- }
-
- /**
- * @return The page break position of the page before the anchor page break position. However,
- * if it reaches the end of the laid out children or position 0, it will just return
- * that.
- */
- private int calculatePreviousPageBreakPosition(int position) {
- if (position == -1) {
- return -1;
- }
- View referenceView = findViewByPosition(position);
- int referenceViewTop = getDecoratedTop(referenceView) - getParams(referenceView).topMargin;
-
- int previousPagePosition = position;
- while (previousPagePosition > 0) {
- previousPagePosition--;
- View child = findViewByPosition(previousPagePosition);
- if (child == null) {
- // View has not been laid out yet.
- return previousPagePosition + 1;
- }
-
- int childTop = getDecoratedTop(child) - getParams(child).topMargin;
-
- if (childTop < referenceViewTop - getHeight()) {
- return previousPagePosition + 1;
- }
- }
- // Beginning of the list.
- return 0;
- }
-
- /**
- * @return The page break position of the next page after the anchor page break position.
- * However, if it reaches the end of the laid out children or end of the list, it will
- * just return that.
- */
- private int calculateNextPageBreakPosition(int position) {
- if (position == -1) {
- return -1;
- }
-
- View referenceView = findViewByPosition(position);
- if (referenceView == null) {
- return position;
- }
- int referenceViewTop = getDecoratedTop(referenceView) - getParams(referenceView).topMargin;
-
- int nextPagePosition = position;
-
- // Search for the first child item after the referenceView that didn't fully fit on to the
- // screen. The next page should start from the item before this child, so that users have
- // a visual anchoring point of the page change.
- while (position < getItemCount() - 1) {
- nextPagePosition++;
- View child = findViewByPosition(nextPagePosition);
- if (child == null) {
- // The next view has not been laid out yet.
- return nextPagePosition - 1;
- }
-
- int childBottom = getDecoratedBottom(child) + getParams(child).bottomMargin;
- if (childBottom - referenceViewTop > getHeight() - getPaddingTop()) {
- // If choosing the previous child causes the view to snap back to the referenceView
- // position, then skip that and go directly to the child. This avoids the case
- // where a tall card in the layout causes the view to constantly snap back to
- // the top when scrolled.
- return nextPagePosition - 1 == position ? nextPagePosition : nextPagePosition - 1;
- }
- }
- // End of the list.
- return nextPagePosition;
- }
-
- /**
- * In this style, the focus will scroll down to the middle of the screen and lock there
- * so that moving in either direction will move the entire list by 1.
- */
- private boolean onRequestChildFocusMarioStyle(RecyclerView parent, View child) {
- int focusedPosition = getPosition(child);
- if (focusedPosition == mLastChildPositionToRequestFocus) {
- return true;
- }
- mLastChildPositionToRequestFocus = focusedPosition;
-
- int availableHeight = getAvailableHeight();
- int focusedChildTop = getDecoratedTop(child);
- int focusedChildBottom = getDecoratedBottom(child);
-
- int childIndex = parent.indexOfChild(child);
- // Iterate through children starting at the focused child to find the child above it to
- // smooth scroll to such that the focused child will be as close to the middle of the screen
- // as possible.
- for (int i = childIndex; i >= 0; i--) {
- View childAtI = getChildAt(i);
- if (childAtI == null) {
- Log.e(TAG, "Child is null at index " + i);
- continue;
- }
- // We haven't found a view that is more than half of the recycler view height above it
- // but we've reached the top so we can't go any further.
- if (i == 0) {
- parent.smoothScrollToPosition(getPosition(childAtI));
- break;
- }
-
- // Because we want to scroll to the first view that is less than half of the screen
- // away from the focused view, we "look ahead" one view. When the look ahead view
- // is more than availableHeight / 2 away, the current child at i is the one we want to
- // scroll to. However, sometimes, that view can be null (ie, if the view is in
- // transition). In that case, just skip that view.
-
- View childBefore = getChildAt(i - 1);
- if (childBefore == null) {
- continue;
- }
- int distanceToChildBeforeFromTop = focusedChildTop - getDecoratedTop(childBefore);
- int distanceToChildBeforeFromBottom = focusedChildBottom - getDecoratedTop(childBefore);
-
- if (distanceToChildBeforeFromTop > availableHeight / 2
- || distanceToChildBeforeFromBottom > availableHeight) {
- parent.smoothScrollToPosition(getPosition(childAtI));
- break;
- }
- }
- return true;
- }
-
- /**
- * In this style, you can free scroll in the middle of the list but if you get to the edge,
- * the list will advance to ensure that there is context ahead of the focused item.
- */
- private boolean onRequestChildFocusSuperMarioStyle(RecyclerView parent,
- RecyclerView.State state, View child) {
- int focusedPosition = getPosition(child);
- if (focusedPosition == mLastChildPositionToRequestFocus) {
- return true;
- }
- mLastChildPositionToRequestFocus = focusedPosition;
-
- int bottomEdgeThatMustBeOnScreen;
- int focusedIndex = parent.indexOfChild(child);
- // The amount of the last card at the end that must be showing to count as visible.
- int peekAmount = mContext.getResources()
- .getDimensionPixelSize(R.dimen.car_last_card_peek_amount);
- if (focusedPosition == state.getItemCount() - 1) {
- // The last item is focused.
- bottomEdgeThatMustBeOnScreen = getDecoratedBottom(child);
- } else if (focusedIndex == getChildCount() - 1) {
- // The last laid out item is focused. Scroll enough so that the next card has at least
- // the peek size visible
- ViewGroup.MarginLayoutParams params =
- (ViewGroup.MarginLayoutParams) child.getLayoutParams();
- // We add params.topMargin as an estimate because we don't actually know the top margin
- // of the next row.
- bottomEdgeThatMustBeOnScreen = getDecoratedBottom(child) +
- params.bottomMargin + params.topMargin + peekAmount;
- } else {
- View nextChild = getChildAt(focusedIndex + 1);
- bottomEdgeThatMustBeOnScreen = getDecoratedTop(nextChild) + peekAmount;
- }
-
- if (bottomEdgeThatMustBeOnScreen > getHeight()) {
- // We're going to have to scroll because the bottom edge that must be on screen is past
- // the bottom.
- int topEdgeToFindViewUnder = getPaddingTop() +
- bottomEdgeThatMustBeOnScreen - getHeight();
-
- View nextChild = null;
- for (int i = 0; i < getChildCount(); i++) {
- View potentialNextChild = getChildAt(i);
- RecyclerView.LayoutParams params = getParams(potentialNextChild);
- float top = getDecoratedTop(potentialNextChild) - params.topMargin;
- if (top >= topEdgeToFindViewUnder) {
- nextChild = potentialNextChild;
- break;
- }
- }
-
- if (nextChild == null) {
- Log.e(TAG, "There is no view under " + topEdgeToFindViewUnder);
- return true;
- }
- int nextChildPosition = getPosition(nextChild);
- parent.smoothScrollToPosition(nextChildPosition);
- } else {
- int firstFullyVisibleIndex = getFirstFullyVisibleChildIndex();
- if (focusedIndex <= firstFullyVisibleIndex) {
- parent.smoothScrollToPosition(Math.max(focusedPosition - 1, 0));
- }
- }
- return true;
- }
-
- /**
- * We don't actually know the size of every single view, only what is currently laid out.
- * This makes it difficult to do accurate scrollbar calculations. However, lists in the car
- * often consist of views with identical heights. Because of that, we can use
- * a single sample view to do our calculations for. The main exceptions are in the first items
- * of a list (hero card, last call card, etc) so if the first view is at position 0, we pick
- * the next one.
- *
- * @return The decorated measured height of the sample view plus its margins.
- */
- private int getSampleViewHeight() {
- if (mSampleViewHeight != -1) {
- return mSampleViewHeight;
- }
- int sampleViewIndex = getFirstFullyVisibleChildIndex();
- View sampleView = getChildAt(sampleViewIndex);
- if (getPosition(sampleView) == 0 && sampleViewIndex < getChildCount() - 1) {
- sampleView = getChildAt(++sampleViewIndex);
- }
- RecyclerView.LayoutParams params = getParams(sampleView);
- int height =
- getDecoratedMeasuredHeight(sampleView) + params.topMargin + params.bottomMargin;
- if (height == 0) {
- // This can happen if the view isn't measured yet.
- Log.w(TAG, "The sample view has a height of 0. Returning a dummy value for now " +
- "that won't be cached.");
- height = mContext.getResources().getDimensionPixelSize(R.dimen.car_sample_row_height);
- } else {
- mSampleViewHeight = height;
- }
- return height;
- }
-
- /**
- * @return The height of the RecyclerView excluding padding.
- */
- private int getAvailableHeight() {
- return getHeight() - getPaddingTop() - getPaddingBottom();
- }
-
- /**
- * @return {@link RecyclerView.LayoutParams} for the given view or null if it isn't a child
- * of {@link RecyclerView}.
- */
- private static RecyclerView.LayoutParams getParams(View view) {
- return (RecyclerView.LayoutParams) view.getLayoutParams();
- }
-
- /**
- * Custom {@link LinearSmoothScroller} that has:
- * a) Custom control over the speed of scrolls.
- * b) Scrolling snaps to start. All of our scrolling logic depends on that.
- * c) Keeps track of some state of the current scroll so that can aid in things like
- * the scrollbar calculations.
- */
- private final class CarSmoothScroller extends LinearSmoothScroller {
- /** This value (150) was hand tuned by UX for what felt right. **/
- private static final float MILLISECONDS_PER_INCH = 150f;
- /** This value (0.45) was hand tuned by UX for what felt right. **/
- private static final float DECELERATION_TIME_DIVISOR = 0.45f;
- private static final int NON_TOUCH_MAX_DECELERATION_MS = 1000;
-
- /** This value (1.8) was hand tuned by UX for what felt right. **/
- private final Interpolator mInterpolator = new DecelerateInterpolator(1.8f);
-
- private final boolean mHasTouch;
- private final int mTargetPosition;
-
-
- public CarSmoothScroller(Context context, int targetPosition) {
- super(context);
- mTargetPosition = targetPosition;
- mHasTouch = mContext.getResources().getBoolean(R.bool.car_true_for_touch);
- }
-
- @Override
- public PointF computeScrollVectorForPosition(int i) {
- if (getChildCount() == 0) {
- return null;
- }
- final int firstChildPos = getPosition(getChildAt(getFirstFullyVisibleChildIndex()));
- final int direction = (mTargetPosition < firstChildPos) ? -1 : 1;
- return new PointF(0, direction);
- }
-
- @Override
- protected int getVerticalSnapPreference() {
- // This is key for most of the scrolling logic that guarantees that scrolling
- // will settle with a view aligned to the top.
- return LinearSmoothScroller.SNAP_TO_START;
- }
-
- @Override
- protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
- int dy = calculateDyToMakeVisible(targetView, SNAP_TO_START);
- if (dy == 0) {
- if (DEBUG) {
- Log.d(TAG, "Scroll distance is 0");
- }
- return;
- }
-
- final int time = calculateTimeForDeceleration(dy);
- if (time > 0) {
- action.update(0, -dy, time, mInterpolator);
- }
- }
-
- @Override
- protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
- return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
- }
-
- @Override
- protected int calculateTimeForDeceleration(int dx) {
- int time = (int) Math.ceil(calculateTimeForScrolling(dx) / DECELERATION_TIME_DIVISOR);
- return mHasTouch ? time : Math.min(time, NON_TOUCH_MAX_DECELERATION_MS);
- }
-
- public int getTargetPosition() {
- return mTargetPosition;
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/CarListItemViewHolder.java b/car-support-lib/src/android/support/car/ui/CarListItemViewHolder.java
deleted file mode 100644
index 986b549..0000000
--- a/car-support-lib/src/android/support/car/ui/CarListItemViewHolder.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.ViewStub;
-import android.widget.CheckBox;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * ViewHolder for @layout/sdk_car_list_item that is used to handle the various sdk item templates
- * @hide
- */
-public class CarListItemViewHolder extends RecyclerView.ViewHolder {
- public final FrameLayout iconContainer;
- public final ImageView icon;
- public final TextView title;
- public final TextView text;
- public final ImageView rightImage;
- public final CheckBox rightCheckbox;
- public final TextView rightText;
- public final FrameLayout remoteViewsContainer;
-
- public CarListItemViewHolder(View v, int viewStubLayoutId) {
- super(v);
- icon = (ImageView) v.findViewById(R.id.icon);
- iconContainer = (FrameLayout) v.findViewById(R.id.icon_container);
- title = (TextView) v.findViewById(R.id.title);
- text = (TextView) v.findViewById(R.id.text);
- remoteViewsContainer = (FrameLayout) v.findViewById(R.id.remoteviews);
- ViewStub rightStub = (ViewStub) v.findViewById(R.id.right_item);
- if (rightStub != null) {
- rightStub.setLayoutResource(viewStubLayoutId);
- rightStub.setInflatedId(R.id.right_item);
-
- if (viewStubLayoutId == R.layout.car_menu_checkbox) {
- rightCheckbox = (CheckBox) rightStub.inflate();
- rightImage = null;
- rightText = null;
- } else if (viewStubLayoutId == R.layout.car_imageview) {
- rightImage = (ImageView) rightStub.inflate();
- rightCheckbox = null;
- rightText = null;
- } else if (viewStubLayoutId == R.layout.car_textview) {
- rightText = (TextView) rightStub.inflate();
- rightCheckbox = null;
- rightImage = null;
- } else {
- rightImage = null;
- rightCheckbox = null;
- rightText = null;
- }
- } else {
- rightImage = null;
- rightCheckbox = null;
- rightText = null;
- }
- }
-}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/ui/CarNavExtender.java b/car-support-lib/src/android/support/car/ui/CarNavExtender.java
deleted file mode 100644
index 26a2c52..0000000
--- a/car-support-lib/src/android/support/car/ui/CarNavExtender.java
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.app.Notification;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.app.NotificationCompat;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Helper class to add navigation extensions to notifications for use in Android Auto.
- * <p>
- * To create a notification with navigation extensions:
- * <ol>
- * <li>Create a {@link android.app.Notification.Builder}, setting any desired
- * properties.
- * <li>Create a {@link CarNavExtender}.
- * <li>Set car-specific properties using the
- * {@code add} and {@code set} methods of {@link CarNavExtender}.
- * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
- * notification.
- * <li>Post the notification to the notification system with the
- * {@code NotificationManager.notify(...)} methods.
- * </ol>
- *
- * <pre class="prettyprint">
- * Notification notif = new Notification.Builder(mContext)
- * .setContentTitle("Turn right in 2.0 miles on to US 101-N")
- * .setContentText("43 mins (32 mi) to Home")
- * .setSmallIcon(R.drawable.ic_nav)
- * .extend(new CarNavExtender()
- * .setContentTitle("US 101-N")
- * .setContentText("400 ft")
- * .setSubText("43 mins to Home")
- * .build();
- * NotificationManager notificationManger =
- * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- * notificationManger.notify(0, notif);</pre>
- *
- * <p>CarNavExtender fields can be accessed on an existing notification by using the
- * {@code CarNavExtender(Notification)} constructor,
- * and then using the {@code get} methods to access values.
- * @hide
- */
-public class CarNavExtender implements NotificationCompat.Extender {
- /** This value must remain unchanged for compatibility. **/
- private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
- private static final String EXTRA_IS_EXTENDED =
- "com.google.android.gms.car.support.CarNavExtender.EXTENDED";
- private static final String EXTRA_CONTENT_ID = "content_id";
- private static final String EXTRA_TYPE = "type";
- private static final String EXTRA_SUB_TEXT = "sub_text";
- private static final String EXTRA_ACTION_ICON = "action_icon";
- /** This value must remain unchanged for compatibility. **/
- private static final String EXTRA_CONTENT_INTENT = "content_intent";
- /** This value must remain unchanged for compatibility. **/
- private static final String EXTRA_COLOR = "app_color";
- private static final String EXTRA_NIGHT_COLOR = "app_night_color";
- /** This value must remain unchanged for compatibility. **/
- private static final String EXTRA_STREAM_VISIBILITY = "stream_visibility";
- /** This value must remain unchanged for compatibility. **/
- private static final String EXTRA_HEADS_UP_VISIBILITY = "heads_up_visibility";
- private static final String EXTRA_IGNORE_IN_STREAM = "ignore_in_stream";
-
- @IntDef({TYPE_HERO, TYPE_NORMAL})
- @Retention(RetentionPolicy.SOURCE)
- private @interface Type {}
- public static final int TYPE_HERO = 0;
- public static final int TYPE_NORMAL = 1;
-
- private boolean mIsExtended;
- /** <code>null</code> if not explicitly set. **/
- private Long mContentId;
- private int mType = TYPE_NORMAL;
- private CharSequence mContentTitle;
- private CharSequence mContentText;
- private CharSequence mSubText;
- private Bitmap mLargeIcon;
- private @DrawableRes int mActionIcon;
- private Intent mContentIntent;
- private int mColor = Notification.COLOR_DEFAULT;
- private int mNightColor = Notification.COLOR_DEFAULT;
- private boolean mShowInStream = true;
- private boolean mShowAsHeadsUp;
- private boolean mIgnoreInStream;
-
- /**
- * Create a new CarNavExtender to extend a new notification.
- */
- public CarNavExtender() {
- }
-
- /**
- * Reconstruct a CarNavExtender from an existing notification. Can be used to retrieve values.
- *
- * @param notification The notification to retrieve the values from.
- */
- public CarNavExtender(@NonNull Notification notification) {
- Bundle extras = NotificationCompat.getExtras(notification);
- if (extras == null) {
- return;
- }
- Bundle b = extras.getBundle(EXTRA_CAR_EXTENDER);
- if (b == null) {
- return;
- }
-
- mIsExtended = b.getBoolean(EXTRA_IS_EXTENDED);
- mContentId = (Long) b.getSerializable(EXTRA_CONTENT_ID);
- // The ternary guarantees that we return either TYPE_HERO or TYPE_NORMAL.
- mType = (b.getInt(EXTRA_TYPE, TYPE_NORMAL) == TYPE_HERO) ? TYPE_HERO : TYPE_NORMAL;
- mContentTitle = b.getCharSequence(Notification.EXTRA_TITLE);
- mContentText = b.getCharSequence(Notification.EXTRA_TEXT);
- mSubText = b.getCharSequence(EXTRA_SUB_TEXT);
- mLargeIcon = b.getParcelable(Notification.EXTRA_LARGE_ICON);
- mActionIcon = b.getInt(EXTRA_ACTION_ICON);
- mContentIntent = b.getParcelable(EXTRA_CONTENT_INTENT);
- mColor = b.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
- mNightColor = b.getInt(EXTRA_NIGHT_COLOR, Notification.COLOR_DEFAULT);
- mShowInStream = b.getBoolean(EXTRA_STREAM_VISIBILITY, true);
- mShowAsHeadsUp = b.getBoolean(EXTRA_HEADS_UP_VISIBILITY);
- mIgnoreInStream = b.getBoolean(EXTRA_IGNORE_IN_STREAM);
- }
-
- @Override
- public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
- Bundle b = new Bundle();
- b.putBoolean(EXTRA_IS_EXTENDED, true);
- b.putSerializable(EXTRA_CONTENT_ID, mContentId);
- b.putInt(EXTRA_TYPE, mType);
- b.putCharSequence(Notification.EXTRA_TITLE, mContentTitle);
- b.putCharSequence(Notification.EXTRA_TEXT, mContentText);
- b.putCharSequence(EXTRA_SUB_TEXT, mSubText);
- b.putParcelable(Notification.EXTRA_LARGE_ICON, mLargeIcon);
- b.putInt(EXTRA_ACTION_ICON, mActionIcon);
- b.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
- b.putInt(EXTRA_COLOR, mColor);
- b.putInt(EXTRA_NIGHT_COLOR, mNightColor);
- b.putBoolean(EXTRA_STREAM_VISIBILITY, mShowInStream);
- b.putBoolean(EXTRA_HEADS_UP_VISIBILITY, mShowAsHeadsUp);
- b.putBoolean(EXTRA_IGNORE_IN_STREAM, mIgnoreInStream);
- builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, b);
- return builder;
- }
-
- /**
- * @return <code>true</code> if the notification was extended with {@link CarNavExtender}.
- */
- public boolean isExtended() {
- return mIsExtended;
- }
-
- /**
- * Static version of {@link #isExtended()}.
- */
- public static boolean isExtended(Notification notification) {
- Bundle extras = NotificationCompat.getExtras(notification);
- if (extras == null) {
- return false;
- }
-
- extras = extras.getBundle(EXTRA_CAR_EXTENDER);
- return extras != null && extras.getBoolean(EXTRA_IS_EXTENDED);
- }
-
- /**
- * Sets an id for the content of this notification. If the content id matches an existing
- * notification, any timers that control ranking and heads up notification will remain
- * unchanged. However, if it differs from the previous notification with the same id then
- * this notification will be treated as a new notification with respect to heads up
- * notifications and ranking.
- *
- * If no content id is specified, it will be treated like a new content id.
- *
- * A content id will only be compared to the existing notification, not the entire history of
- * content ids.
- *
- * @param contentId The content id that represents this notification.
- * @return This object for method chaining.
- */
- public CarNavExtender setContentId(long contentId) {
- mContentId = contentId;
- return this;
- }
-
- /**
- * @return The content id for this notification or <code>null</code> if it was not specified.
- */
- @Nullable
- public Long getContentId() {
- return mContentId;
- }
-
- /**
- * @param type The type of notification that this will be displayed as in the Android Auto.
- * @return This object for method chaining.
- *
- * @see #TYPE_NORMAL
- * @see #TYPE_HERO
- */
- public CarNavExtender setType(@Type int type) {
- mType = type;
- return this;
- }
-
- /**
- * @return The type of notification
- *
- * @see #TYPE_NORMAL
- * @see #TYPE_HERO
- */
- @Type
- public int getType() {
- return mType;
- }
-
- /**
- * @return The type without having to construct an entire {@link CarNavExtender} object.
- */
- @Type
- public static int getType(Notification notification) {
- Bundle extras = NotificationCompat.getExtras(notification);
- if (extras == null) {
- return TYPE_NORMAL;
- }
- Bundle b = extras.getBundle(EXTRA_CAR_EXTENDER);
- if (b == null) {
- return TYPE_NORMAL;
- }
-
- // The ternary guarantees that we return either TYPE_HERO or TYPE_NORMAL.
- return (b.getInt(EXTRA_TYPE, TYPE_NORMAL) == TYPE_HERO) ? TYPE_HERO : TYPE_NORMAL;
- }
-
- /**
- * @param contentTitle Override for the notification's content title.
- * @return This object for method chaining.
- */
- public CarNavExtender setContentTitle(CharSequence contentTitle) {
- mContentTitle = contentTitle;
- return this;
- }
-
- /**
- * @return The content title for the notification if one was explicitly set with
- * {@link #setContentTitle(CharSequence)}.
- */
- public CharSequence getContentTitle() {
- return mContentTitle;
- }
-
- /**
- * @param contentText Override for the notification's content text. If set to an empty string,
- * it will be treated as if there is no context text by the UI.
- * @return This object for method chaining.
- */
- public CarNavExtender setContentText(CharSequence contentText) {
- mContentText = contentText;
- return this;
- }
-
- /**
- * @return The content text for the notification if one was explicitly set with
- * {@link #setContentText(CharSequence)}.
- */
- @Nullable
- public CharSequence getContentText() {
- return mContentText;
- }
-
- /**
- * @param subText A third text field that will be displayed on hero cards.
- * @return This object for method chaining.
- */
- public CarNavExtender setSubText(CharSequence subText) {
- mSubText = subText;
- return this;
- }
-
- /**
- * @return The secondary content text for the notification or null if it wasn't set.
- */
- @Nullable
- public CharSequence getSubText() {
- return mSubText;
- }
-
- /**
- * @param largeIcon Override for the notification's large icon.
- * @return This object for method chaining.
- */
- public CarNavExtender setLargeIcon(Bitmap largeIcon) {
- mLargeIcon = largeIcon;
- return this;
- }
-
- /**
- * @return The large icon for the notification if one was explicitly set with
- * {@link #setLargeIcon(android.graphics.Bitmap)}.
- */
- public Bitmap getLargeIcon() {
- return mLargeIcon;
- }
-
- /**
- * By default, Android Auto will show a navigation chevron on cards. However, a separate icon
- * can be set here to override it.
- *
- * @param actionIcon The action icon resource id from your package that you would like to
- * use instead of the navigation chevron.
- * @return This object for method chaining.
- */
- public CarNavExtender setActionIcon(@DrawableRes int actionIcon) {
- mActionIcon = actionIcon;
- return this;
- }
-
- /**
- * @return The overridden action icon or 0 if one wasn't set.
- */
- @DrawableRes
- public int getActionIcon() {
- return mActionIcon;
- }
-
- /**
- * @param contentIntent The content intent that will be sent using
- * {@link com.google.android.gms.car.CarActivity#startCarProjectionActivity(android.content.Intent)}
- * It is STRONGLY suggested that you set a content intent or else the
- * notification will have no action when tapped.
- * @return This object for method chaining.
- */
- public CarNavExtender setContentIntent(Intent contentIntent) {
- mContentIntent = contentIntent;
- return this;
- }
-
- /**
- * @return The content intent that will be sent using
- * {@link com.google.android.gms.car.CarActivity#startCarProjectionActivity(android.content.Intent)}
- */
- public Intent getContentIntent() {
- return mContentIntent;
- }
-
- /**
- * @param color Override for the notification color.
- * @return This object for method chaining.
- *
- * @see android.app.Notification.Builder#setColor(int)
- */
- public CarNavExtender setColor(int color) {
- mColor = color;
- return this;
- }
-
- /**
- * @return The color specified by the notification or {@link android.app.Notification#COLOR_DEFAULT} if
- * one wasn't explicitly set with {@link #setColor(int)}.
- */
- public int getColor() {
- return mColor;
- }
-
- /**
- * @param nightColor Override for the notification color at night.
- * @return This object for method chaining.
- *
- * @see android.app.Notification.Builder#setColor(int)
- */
- public CarNavExtender setNightColor(int nightColor) {
- mNightColor = nightColor;
- return this;
- }
-
- /**
- * @return The night color specified by the notification or {@link android.app.Notification#COLOR_DEFAULT}
- * if one wasn't explicitly set with {@link #setNightColor(int)}.
- */
- public int getNightColor() {
- return mNightColor;
- }
-
- /**
- * @param show Whether or not to show the notification in the stream.
- * @return This object for method chaining.
- */
- public CarNavExtender setShowInStream(boolean show) {
- mShowInStream = show;
- return this;
- }
-
- /**
- * @return Whether or not to show the notification in the stream.
- */
- public boolean getShowInStream() {
- return mShowInStream;
- }
-
- /**
- * @param show Whether or not to show the notification as a heads up notification.
- * @return This object for method chaining.
- */
- public CarNavExtender setShowAsHeadsUp(boolean show) {
- mShowAsHeadsUp = show;
- return this;
- }
-
- /**
- * @return Whether or not to show the notification as a heads up notification.
- */
- public boolean getShowAsHeadsUp() {
- return mShowAsHeadsUp;
- }
-
- /**
- * @param ignore Whether or not this notification can be shown as a heads-up notification if
- * the user is already on the stream.
- * @return This object for method chaining.
- */
- public CarNavExtender setIgnoreInStream(boolean ignore) {
- mIgnoreInStream = ignore;
- return this;
- }
-
- /**
- * @return Whether or not the stream item can be shown as a heads-up notification if ther user
- * already is on the stream.
- */
- public boolean getIgnoreInStream() {
- return mIgnoreInStream;
- }
-}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/ui/CarRecyclerView.java b/car-support-lib/src/android/support/car/ui/CarRecyclerView.java
deleted file mode 100644
index 9838e18..0000000
--- a/car-support-lib/src/android/support/car/ui/CarRecyclerView.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.RecyclerView;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * Custom {@link RecyclerView} that helps {@link CarLayoutManager} properly fling and paginate.
- *
- * It also has the ability to fade children as they scroll off screen that can be set
- * with {@link #setFadeLastItem(boolean)}.
- * @hide
- */
-public class CarRecyclerView extends RecyclerView {
- private static final String PARCEL_CLASS = "android.os.Parcel";
- private static final String SAVED_STATE_CLASS =
- "android.support.v7.widget.RecyclerView.SavedState";
- private boolean mFadeLastItem;
- private Constructor<?> mSavedStateConstructor;
- /**
- * If the user releases the list with a velocity of 0, {@link #fling(int, int)} will not be
- * called. However, we want to make sure that the list still snaps to the next page when this
- * happens.
- */
- private boolean mWasFlingCalledForGesture;
-
- public CarRecyclerView(Context context) {
- this(context, null);
- }
-
- public CarRecyclerView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public CarRecyclerView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- setFocusableInTouchMode(false);
- setFocusable(false);
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- if (state.getClass().getClassLoader() != getClass().getClassLoader()) {
- if (mSavedStateConstructor == null) {
- mSavedStateConstructor = getSavedStateConstructor();
- }
- // Class loader mismatch, recreate from parcel.
- Parcel obtain = Parcel.obtain();
- state.writeToParcel(obtain, 0);
- try {
- Parcelable newState = (Parcelable) mSavedStateConstructor.newInstance(obtain);
- super.onRestoreInstanceState(newState);
- } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
- | InvocationTargetException e) {
- // Fail loudy here.
- throw new RuntimeException(e);
- }
- } else {
- super.onRestoreInstanceState(state);
- }
- }
-
- @Override
- public boolean fling(int velocityX, int velocityY) {
- mWasFlingCalledForGesture = true;
- return ((CarLayoutManager) getLayoutManager()).settleScrollForFling(this, velocityY);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent e) {
- // We want the parent to handle all touch events. There's a lot going on there,
- // and there is no reason to overwrite that functionality. If we do, bad things will happen.
- final boolean ret = super.onTouchEvent(e);
-
- int action = e.getActionMasked();
- if (action == MotionEvent.ACTION_UP) {
- if (!mWasFlingCalledForGesture) {
- ((CarLayoutManager) getLayoutManager()).settleScrollForFling(this, 0);
- }
- mWasFlingCalledForGesture = false;
- }
-
- return ret;
- }
-
- @Override
- public boolean drawChild(@NonNull Canvas canvas, @NonNull View child, long drawingTime) {
- if (mFadeLastItem) {
- float onScreen = 1f;
- if ((child.getTop() < getBottom() && child.getBottom() > getBottom())) {
- onScreen = ((float) (getBottom() - child.getTop())) / (float) child.getHeight();
- } else if ((child.getTop() < getTop() && child.getBottom() > getTop())) {
- onScreen = ((float) (child.getBottom() - getTop())) / (float) child.getHeight();
- }
- float alpha = 1 - (1 - onScreen) * (1 - onScreen);
- fadeChild(child, alpha);
- }
-
- return super.drawChild(canvas, child, drawingTime);
- }
-
- public void setFadeLastItem(boolean fadeLastItem) {
- mFadeLastItem = fadeLastItem;
- }
-
- public void pageUp() {
- CarLayoutManager lm = (CarLayoutManager) getLayoutManager();
- int pageUpPosition = lm.getPageUpPosition();
- if (pageUpPosition == -1) {
- return;
- }
-
- smoothScrollToPosition(pageUpPosition);
- }
-
- public void pageDown() {
- CarLayoutManager lm = (CarLayoutManager) getLayoutManager();
- int pageDownPosition = lm.getPageDownPosition();
- if (pageDownPosition == -1) {
- return;
- }
-
- smoothScrollToPosition(pageDownPosition);
- }
-
- /**
- * Sets {@link #mSavedStateConstructor} to private SavedState constructor.
- */
- private Constructor<?> getSavedStateConstructor() {
- Class<?> savedStateClass = null;
- // Find package private subclass RecyclerView$SavedState.
- for (Class<?> c : RecyclerView.class.getDeclaredClasses()) {
- if (c.getCanonicalName().equals(SAVED_STATE_CLASS)) {
- savedStateClass = c;
- break;
- }
- }
- if (savedStateClass == null) {
- throw new RuntimeException("RecyclerView$SavedState not found!");
- }
- // Find constructor that takes a {@link Parcel}.
- for (Constructor<?> c : savedStateClass.getDeclaredConstructors()) {
- Class<?>[] parameterTypes = c.getParameterTypes();
- if (parameterTypes.length == 1
- && parameterTypes[0].getCanonicalName().equals(PARCEL_CLASS)) {
- mSavedStateConstructor = c;
- mSavedStateConstructor.setAccessible(true);
- break;
- }
- }
- if (mSavedStateConstructor == null) {
- throw new RuntimeException("RecyclerView$SavedState constructor not found!");
- }
- return mSavedStateConstructor;
- }
-
- /**
- * Fades child by alpha. If child is a {@link android.view.ViewGroup} then it will recursively fade its
- * children instead.
- */
- private void fadeChild(@NonNull View child, float alpha) {
- if (child instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) child;
- for (int i = 0; i < vg.getChildCount(); i++) {
- fadeChild(vg.getChildAt(i), alpha);
- }
- } else {
- child.setAlpha(alpha);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/CarUiResourceLoader.java b/car-support-lib/src/android/support/car/ui/CarUiResourceLoader.java
deleted file mode 100644
index bc0a769..0000000
--- a/car-support-lib/src/android/support/car/ui/CarUiResourceLoader.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.DisplayMetrics;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class CarUiResourceLoader {
- private static final String TAG = "CarUiResourceLoader";
- private static final String CAR_UI_PACKAGE = "android.car.ui.provider";
- private static final String DRAWABLE = "drawable";
- private static final String BOOL = "bool";
- private static final String DIMEN = "dimen";
-
- public static synchronized Drawable getDrawable(
- Context context, String drawableName) {
- return getDrawable(context, drawableName, null);
- }
-
- public static synchronized Drawable getDrawable(
- Context context, String drawableName, DisplayMetrics metrics) {
- Resources res;
- try {
- res = context.getPackageManager().getResourcesForApplication(CAR_UI_PACKAGE);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "CarUiProvider not installed, this class will return blank drawables.");
- return new ColorDrawable(Color.TRANSPARENT);
- }
-
- int id = res.getIdentifier(drawableName, DRAWABLE, CAR_UI_PACKAGE);
- if (id == 0) {
- Log.w(TAG, "Resource not found in CarUiProvider.apk: " + drawableName);
- return new ColorDrawable(Color.TRANSPARENT);
- }
- if (metrics == null) {
- return res.getDrawable(id, null);
- } else {
- return res.getDrawableForDensity(id, metrics.densityDpi, null);
- }
- }
-
- public static synchronized boolean getBoolean(
- Context context, String boolName, boolean def) {
- Resources res;
- try {
- res = context.getPackageManager().getResourcesForApplication(CAR_UI_PACKAGE);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "CarUiProvider not installed, returning default");
- return def;
- }
-
- int id = res.getIdentifier(boolName, BOOL, CAR_UI_PACKAGE);
- if (id == 0) {
- Log.w(TAG, "Resource not found in CarUiProvider.apk: " + boolName);
- return def;
- }
- return res.getBoolean(id);
- }
-
- public static synchronized float getDimen(
- Context context, String dimenName, float def) {
- Resources res;
- try {
- res = context.getPackageManager().getResourcesForApplication(CAR_UI_PACKAGE);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "CarUiProvider not installed, returning default");
- return def;
- }
-
- int id = res.getIdentifier(dimenName, DIMEN, CAR_UI_PACKAGE);
- if (id == 0) {
- Log.w(TAG, "Resource not found in CarUiProvider.apk: " + dimenName);
- return def;
- }
- return res.getDimension(id);
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/CheckboxWrapperView.java b/car-support-lib/src/android/support/car/ui/CheckboxWrapperView.java
deleted file mode 100644
index 157f7fb..0000000
--- a/car-support-lib/src/android/support/car/ui/CheckboxWrapperView.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.widget.CheckBox;
-
-/**
- * A wrapper class for CheckBox that is required because the state is created by two different
- * class loaders, which causes a crash. When the state and class are created by different class
- * loaders, the default state will be restored instead of the saved state.
- * Reflection cannot be used to recreate the state because the class that stores the state
- * (CompoundButton$SavedState) has been stripped out of the Android SDK.
- * @hide
- */
-public class CheckboxWrapperView extends CheckBox {
-
- public CheckboxWrapperView(Context context) {
- super(context);
- }
-
- public CheckboxWrapperView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public CheckboxWrapperView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- // If the class loaders are different, just restore the default state. This is fine
- // since we refetch the menus anyways and any state will be restored then.
- if (state.getClass().getClassLoader() != getClass().getClassLoader()) {
- super.onRestoreInstanceState(onSaveInstanceState());
- } else {
- super.onRestoreInstanceState(state);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/CircleBitmapDrawable.java b/car-support-lib/src/android/support/car/ui/CircleBitmapDrawable.java
deleted file mode 100644
index 3f3696b..0000000
--- a/car-support-lib/src/android/support/car/ui/CircleBitmapDrawable.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
-
-
-/**
- * A drawable for displaying a circular bitmap. This is a wrapper over RoundedBitmapDrawable,
- * since that implementation doesn't behave quite as desired.
- *
- * Note that not all drawable functionality is passed to the RoundedBitmapDrawable at this
- * time. Feel free to add more as necessary.
- * @hide
- */
-public class CircleBitmapDrawable extends Drawable {
- private final Resources mResources;
-
- private Bitmap mBitmap;
- private RoundedBitmapDrawable mDrawable;
- private int mAlpha = -1;
- private ColorFilter mCf = null;
-
- public CircleBitmapDrawable(@NonNull Resources res, @NonNull Bitmap bitmap) {
- mBitmap = bitmap;
- mResources = res;
- }
-
- @Override
- public void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
- int width = bounds.right - bounds.left;
- int height = bounds.bottom - bounds.top;
-
- Bitmap processed = mBitmap;
- /* if (processed.getWidth() != width || processed.getHeight() != height) {
- processed = BitmapUtils.scaleBitmap(processed, width, height);
- }
- // RoundedBitmapDrawable is actually just a rounded rectangle. So it can't turn
- // rectangular images into circles.
- if (processed.getWidth() != processed.getHeight()) {
- int diam = Math.min(width, height);
- Bitmap cropped = BitmapUtils.cropBitmap(processed, diam, diam);
- if (processed != mBitmap) {
- processed.recycle();
- }
- processed = cropped;
- }*/
- mDrawable = RoundedBitmapDrawableFactory.create(mResources, processed);
- mDrawable.setBounds(bounds);
- mDrawable.setAntiAlias(true);
- mDrawable.setCornerRadius(Math.min(width, height) / 2f);
- if (mAlpha != -1) {
- mDrawable.setAlpha(mAlpha);
- }
- if (mCf != null) {
- mDrawable.setColorFilter(mCf);
- }
- invalidateSelf();
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (mDrawable != null) {
- mDrawable.draw(canvas);
- }
- }
-
- @Override
- public int getOpacity() {
- return mDrawable != null ? mDrawable.getOpacity() : PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public void setAlpha(int alpha) {
- mAlpha = alpha;
- if (mDrawable != null) {
- mDrawable.setAlpha(alpha);
- invalidateSelf();
- }
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- mCf = cf;
- if (mDrawable != null) {
- mDrawable.setColorFilter(cf);
- invalidateSelf();
- }
- }
-
- /**
- * Convert the drawable to a bitmap.
- * @param size The target size of the bitmap in pixels.
- * @return A bitmap representation of the drawable.
- */
- public Bitmap toBitmap(int size) {
- Bitmap largeIcon = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(largeIcon);
- Rect bounds = getBounds();
- setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- draw(canvas);
- setBounds(bounds);
- return largeIcon;
- }
-}
-
diff --git a/car-support-lib/src/android/support/car/ui/CircularClipAnimation.java b/car-support-lib/src/android/support/car/ui/CircularClipAnimation.java
deleted file mode 100644
index 4952720..0000000
--- a/car-support-lib/src/android/support/car/ui/CircularClipAnimation.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.graphics.Path;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Handles the quantum animation to show or hide elements using a circular clip animation. Views
- * that should be clipped can be added to the animation and are notified through the
- * {@link PathClippingView} interface.
- *
- * The effect is implemented using a circular clip. The state runs the animation and at each
- * step computes the effect's parameters (circle center and radius), which it then passes to
- * registered {@link PathClippingView}s.
- *
- * This a modified version of GoogleSearch/com.google.android.shared.ui/CircularClipAnimation
- * @hide
- */
-public class CircularClipAnimation
- implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
- private static final boolean DBG = false;
- private static final String TAG = "CircularClipAnimation";
-
- public static final int DURATION_MS = 300;
-
- private static float getFillRadius(int width, int height, int x, int y) {
- return (float) Math.sqrt(
- Math.pow(Math.max(width - x, x), 2) + Math.pow(Math.max(height - y, y), 2));
- }
-
- /**
- * Container view, in whose coordinate space the clip parameters are computed. The offset for
- * each listener view is computed relative to this view.
- */
- private final ViewGroup mContainer;
-
- /**
- * Listeners that will be called when the clip animation updates.
- */
- private final List<PathClipingViewInfo> mViewInfos = new ArrayList<>();
-
- private final Rect mRect;
- private final ValueAnimator mAnimator;
-
- /** A view, to which the circle's center can be anchored to during animation. */
- private View mAnchorView;
-
- /*
- * Circular crop's parameters. These are updated by the animator but can also be set manually
- * prior to starting the animation. Do not modify when animation is in progress.
- */
- private int mCircleX;
- private int mCircleY;
- private float mCircleRadius;
- private int mCircleStartRadius;
-
- public CircularClipAnimation(ViewGroup container) {
- mRect = new Rect();
-
- mContainer = container;
-
- mAnimator = new ValueAnimator();
- mAnimator.setInterpolator(
- new QuantumInterpolator(QuantumInterpolator.FAST_OUT_SLOW_IN, 0, 1, 0));
-
- mAnimator.addUpdateListener(this);
- mAnimator.addListener(this);
- mAnimator.setDuration(DURATION_MS);
- mCircleStartRadius = container.getContext().getResources().getDimensionPixelSize(
- R.dimen.car_touch_feedback_radius);
- }
-
- private int findViewInfo(PathClippingView clipView) {
- for (int c = 0; c < mViewInfos.size(); c++) {
- PathClipingViewInfo info = mViewInfos.get(c);
- if (info.mClippingView == clipView) {
- return c;
- }
- }
- return -1;
- }
-
- /**
- * @param clipView Listener that will receive updates to the animation.
- * @param view The view in whose coordinate space we will call onClipUpdate.
- */
- public void addView(PathClippingView clipView, View view) {
- if (findViewInfo(clipView) == -1) {
- mViewInfos.add(new PathClipingViewInfo(clipView, view));
- }
- }
-
- public void removeView(PathClippingView clipView) {
- if (!mAnimator.isRunning()) {
- Log.e(TAG, "Animator is not running.");
- return;
- }
- int index = findViewInfo(clipView);
- if (index > -1) {
- mViewInfos.remove(index);
- }
- }
-
- /**
- * Updates the circle's center to given point in container coordinate space.
- * Overrides any previous calls to {@link #setupCenter}.
- */
- public void setupCenter(int x, int y) {
- mCircleX = x;
- mCircleY = y;
- }
-
- /**
- * Triggers the effect. Any previous animation will be cancelled.
- *
- * @param showing Whether the animation is supposed to show or hide components.
- * @param x X of the content to be revealed or hidden.
- * @param y Y of the content to be revealed or hidden.
- * @param width Width of the content to be revealed or hidden.
- * @param height Height of the content to be revealed or hidden.
- * @param anchorView See {@link #mAnchorView}.
- */
- public void start(boolean showing, int x, int y, int width, int height, View anchorView) {
- if (DBG) Log.v(TAG,
- "start(" + showing + ", " + width + ", " + height + ", " + anchorView + ")");
-
- // Compute the radius.
- final float radius = getFillRadius(width, height,
- mCircleX - x, mCircleY - y);
-
- // Set anchor view.
- mAnchorView = anchorView;
-
- // Set up animation values. Note that the minimum radius should be larger than 0,
- // otherwise the addCircle operation becomes a No-op, leading to inverted clipping.
- if (showing) {
- mAnimator.setFloatValues(mCircleStartRadius, radius);
- } else {
- mAnimator.setFloatValues(radius, 1);
- }
- mAnimator.start();
- }
-
- /**
- * Updates the circle's center to that of the anchor view.
- */
- private void updateCircleCenterTo(View view) {
- mRect.set(0, 0, view.getWidth(), view.getHeight());
- mContainer.offsetDescendantRectToMyCoords(view, mRect);
-
- mCircleX = mRect.centerX();
- mCircleY = mRect.centerY();
- }
-
- // Animation listeners.
- @Override
- public void onAnimationStart(Animator animation) {
- for (int c = 0; c < mViewInfos.size(); c++) {
- // We find out the offset of each Listeners View from the mContainer and store it
- // so we can offset the paths we pass to each Listener into its coords space.
- mRect.left = 0;
- mRect.top = 0;
- mContainer.offsetRectIntoDescendantCoords(mViewInfos.get(c).mView, mRect);
- mViewInfos.get(c).mOffset.set(mRect.left, mRect.top);
- }
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (mAnchorView != null) {
- updateCircleCenterTo(mAnchorView);
- }
-
- mCircleRadius = (Float) animation.getAnimatedValue();
-
- // Notify listeners.
- for (int c = 0; c < mViewInfos.size(); c++) {
- PathClipingViewInfo info = mViewInfos.get(c);
- info.mPath.reset();
- info.mPath.addCircle(mCircleX + info.mOffset.x, mCircleY + info.mOffset.y,
- mCircleRadius, Path.Direction.CW);
- info.mClippingView.setClipPath(info.mPath);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- for (int c = 0; c < mViewInfos.size(); c++) {
- PathClipingViewInfo info = mViewInfos.get(c);
- info.mClippingView.setClipPath(null);
- }
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
-
- public void addListener(Animator.AnimatorListener listener) {
- mAnimator.addListener(listener);
- }
-
- /**
- * Internal structure to keep track of the clipping components.
- */
- private static class PathClipingViewInfo {
- final PathClippingView mClippingView;
- final View mView;
- final Point mOffset;
- final Path mPath;
-
- PathClipingViewInfo(PathClippingView clippingView, View view) {
- mClippingView = clippingView;
- mView = view;
- mOffset = new Point();
- mPath = new Path();
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/ClippableFrameLayout.java b/car-support-lib/src/android/support/car/ui/ClippableFrameLayout.java
deleted file mode 100644
index cbc627c..0000000
--- a/car-support-lib/src/android/support/car/ui/ClippableFrameLayout.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Path;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-/**
- * FrameLayout that enables the user to set a Path that the view will be clipped to
- *
- * From GoogleSearch/com.google.android.shared.ui/CircularClipAnimation
- * @hide
- */
-public class ClippableFrameLayout extends FrameLayout implements PathClippingView {
- private Path mClipPath;
-
- public ClippableFrameLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public void setClipPath(Path clipPath) {
- mClipPath = clipPath;
- setClipToPadding(true);
- setClipChildren(true);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- setWillNotDraw(false);
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mClipPath != null) {
- canvas.clipPath(mClipPath);
- }
- super.onDraw(canvas);
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/ColorChecker.java b/car-support-lib/src/android/support/car/ui/ColorChecker.java
deleted file mode 100644
index 8191d6f..0000000
--- a/car-support-lib/src/android/support/car/ui/ColorChecker.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class ColorChecker {
- private static final String TAG = "GH.ColorChecker";
- private static final double MIN_CONTRAST_RATIO = 4.5;
- /**
- * Non-critical information doesn't have to meet as stringent contrast requirements.
- */
- private static final double MIN_NON_CRITICAL_CONTRAST_RATIO = 1.5;
-
- /**
- * Calls {@link #getTintColor(int, int...)} with:
- * {@link R.color#car_tint_light} and
- * {@link R.color#car_tint_dark}
- */
- public static int getTintColor(Context context, int backgroundColor) {
- int lightTintColor = context.getResources().getColor(R.color.car_tint_light);
- int darkTintColor = context.getResources().getColor(R.color.car_tint_dark);
-
- return getTintColor(backgroundColor, lightTintColor, darkTintColor);
- }
-
- /**
- * Calls {@link #getNonCriticalTintColor(int, int...)} with:
- * {@link R.color#car_tint_light} and
- * {@link R.color#car_tint_dark}
- */
- public static int getNonCriticalTintColor(Context context, int backgroundColor) {
- int lightTintColor = context.getResources().getColor(R.color.car_tint_light);
- int darkTintColor = context.getResources().getColor(R.color.car_tint_dark);
-
- return getNonCriticalTintColor(backgroundColor, lightTintColor, darkTintColor);
- }
-
- /**
- * Calls {@link #getTintColor(int, int...)} with {@link #MIN_CONTRAST_RATIO}.
- */
- public static int getTintColor(int backgroundColor, int... tintColors) {
- return getTintColor(MIN_CONTRAST_RATIO, backgroundColor, tintColors);
- }
-
- /**
- * Calls {@link #getTintColor(int, int...)} with {@link #MIN_NON_CRITICAL_CONTRAST_RATIO}.
- */
- public static int getNonCriticalTintColor(int backgroundColor, int... tintColors) {
- return getTintColor(MIN_NON_CRITICAL_CONTRAST_RATIO, backgroundColor, tintColors);
- }
-
- /**
- *
- * Determines what color to tint icons given the background color that they sit on.
- *
- * @param minAllowedContrastRatio The minimum contrast ratio
- * @param bgColor The background color that the icons sit on.
- * @param tintColors A list of potential colors to tint the icons with.
- * @return The color that the icons should be tinted. Will be the first tinted color that
- * meets the requirements. If none of the tint colors meet the minimum requirements,
- * either black or white will be returned, whichever has a higher contrast.
- */
- public static int getTintColor(double minAllowedContrastRatio, int bgColor, int... tintColors) {
- for (int tc : tintColors) {
- double contrastRatio = getContrastRatio(bgColor, tc);
- if (contrastRatio >= minAllowedContrastRatio) {
- return tc;
- }
- }
- double blackContrastRatio = getContrastRatio(bgColor, Color.BLACK);
- double whiteContrastRatio = getContrastRatio(bgColor, Color.WHITE);
- if (whiteContrastRatio >= blackContrastRatio) {
- Log.w(TAG, "Tint color does not meet contrast requirements. Using white.");
- return Color.WHITE;
- } else {
- Log.w(TAG, "Tint color does not meet contrast requirements. Using black.");
- return Color.BLACK;
- }
- }
-
- public static double getContrastRatio(int color1, int color2) {
- return getContrastRatio(getLuminance(color1), getLuminance(color2));
- }
-
- public static double getContrastRatio(double luminance1, double luminance2) {
- return (Math.max(luminance1, luminance2) + 0.05) /
- (Math.min(luminance1, luminance2) + 0.05);
- }
-
- /**
- * Calculates the luminance of a color as specified by:
- * http://www.w3.org/TR/WCAG20-TECHS/G17.html
- *
- * @param color The color to calculate the luminance of.
- * @return The luminance.
- */
- public static double getLuminance(int color) {
- // Values are in sRGB
- double r = convert8BitToLuminanceComponent(Color.red(color));
- double g = convert8BitToLuminanceComponent(Color.green(color));
- double b = convert8BitToLuminanceComponent(Color.blue(color));
- return r * 0.2126 + g * 0.7152 + b * 0.0722;
- }
-
- /**
- * Converts am 8 bit color component (0-255) to the luminance component as specified by:
- * http://www.w3.org/TR/WCAG20-TECHS/G17.html
- */
- private static double convert8BitToLuminanceComponent(double component) {
- component /= 255.0;
- if (component <= 0.03928) {
- return component / 12.92;
- } else {
- return Math.pow(((component + 0.055) / 1.055), 2.4);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/Constants.java b/car-support-lib/src/android/support/car/ui/Constants.java
deleted file mode 100644
index e73f6df..0000000
--- a/car-support-lib/src/android/support/car/ui/Constants.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-/**
- * Constants shared by car ui lib and car support lib.
- *
- * @hide
- */
-public class Constants {
- public class CarMenuConstants {
- public static final String KEY_TITLE = "title";
- public static final String KEY_TEXT = "text";
- public static final String KEY_LEFTICON = "leftIcon";
- public static final String KEY_RIGHTICON = "rightIcon";
- public static final String KEY_RIGHTTEXT = "rightText";
- public static final String KEY_WIDGET = "widget";
- public static final String KEY_WIDGET_STATE = "widget_state";
- public static final String KEY_EMPTY_PLACEHOLDER = "empty_placeholder";
- public static final String KEY_FLAGS = "flags";
- public static final String KEY_ID = "id";
- public static final String KEY_REMOTEVIEWS = "remoteViews";
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/CursorFilter.java b/car-support-lib/src/android/support/car/ui/CursorFilter.java
deleted file mode 100644
index 7ebb865..0000000
--- a/car-support-lib/src/android/support/car/ui/CursorFilter.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.database.Cursor;
-import android.widget.Filter;
-
-/**
- * <p>The CursorFilter delegates most of the work to the CursorAdapter.
- * Subclasses should override these delegate methods to run the queries
- * and convert the results into String that can be used by auto-completion
- * widgets.</p>
- *
- * NOTE: this is copied directly from android.widget.CursorFilter
- * @hide
- */
-class CursorFilter extends Filter {
-
- CursorFilterClient mClient;
-
- interface CursorFilterClient {
- CharSequence convertToString(Cursor cursor);
- Cursor runQueryOnBackgroundThread(CharSequence constraint);
- Cursor getCursor();
- void changeCursor(Cursor cursor);
- }
-
- CursorFilter(CursorFilterClient client) {
- mClient = client;
- }
-
- @Override
- public CharSequence convertResultToString(Object resultValue) {
- return mClient.convertToString((Cursor) resultValue);
- }
-
- @Override
- protected FilterResults performFiltering(CharSequence constraint) {
- Cursor cursor = mClient.runQueryOnBackgroundThread(constraint);
-
- FilterResults results = new FilterResults();
- if (cursor != null) {
- results.count = cursor.getCount();
- results.values = cursor;
- } else {
- results.count = 0;
- results.values = null;
- }
- return results;
- }
-
- @Override
- protected void publishResults(CharSequence constraint, FilterResults results) {
- Cursor oldCursor = mClient.getCursor();
-
- if (results.values != null && results.values != oldCursor) {
- mClient.changeCursor((Cursor) results.values);
- }
- }
-}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/ui/CursorRecyclerViewAdapter.java b/car-support-lib/src/android/support/car/ui/CursorRecyclerViewAdapter.java
deleted file mode 100644
index bb7ff87..0000000
--- a/car-support-lib/src/android/support/car/ui/CursorRecyclerViewAdapter.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DataSetObserver;
-import android.provider.BaseColumns;
-import android.support.v7.widget.RecyclerView;
-
-/**
- * Adapter that exposes data from a Cursor to a {@link RecyclerView} widget.
- * The Cursor must include an Id column or this class will not work.
- * @hide
- */
-public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder>
- extends RecyclerView.Adapter<VH> {
-
- protected Context mContext;
- protected Cursor mCursor;
- protected int mRowIdColumn;
-
- public CursorRecyclerViewAdapter(Context context, Cursor cursor) {
- mContext = context;
- mCursor = cursor;
- mRowIdColumn = -1;
- if (mCursor != null) {
- mRowIdColumn = getRowIdColumnIndex(mCursor);
- mCursor.registerDataSetObserver(mDataSetObserver);
- }
- }
-
- public Cursor getCursor() {
- return mCursor;
- }
-
- @Override
- public int getItemCount() {
- if (mCursor != null) {
- return mCursor.getCount();
- }
- return 0;
- }
-
- @Override
- public long getItemId(int position) {
- if (mCursor != null && mCursor.moveToPosition(position)) {
- return mCursor.getLong(mRowIdColumn);
- }
- return 0;
- }
-
- public void onBindViewHolder(VH viewHolder, Cursor cursor) {}
-
- @Override
- public void onBindViewHolder(VH viewHolder, int position) {
- if (!mCursor.moveToPosition(position)) {
- throw new IllegalStateException("can't move cursor to position " + position);
- }
- onBindViewHolder(viewHolder, mCursor);
- }
-
- /**
- * Change the underlying cursor to a new cursor. If there is an existing cursor it will
- * be closed.
- *
- * @param cursor The new cursor to be used.
- */
- public void changeCursor(Cursor cursor) {
- Cursor old = swapCursor(cursor);
- if (old != null) {
- old.close();
- }
- }
-
- /**
- * Swap in a new Cursor, returning the old Cursor. Unlike changeCursor(android.database.Cursor),
- * the returned old Cursor is not closed.
- *
- * @param newCursor The new cursor to be used.
- */
- public Cursor swapCursor(Cursor newCursor) {
- if (newCursor == mCursor) {
- return null;
- }
-
- Cursor oldCursor = mCursor;
- if (oldCursor != null) {
- if (mDataSetObserver != null) {
- oldCursor.unregisterDataSetObserver(mDataSetObserver);
- }
- }
-
- mCursor = newCursor;
- if (mCursor != null) {
- if (mDataSetObserver != null) {
- mCursor.registerDataSetObserver(mDataSetObserver);
- }
- mRowIdColumn = getRowIdColumnIndex(mCursor);
- notifyDataSetChanged();
- } else {
- mRowIdColumn = -1;
- notifyDataSetChanged();
- }
- return oldCursor;
- }
-
- protected int getRowIdColumnIndex(Cursor cursor) {
- return cursor.getColumnIndex(BaseColumns._ID);
- }
-
- protected DataSetObserver mDataSetObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- super.onChanged();
- notifyDataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- mCursor = null;
- notifyDataSetChanged();
- }
- };
-}
diff --git a/car-support-lib/src/android/support/car/ui/DrawerArrowDrawable.java b/car-support-lib/src/android/support/car/ui/DrawerArrowDrawable.java
deleted file mode 100644
index 718a249..0000000
--- a/car-support-lib/src/android/support/car/ui/DrawerArrowDrawable.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-
-/**
- * A drawable that can draw a "Drawer hamburger" menu or an Arrow and animate between them.
- *
- * This is copied from android.support.v7.app.DrawerArrowDrawable except with the styles prefixed
- * with carArrow and the abstract {@link #isLayoutRtl()} set to false.
- * @hide
- */
-public class DrawerArrowDrawable extends Drawable {
-
- private final Paint mPaint = new Paint();
-
- // The angle in degrees that the arrow head is inclined at.
- private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45);
- private final float mBarThickness;
- // The length of top and bottom bars when they merge into an arrow
- private final float mTopBottomArrowSize;
- // The length of middle bar
- private final float mBarSize;
- // The length of the middle bar when arrow is shaped
- private final float mMiddleArrowSize;
- // The space between bars when they are parallel
- private final float mBarGap;
- // Whether bars should spin or not during progress
- private final boolean mSpin;
- // Use Path instead of canvas operations so that if color has transparency, overlapping sections
- // wont look different
- private final Path mPath = new Path();
- // The reported intrinsic size of the drawable.
- private final int mSize;
- // Whether we should mirror animation when animation is reversed.
- private boolean mVerticalMirror = false;
- // The interpolated version of the original progress
- private float mProgress;
- // the amount that overlaps w/ bar size when rotation is max
- private final float mMaxCutForBarSize;
- // The distance of arrow's center from top when horizontal
- private float mCenterOffset;
-
- /**
- * @param context used to get the configuration for the drawable from
- */
- public DrawerArrowDrawable(Context context) {
- final TypedArray typedArray = context.getTheme()
- .obtainStyledAttributes(null, R.styleable.DrawerArrowDrawable,
- R.attr.carDrawerArrowStyle,
- R.style.CarDrawerArrowDrawable);
- mPaint.setAntiAlias(true);
- mPaint.setColor(typedArray.getColor(R.styleable.DrawerArrowDrawable_carArrowColor, 0));
- mSize = typedArray.getDimensionPixelSize(R.styleable.DrawerArrowDrawable_carArrowDrawableSize, 0);
- // round this because having this floating may cause bad measurements
- mBarSize = Math.round(typedArray.getDimension(R.styleable.DrawerArrowDrawable_carArrowBarSize, 0));
- // round this because having this floating may cause bad measurements
- mTopBottomArrowSize = Math.round(typedArray.getDimension(
- R.styleable.DrawerArrowDrawable_carArrowTopBottomBarSize, 0));
- mBarThickness = typedArray.getDimension(R.styleable.DrawerArrowDrawable_carArrowThickness, 0);
- // round this because having this floating may cause bad measurements
- mBarGap = Math.round(typedArray.getDimension(
- R.styleable.DrawerArrowDrawable_carArrowGapBetweenBars, 0));
- mSpin = typedArray.getBoolean(R.styleable.DrawerArrowDrawable_carArrowSpinBars, true);
- mMiddleArrowSize = typedArray
- .getDimension(R.styleable.DrawerArrowDrawable_carArrowMiddleBarSize, 0);
- final int remainingSpace = (int) (mSize - mBarThickness * 3 - mBarGap * 2);
- mCenterOffset = (remainingSpace / 4) * 2; //making sure it is a multiple of 2.
- mCenterOffset += mBarThickness * 1.5 + mBarGap;
- typedArray.recycle();
-
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeJoin(Paint.Join.MITER);
- mPaint.setStrokeCap(Paint.Cap.BUTT);
- mPaint.setStrokeWidth(mBarThickness);
-
- mMaxCutForBarSize = (float) (mBarThickness / 2 * Math.cos(ARROW_HEAD_ANGLE));
- }
-
- public boolean isLayoutRtl() {
- return false;
- }
-
- /**
- * If set, canvas is flipped when progress reached to end and going back to start.
- */
- protected void setVerticalMirror(boolean verticalMirror) {
- mVerticalMirror = verticalMirror;
- }
-
- @Override
- public void draw(Canvas canvas) {
- Rect bounds = getBounds();
- final boolean isRtl = isLayoutRtl();
- // Interpolated widths of arrow bars
- final float arrowSize = lerp(mBarSize, mTopBottomArrowSize, mProgress);
- final float middleBarSize = lerp(mBarSize, mMiddleArrowSize, mProgress);
- // Interpolated size of middle bar
- final float middleBarCut = Math.round(lerp(0, mMaxCutForBarSize, mProgress));
- // The rotation of the top and bottom bars (that make the arrow head)
- final float rotation = lerp(0, ARROW_HEAD_ANGLE, mProgress);
-
- // The whole canvas rotates as the transition happens
- final float canvasRotate = lerp(isRtl ? 0 : -180, isRtl ? 180 : 0, mProgress);
- final float arrowWidth = Math.round(arrowSize * Math.cos(rotation));
- final float arrowHeight = Math.round(arrowSize * Math.sin(rotation));
-
-
- mPath.rewind();
- final float topBottomBarOffset = lerp(mBarGap + mBarThickness, -mMaxCutForBarSize,
- mProgress);
-
- final float arrowEdge = -middleBarSize / 2;
- // draw middle bar
- mPath.moveTo(arrowEdge + middleBarCut, 0);
- mPath.rLineTo(middleBarSize - middleBarCut * 2, 0);
-
- // bottom bar
- mPath.moveTo(arrowEdge, topBottomBarOffset);
- mPath.rLineTo(arrowWidth, arrowHeight);
-
- // top bar
- mPath.moveTo(arrowEdge, -topBottomBarOffset);
- mPath.rLineTo(arrowWidth, -arrowHeight);
-
- mPath.close();
-
- canvas.save();
- // Rotate the whole canvas if spinning, if not, rotate it 180 to get
- // the arrow pointing the other way for RTL.
- canvas.translate(bounds.centerX(), mCenterOffset);
- if (mSpin) {
- canvas.rotate(canvasRotate * ((mVerticalMirror ^ isRtl) ? -1 : 1));
- } else if (isRtl) {
- canvas.rotate(180);
- }
- canvas.drawPath(mPath, mPaint);
-
- canvas.restore();
- }
-
- @Override
- public void setAlpha(int i) {
- mPaint.setAlpha(i);
- }
-
- @Override
- public boolean isAutoMirrored() {
- // Draws rotated 180 degrees in RTL mode.
- return true;
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mPaint.setColorFilter(colorFilter);
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mSize;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mSize;
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- public float getProgress() {
- return mProgress;
- }
-
- public void setProgress(float progress) {
- if (progress == 1f) {
- setVerticalMirror(true);
- } else if (progress == 0f) {
- setVerticalMirror(false);
- }
- mProgress = progress;
- invalidateSelf();
- }
-
- /**
- * Linear interpolate between a and b with parameter t.
- */
- private static float lerp(float a, float b, float t) {
- return a + (b - a) * t;
- }
-}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/ui/FabDrawable.java b/car-support-lib/src/android/support/car/ui/FabDrawable.java
deleted file mode 100644
index 100df67..0000000
--- a/car-support-lib/src/android/support/car/ui/FabDrawable.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.animation.DecelerateInterpolator;
-
-/**
- * Custom drawable that can be used as the background for fabs.
- *
- * When not focused or pressed, the fab will be a solid circle of the color specified with
- * {@link #setFabColor(int)}. When it is pressed or focused, the fab will grow or shrink
- * and it will gain a stroke that has the color specified with {@link #setStrokeColor(int)}.
- *
- * {@link #FabDrawable(android.content.Context)} provides a quick way to use fab drawable using
- * default values for size and animation values provided for consistency.
- *
- * {@link #FabDrawable(int, int, int)} can also be used for added customization.
- * @hide
- */
-public class FabDrawable extends Drawable {
- private final int mFabGrowth;
- private final int mStrokeWidth;
- private final Paint mFabPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final Paint mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final ValueAnimator mStrokeAnimator;
-
- private boolean mStrokeAnimatorIsReversing;
- private int mFabRadius;
- private int mStrokeRadius;
- private Outline mOutline;
-
- /**
- * Default constructor to provide consistent fab values across uses.
- */
- public FabDrawable(Context context) {
- this(context.getResources().getDimensionPixelSize(R.dimen.car_fab_focused_growth),
- context.getResources().getDimensionPixelSize(R.dimen.car_fab_focused_stroke_width),
- context.getResources().getInteger(R.integer.car_fab_animation_duration));
- }
-
- /**
- * Custom constructor allows extra customization of the fab's behavior.
- *
- * @param fabGrowth The amount that the fab should change by when it is focused in pixels.
- * @param strokeWidth The width of the stroke when the fab is focused in pixels.
- * @param duration The animation duration for the growth of the fab and stroke.
- */
- public FabDrawable(int fabGrowth, int strokeWidth, int duration) {
- if (fabGrowth < 0) {
- throw new IllegalArgumentException("Fab growth must be >= 0.");
- } else if (fabGrowth > strokeWidth) {
- throw new IllegalArgumentException("Fab growth must be <= strokeWidth.");
- } else if (strokeWidth < 0) {
- throw new IllegalArgumentException("Stroke width must be >= 0.");
- }
- mFabGrowth = fabGrowth;
- mStrokeWidth = strokeWidth;
- mStrokeAnimator = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
- mStrokeAnimator.setInterpolator(new DecelerateInterpolator());
- mStrokeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- updateRadius();
- }
- });
- }
-
- /**
- * @param color The primary color of the fab. It will be the entire fab color when not selected
- * or pressed and will be the color of the interior circle when selected
- * or pressed.
- */
- public void setFabColor(int color) {
- mFabPaint.setColor(color);
- }
-
- /**
- * @param color The color of the stroke on the fab that appears when the fab is selected
- * or pressed.
- */
- public void setStrokeColor(int color) {
- mStrokePaint.setColor(color);
- }
-
- /**
- * Default implementation of {@link #setFabAndStrokeColor(int, float)} with valueMultiplier
- * set to 0.9.
- */
- public void setFabAndStrokeColor(int color) {
- setFabAndStrokeColor(color, 0.9f);
- }
-
- /**
- * @param color The primary color of the fab.
- * @param valueMultiplier The hsv value multiplier that will be set as the stroke color.
- */
- public void setFabAndStrokeColor(int color, float valueMultiplier) {
- setFabColor(color);
- float[] hsv = new float[3];
- Color.colorToHSV(color, hsv);
- hsv[2] *= valueMultiplier;
- setStrokeColor(Color.HSVToColor(hsv));
- }
-
- @Override
- protected boolean onStateChange(int[] stateSet) {
- boolean superChanged = super.onStateChange(stateSet);
-
- boolean focused = false;
- boolean pressed = false;
-
- for (int state : stateSet) {
- if (state == android.R.attr.state_focused) {
- focused = true;
- } if (state == android.R.attr.state_pressed) {
- pressed = true;
- }
- }
-
- if ((focused || pressed) && mStrokeAnimatorIsReversing) {
- mStrokeAnimator.start();
- mStrokeAnimatorIsReversing = false;
- } else if (!(focused || pressed) && !mStrokeAnimatorIsReversing) {
- mStrokeAnimator.reverse();
- mStrokeAnimatorIsReversing = true;
- }
-
- return superChanged || focused;
- }
-
- @Override
- public void draw(Canvas canvas) {
- int cx = canvas.getWidth() / 2;
- int cy = canvas.getHeight() / 2;
-
- canvas.drawCircle(cx, cy, mStrokeRadius, mStrokePaint);
- canvas.drawCircle(cx, cy, mFabRadius, mFabPaint);
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- updateRadius();
- }
-
- @Override
- public void setAlpha(int alpha) {
- mFabPaint.setAlpha(alpha);
- mStrokePaint.setAlpha(alpha);
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mFabPaint.setColorFilter(colorFilter);
- mStrokePaint.setColorFilter(colorFilter);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.OPAQUE;
- }
-
- @Override
- public void getOutline(Outline outline) {
- mOutline = outline;
- updateOutline();
- }
-
- @Override
- public boolean isStateful() {
- return true;
- }
-
- private void updateRadius() {
- int normalRadius = Math.min(getBounds().width(), getBounds().height()) / 2 - mStrokeWidth;
- float fraction = mStrokeAnimator.getAnimatedFraction();
- mStrokeRadius = (int) (normalRadius + (mStrokeWidth * fraction));
- mFabRadius = (int) (normalRadius + (mFabGrowth * fraction));
- updateOutline();
- invalidateSelf();
- }
-
- private void updateOutline() {
- int cx = getBounds().width() / 2;
- int cy = getBounds().height() / 2;
- if (mOutline != null) {
- mOutline.setRoundRect(
- cx - mStrokeRadius,
- cy - mStrokeRadius,
- cx + mStrokeRadius,
- cy + mStrokeRadius,
- mStrokeRadius);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/GroupingCursorRecyclerViewAdapter.java b/car-support-lib/src/android/support/car/ui/GroupingCursorRecyclerViewAdapter.java
deleted file mode 100644
index 08f64a2..0000000
--- a/car-support-lib/src/android/support/car/ui/GroupingCursorRecyclerViewAdapter.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Maintains a list that groups adjacent items sharing the same value of
- * a "group-by" field. The list has three types of elements: stand-alone, group header and group
- * child. Groups are collapsible and collapsed by default.
- * @hide
- */
-public abstract class GroupingCursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder>
- extends CursorRecyclerViewAdapter<VH> {
- private static final String TAG = "CAR.UI.GroupingCursorRecyclerViewAdapter";
-
- public static final int VIEW_TYPE_STANDALONE = 0;
- public static final int VIEW_TYPE_GROUP_HEADER = 1;
- public static final int VIEW_TYPE_IN_GROUP = 2;
-
- /**
- * Build all groups based on grouping rules given cursor and calls {@link #addGroup} for
- * each of them.
- */
- protected abstract void buildGroups(Cursor cursor);
-
- protected abstract VH onCreateStandAloneViewHolder(Context context, ViewGroup parent);
- protected abstract void onBindStandAloneViewHolder(VH holder, Context context, Cursor cursor);
-
- protected abstract VH onCreateGroupViewHolder(Context context, ViewGroup parent);
- protected abstract void onBindGroupViewHolder(VH holder, Context context, Cursor cursor,
- int groupSize, boolean expanded);
-
- protected abstract VH onCreateChildViewHolder(Context context, ViewGroup parent);
- protected abstract void onBindChildViewHolder(VH holder, Context context, Cursor cursor);
-
- private int mCount;
- private List<GroupMetadata> mGroupMetadata;
-
- public GroupingCursorRecyclerViewAdapter(Context context, Cursor cursor) {
- super(context, cursor);
- mGroupMetadata = new ArrayList<>();
- resetGroup();
- }
-
- @Override
- public Cursor swapCursor(Cursor newCursor) {
- if (newCursor != mCursor) {
- resetGroup();
- if (newCursor != null) {
- buildGroups(newCursor);
- rebuildGroupMetadata();
- }
- }
- return super.swapCursor(newCursor);
- }
-
- @Override
- public int getItemCount() {
- if (mCursor != null && mCount != -1) {
- return mCount;
- }
- return 0;
- }
-
- @Override
- public long getItemId(int position) {
- Cursor cursor = getItem(position);
- if (cursor != null) {
- return cursor.getLong(mRowIdColumn);
- }
- return 0;
- }
-
- @Override
- public int getItemViewType(int position) {
- return getPositionMetadata(position).itemType;
- }
-
- public Cursor getItem(int position) {
- if (mCursor == null) {
- return null;
- }
-
- PositionMetadata pMetadata = getPositionMetadata(position);
- if (!mCursor.moveToPosition(pMetadata.cursorPosition)) {
- throw new IllegalStateException(
- "can't move cursor to position " + pMetadata.cursorPosition);
- }
- return mCursor;
- }
-
- @Override
- public VH onCreateViewHolder(ViewGroup parent, int viewType) {
- switch (viewType) {
- case VIEW_TYPE_STANDALONE:
- return onCreateStandAloneViewHolder(mContext, parent);
- case VIEW_TYPE_GROUP_HEADER:
- return onCreateGroupViewHolder(mContext, parent);
- case VIEW_TYPE_IN_GROUP:
- return onCreateChildViewHolder(mContext, parent);
- }
- Log.e(TAG, "Unknown viewType. Returning null ViewHolder");
- return null;
- }
-
- @Override
- public void onBindViewHolder(VH holder, int position) {
- PositionMetadata pMetadata = getPositionMetadata(position);
- mCursor.moveToPosition(pMetadata.cursorPosition);
- switch (pMetadata.itemType) {
- case VIEW_TYPE_STANDALONE:
- onBindStandAloneViewHolder(holder, mContext, mCursor);
- break;
- case VIEW_TYPE_GROUP_HEADER:
- onBindGroupViewHolder(holder, mContext, mCursor, pMetadata.gMetadata.itemNumber,
- pMetadata.gMetadata.isExpanded());
- break;
- case VIEW_TYPE_IN_GROUP:
- onBindChildViewHolder(holder, mContext, mCursor);
- break;
- }
- }
-
- public boolean toggleGroup(int position) {
- PositionMetadata pMetadata = getPositionMetadata(position);
- if (pMetadata.itemType != VIEW_TYPE_GROUP_HEADER) {
- return false;
- }
-
- pMetadata.gMetadata.isExpanded = !pMetadata.gMetadata.isExpanded;
- rebuildGroupMetadata();
- notifyDataSetChanged();
- return true;
- }
-
- /**
- * Records information about grouping in the list. Should only be called by the overridden
- * {@link #buildGroups} method.
- */
- protected void addGroup(int cursorPosition, int size, boolean expanded) {
- mGroupMetadata.add(GroupMetadata.obtain(cursorPosition, size, expanded));
- }
-
- private void resetGroup() {
- mCount = -1;
- mGroupMetadata.clear();
- }
-
- private void rebuildGroupMetadata() {
- int currentPos = 0;
- for (int groupIndex = 0; groupIndex < mGroupMetadata.size(); groupIndex++) {
- GroupMetadata gMetadata = mGroupMetadata.get(groupIndex);
- gMetadata.offsetInList = currentPos;
- currentPos += gMetadata.getActualSize();
- }
- mCount = currentPos;
- }
-
- private PositionMetadata getPositionMetadata(int position) {
- int left = 0;
- int right = mGroupMetadata.size() - 1;
- int mid;
- GroupMetadata midItem;
-
- while (left <= right) {
- mid = (right - left) / 2 + left;
- midItem = mGroupMetadata.get(mid);
-
- if (position > midItem.offsetInList + midItem.getActualSize() - 1) {
- left = mid + 1;
- continue;
- }
-
- if (position < midItem.offsetInList) {
- right = mid - 1;
- continue;
- }
-
- int cursorOffset = midItem.offsetInCursor + (position - midItem.offsetInList);
- int viewType;
- if (midItem.offsetInList == position) {
- if (midItem.isStandAlone()) {
- viewType = VIEW_TYPE_STANDALONE;
- } else {
- viewType = VIEW_TYPE_GROUP_HEADER;
- }
- } else {
- viewType = VIEW_TYPE_IN_GROUP;
- // Offset cursorOffset by 1, because the group_header and the first child
- // will share the same cursor.
- cursorOffset--;
- }
- return new PositionMetadata(viewType, cursorOffset, midItem);
- }
-
- throw new IllegalStateException(
- "illegal position " + position + ", total size is " + mCount);
- }
-
- /**
- * Information about where groups are located in the list, how large they are
- * and whether they are expanded.
- */
- protected static class GroupMetadata {
- private int offsetInList;
- private int offsetInCursor;
- private int itemNumber;
- private boolean isExpanded;
-
- static GroupMetadata obtain(int offset, int itemNumber, boolean isExpanded) {
- GroupMetadata gm = new GroupMetadata();
- gm.offsetInCursor = offset;
- gm.itemNumber = itemNumber;
- gm.isExpanded = isExpanded;
- return gm;
- }
-
- public boolean isExpanded() {
- return !isStandAlone() && isExpanded;
- }
-
- public boolean isStandAlone() {
- return itemNumber == 1;
- }
-
- public int getActualSize() {
- if (!isExpanded()) {
- return 1;
- } else {
- return itemNumber + 1;
- }
- }
- }
-
- protected static class PositionMetadata {
- int itemType;
- int cursorPosition;
- GroupMetadata gMetadata;
-
- public PositionMetadata(int itemType, int cursorPosition, GroupMetadata gMetadata) {
- this.itemType = itemType;
- this.cursorPosition = cursorPosition;
- this.gMetadata = gMetadata;
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/GroupingRecyclerViewAdapter.java b/car-support-lib/src/android/support/car/ui/GroupingRecyclerViewAdapter.java
deleted file mode 100644
index 844f07f..0000000
--- a/car-support-lib/src/android/support/car/ui/GroupingRecyclerViewAdapter.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Maintains a list that groups adjacent items sharing the same value of
- * a "group-by" field. The list has three types of elements: stand-alone, group header and group
- * child. Groups are collapsible and collapsed by default.
- * @hide
- */
-public abstract class GroupingRecyclerViewAdapter<E, VH extends RecyclerView.ViewHolder>
- extends RecyclerView.Adapter<VH> {
- private static final String TAG = "CAR.UI.GroupingRecyclerViewAdapter";
-
- public static final int VIEW_TYPE_STANDALONE = 0;
- public static final int VIEW_TYPE_GROUP_HEADER = 1;
- public static final int VIEW_TYPE_IN_GROUP = 2;
-
- /**
- * Build all groups based on grouping rules given cursor and calls {@link #addGroup} for
- * each of them.
- */
- protected abstract void buildGroups(List<E> data);
-
- protected abstract VH onCreateStandAloneViewHolder(Context context, ViewGroup parent);
- protected abstract void onBindStandAloneViewHolder(
- VH holder, Context context, int positionInData);
-
- protected abstract VH onCreateGroupViewHolder(Context context, ViewGroup parent);
- protected abstract void onBindGroupViewHolder(VH holder, Context context, int positionInData,
- int groupSize, boolean expanded);
-
- protected abstract VH onCreateChildViewHolder(Context context, ViewGroup parent);
- protected abstract void onBindChildViewHolder(VH holder, Context context, int positionInData);
-
- protected Context mContext;
- protected List<E> mData;
-
- private int mCount;
- private List<GroupMetadata> mGroupMetadata;
-
- public GroupingRecyclerViewAdapter(Context context) {
- mContext = context;
- mGroupMetadata = new ArrayList<>();
- resetGroup();
- }
-
- public void setData(List<E> data) {
- mData = data;
- resetGroup();
- if (mData != null) {
- buildGroups(mData);
- rebuildGroupMetadata();
- }
- notifyDataSetChanged();
- }
-
- @Override
- public int getItemCount() {
- if (mData != null && mCount != -1) {
- return mCount;
- }
- return 0;
- }
-
- @Override
- public long getItemId(int position) {
- E item = getItem(position);
- if (item != null) {
- return item.hashCode();
- }
- return 0;
- }
-
- @Override
- public int getItemViewType(int position) {
- return getPositionMetadata(position).itemType;
- }
-
- public E getItem(int position) {
- if (mData == null) {
- return null;
- }
-
- PositionMetadata pMetadata = getPositionMetadata(position);
- return mData.get(pMetadata.positionInData);
- }
-
- @Override
- public VH onCreateViewHolder(ViewGroup parent, int viewType) {
- switch (viewType) {
- case VIEW_TYPE_STANDALONE:
- return onCreateStandAloneViewHolder(mContext, parent);
- case VIEW_TYPE_GROUP_HEADER:
- return onCreateGroupViewHolder(mContext, parent);
- case VIEW_TYPE_IN_GROUP:
- return onCreateChildViewHolder(mContext, parent);
- }
- Log.e(TAG, "Unknown viewType. Returning null ViewHolder");
- return null;
- }
-
- @Override
- public void onBindViewHolder(VH holder, int position) {
- PositionMetadata pMetadata = getPositionMetadata(position);
- switch (holder.getItemViewType()) {
- case VIEW_TYPE_STANDALONE:
- onBindStandAloneViewHolder(holder, mContext, pMetadata.positionInData);
- break;
- case VIEW_TYPE_GROUP_HEADER:
- onBindGroupViewHolder(holder, mContext, pMetadata.positionInData,
- pMetadata.gMetadata.itemNumber, pMetadata.gMetadata.isExpanded());
- break;
- case VIEW_TYPE_IN_GROUP:
- onBindChildViewHolder(holder, mContext, pMetadata.positionInData);
- break;
- }
- }
-
- public boolean toggleGroup(int positionInData, int positionOnUI) {
- PositionMetadata pMetadata = getPositionMetadata(positionInData);
- if (pMetadata.itemType != VIEW_TYPE_GROUP_HEADER) {
- return false;
- }
-
- pMetadata.gMetadata.isExpanded = !pMetadata.gMetadata.isExpanded;
- rebuildGroupMetadata();
- if (pMetadata.gMetadata.isExpanded) {
- notifyItemRangeInserted(positionOnUI + 1, pMetadata.gMetadata.itemNumber);
- } else {
- notifyItemRangeRemoved(positionOnUI + 1, pMetadata.gMetadata.itemNumber);
- }
- return true;
- }
-
- /**
- * Return True if the item on the given position is a group header and the group is expanded,
- * otherwise False.
- */
- public boolean isGroupExpanded(int position) {
- PositionMetadata pMetadata = getPositionMetadata(position);
- if (pMetadata.itemType != VIEW_TYPE_GROUP_HEADER) {
- return false;
- }
-
- return pMetadata.gMetadata.isExpanded();
- }
-
- /**
- * Records information about grouping in the list. Should only be called by the overridden
- * {@link #buildGroups} method.
- */
- protected void addGroup(int offset, int size, boolean expanded) {
- mGroupMetadata.add(GroupMetadata.obtain(offset, size, expanded));
- }
-
- private void resetGroup() {
- mCount = -1;
- mGroupMetadata.clear();
- }
-
- private void rebuildGroupMetadata() {
- int currentPos = 0;
- for (int groupIndex = 0; groupIndex < mGroupMetadata.size(); groupIndex++) {
- GroupMetadata gMetadata = mGroupMetadata.get(groupIndex);
- gMetadata.offsetInDisplayList = currentPos;
- currentPos += gMetadata.getActualSize();
- }
- mCount = currentPos;
- }
-
- private PositionMetadata getPositionMetadata(int position) {
- int left = 0;
- int right = mGroupMetadata.size() - 1;
- int mid;
- GroupMetadata midItem;
-
- while (left <= right) {
- mid = (right - left) / 2 + left;
- midItem = mGroupMetadata.get(mid);
-
- if (position > midItem.offsetInDisplayList + midItem.getActualSize() - 1) {
- left = mid + 1;
- continue;
- }
-
- if (position < midItem.offsetInDisplayList) {
- right = mid - 1;
- continue;
- }
-
- int cursorOffset = midItem.offsetInDataList + (position - midItem.offsetInDisplayList);
- int viewType;
- if (midItem.offsetInDisplayList == position) {
- if (midItem.isStandAlone()) {
- viewType = VIEW_TYPE_STANDALONE;
- } else {
- viewType = VIEW_TYPE_GROUP_HEADER;
- }
- } else {
- viewType = VIEW_TYPE_IN_GROUP;
- // Offset cursorOffset by 1, because the group_header and the first child
- // will share the same cursor.
- cursorOffset--;
- }
- return new PositionMetadata(viewType, cursorOffset, midItem);
- }
-
- throw new IllegalStateException(
- "illegal position " + position + ", total size is " + mCount);
- }
-
- /**
- * Information about where groups are located in the list, how large they are
- * and whether they are expanded.
- */
- protected static class GroupMetadata {
- private int offsetInDisplayList;
- private int offsetInDataList;
- private int itemNumber;
- private boolean isExpanded;
-
- static GroupMetadata obtain(int offset, int itemNumber, boolean isExpanded) {
- GroupMetadata gm = new GroupMetadata();
- gm.offsetInDataList = offset;
- gm.itemNumber = itemNumber;
- gm.isExpanded = isExpanded;
- return gm;
- }
-
- public boolean isExpanded() {
- return !isStandAlone() && isExpanded;
- }
-
- public boolean isStandAlone() {
- return itemNumber == 1;
- }
-
- public int getActualSize() {
- if (!isExpanded()) {
- return 1;
- } else {
- return itemNumber + 1;
- }
- }
- }
-
- protected static class PositionMetadata {
- int itemType;
- int positionInData;
- GroupMetadata gMetadata;
-
- public PositionMetadata(int itemType, int positionInData, GroupMetadata gMetadata) {
- this.itemType = itemType;
- this.positionInData = positionInData;
- this.gMetadata = gMetadata;
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/LogDecelerateInterpolator.java b/car-support-lib/src/android/support/car/ui/LogDecelerateInterpolator.java
deleted file mode 100644
index d5f7adb..0000000
--- a/car-support-lib/src/android/support/car/ui/LogDecelerateInterpolator.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.view.animation.Interpolator;
-
-/**
- * @hide
- */
-public class LogDecelerateInterpolator implements Interpolator {
-
- int mBase;
- int mDrift;
- final float mLogScale;
-
- public LogDecelerateInterpolator(int base, int drift) {
- mBase = base;
- mDrift = drift;
-
- mLogScale = 1f / computeLog(1, mBase, mDrift);
- }
-
- static float computeLog(float t, int base, int drift) {
- return (float) -Math.pow(base, -t) + 1 + (drift * t);
- }
-
- @Override
- public float getInterpolation(float t) {
- return computeLog(t, mBase, mDrift) * mLogScale;
- }
-}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/ui/MaxWidthLayout.java b/car-support-lib/src/android/support/car/ui/MaxWidthLayout.java
deleted file mode 100644
index cbb4d12..0000000
--- a/car-support-lib/src/android/support/car/ui/MaxWidthLayout.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Acts as a container to make the width of all its children not larger than the setup max width.
- *
- * To use MaxWidthLayout, put it as the outermost layout of all children you want to limit its max
- * width and set the maxWidth appropriately.
- * @hide
- */
-public class MaxWidthLayout extends FrameLayout {
-
- // If mMaxChildrenWidth == 0, it means that it doesn't set the max width, just use the current
- // width directly.
- private int mMaxChildrenWidth;
-
- public MaxWidthLayout(Context context) {
- super(context);
- initialize(context.obtainStyledAttributes(R.styleable.MaxWidthLayout));
- }
-
- public MaxWidthLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialize(context.obtainStyledAttributes(attrs, R.styleable.MaxWidthLayout));
- }
-
- public MaxWidthLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- initialize(context
- .obtainStyledAttributes(attrs, R.styleable.MaxWidthLayout, defStyleAttr, 0));
- }
-
- /**
- * Initialize MaxWidthLayout specific attributes and recycle the TypeArray.
- */
- private void initialize(TypedArray ta) {
- mMaxChildrenWidth = (int) (ta.getDimension(R.styleable.MaxWidthLayout_carMaxWidth, 0));
- ta.recycle();
- }
-
- /**
- * Re-measure the child width if it is greater than max width.
- */
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mMaxChildrenWidth == 0) {
- return;
- }
-
- final List<View> matchParentChildren = new ArrayList<View>();
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp.width == LayoutParams.MATCH_PARENT) {
- matchParentChildren.add(child);
- }
- }
- }
- for (int i = matchParentChildren.size() - 1; i >= 0; --i) {
- final View child = matchParentChildren.get(i);
- final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
- if (child.getMeasuredWidth() > mMaxChildrenWidth) {
- int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- mMaxChildrenWidth - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
- int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
- child.getMeasuredHeight(), MeasureSpec.EXACTLY);
- child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/PagedLayoutManager.java b/car-support-lib/src/android/support/car/ui/PagedLayoutManager.java
deleted file mode 100644
index f3dc94f..0000000
--- a/car-support-lib/src/android/support/car/ui/PagedLayoutManager.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.LinearSmoothScroller;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-/**
- * An extension of {@link LinearLayoutManager} that adds some helper methods for paging
- * such as whether or not it is at the top or bottom of a list and layout param checking.
- * @hide
- */
-public class PagedLayoutManager extends LinearLayoutManager {
- private static final String TAG = PagedLayoutManager.class.getSimpleName();
-
- private final LinearSmoothScroller mSmoothScrollerForDrag;
- private final LinearSmoothScroller mSmoothScrollerForNonDrag;
-
- private int mLastScrollPosition = 0;
- private boolean mScrollingEnabled = true;
- public Runnable mItemsChangedRunnable;
-
- public PagedLayoutManager(Context context) {
- super(context, VERTICAL, false);
- mSmoothScrollerForDrag = new SnapToStartSmoothScroller(context, true);
- mSmoothScrollerForNonDrag = new SnapToStartSmoothScroller(context, true);
- }
-
- public void setItemsChangedListener(Runnable runnable) {
- mItemsChangedRunnable = runnable;
- }
-
- @Override
- public void onItemsChanged(RecyclerView recyclerView) {
- super.onItemsChanged(recyclerView);
- if (mItemsChangedRunnable != null) {
- mItemsChangedRunnable.run();
- }
- }
-
- @Override
- protected int getExtraLayoutSpace(RecyclerView.State state) {
- return getHeight() - getPaddingTop() - getPaddingBottom();
- }
-
- @Override
- public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
- int position) {
- boolean forDrag = recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING;
- LinearSmoothScroller ss;
- if (forDrag) {
- ss = mSmoothScrollerForDrag;
- } else {
- ss = mSmoothScrollerForNonDrag;
- }
- ss.setTargetPosition(position);
- startSmoothScroll(ss);
- }
-
- public int getLastScrollPosition() {
- return mLastScrollPosition;
- }
-
- @Override
- public void scrollToPosition(int position) {
- super.scrollToPosition(position);
- mLastScrollPosition = position;
- }
-
- @Override
- public int scrollVerticallyBy(int dy, RecyclerView.Recycler r, RecyclerView.State s) {
- // Our isAtBottom will return true if the view is on screen the the margin extends below
- // the bottom. This will make it so that you can't scroll if only the margin is hanging
- // off the bottom.
- if (isAtBottom() && dy > 0) {
- return 0;
- }
- return super.scrollVerticallyBy(dy, r, s);
- }
-
- @Override
- public boolean canScrollVertically() {
- return mScrollingEnabled;
- }
-
- public void setScrollingEnabled(boolean enabled) {
- mScrollingEnabled = enabled;
- }
-
- public boolean isAtTop() {
- if (getChildCount() == 0 || getItemCount() == 0) {
- return true;
- }
- return findFirstCompletelyVisibleItemPosition() < 1;
- }
-
- public boolean isAtBottom() {
- if (getChildCount() == 0 || getItemCount() == 0) {
- return true;
- }
- return findLastCompletelyVisibleItemPosition() == getItemCount() - 1;
- }
-
- private class SnapToStartSmoothScroller extends LinearSmoothScroller {
- private static final int DURATION_MS = 500;
- private final Interpolator mInterpolator;
-
- public SnapToStartSmoothScroller(Context context, boolean forDrag) {
- super(context);
- int interpolator = forDrag ? android.R.interpolator.decelerate_quint :
- android.R.interpolator.fast_out_slow_in;
- mInterpolator = AnimationUtils.loadInterpolator(context, interpolator);
- }
-
- @Override
- protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
- int dy = calculateDyToMakeVisible(targetView, SNAP_TO_START);
- if (dy == 0) {
- Log.w(TAG, "Scroll distance is 0.");
- return;
- }
-
- action.update(0, -dy, DURATION_MS, mInterpolator);
- }
-
- @Override
- public PointF computeScrollVectorForPosition(int targetPosition) {
- return PagedLayoutManager.this
- .computeScrollVectorForPosition(targetPosition);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/PagedListView.java b/car-support-lib/src/android/support/car/ui/PagedListView.java
deleted file mode 100644
index 5c9b504..0000000
--- a/car-support-lib/src/android/support/car/ui/PagedListView.java
+++ /dev/null
@@ -1,544 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-
-/**
- * Custom {@link android.support.v7.widget.RecyclerView} that displays a list of items that
- * resembles a {@link android.widget.ListView} but also has page up and page down arrows
- * on the right side.
- * @hide
- */
-public class PagedListView extends FrameLayout {
- private static final String TAG = "PagedListView";
-
- /**
- * The amount of time after settling to wait before autoscrolling to the next page when the
- * user holds down a pagination button.
- */
- private static final int PAGINATION_HOLD_DELAY_MS = 400;
-
- private final CarRecyclerView mRecyclerView;
- private final CarLayoutManager mLayoutManager;
- private final PagedScrollBarView mScrollBarView;
- private final Handler mHandler = new Handler();
- private Decoration mDecor = new Decoration(getContext());
-
- /** Maximum number of pages to show. Values < 0 show all pages. */
- private int mMaxPages = -1;
- /** Number of visible rows per page */
- private int mRowsPerPage = -1;
-
- /**
- * Used to check if there are more items added to the list.
- */
- private int mLastItemCount = 0;
-
- private RecyclerView.Adapter<? extends RecyclerView.ViewHolder> mAdapter;
-
- private boolean mNeedsFocus;
- private OnScrollBarListener mOnScrollBarListener;
-
- /**
- * Interface for a {@link android.support.v7.widget.RecyclerView.Adapter} to cap the
- * number of items.
- * <p>NOTE: it is still up to the adapter to use maxItems in
- * {@link android.support.v7.widget.RecyclerView.Adapter#getItemCount()}.
- *
- * the recommended way would be with:
- * <pre>
- * @Override
- * public int getItemCount() {
- * return Math.min(super.getItemCount(), mMaxItems);
- * }
- * </pre>
- */
- public interface ItemCap {
- public static final int UNLIMITED = -1;
-
- /**
- * Sets the maximum number of items available in the adapter. A value less than '0'
- * means the list should not be capped.
- */
- void setMaxItems(int maxItems);
- }
-
- public PagedListView(Context context, AttributeSet attrs) {
- this(context, attrs, 0 /*defStyleAttrs*/, 0 /*defStyleRes*/);
- }
-
- public PagedListView(Context context, AttributeSet attrs, int defStyleAttrs) {
- this(context, attrs, defStyleAttrs, 0 /*defStyleRes*/);
- }
-
- public PagedListView(Context context, AttributeSet attrs, int defStyleAttrs, int defStyleRes) {
- super(context, attrs, defStyleAttrs, defStyleRes);
- TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.PagedListView, defStyleAttrs, defStyleRes);
- boolean rightGutterEnabled =
- a.getBoolean(R.styleable.PagedListView_rightGutterEnabled, false);
- LayoutInflater.from(context)
- .inflate(R.layout.car_paged_recycler_view, this /*root*/, true /*attachToRoot*/);
- if (rightGutterEnabled) {
- FrameLayout maxWidthLayout = (FrameLayout) findViewById(R.id.max_width_layout);
- LayoutParams params =
- (LayoutParams) maxWidthLayout.getLayoutParams();
- params.rightMargin = getResources().getDimensionPixelSize(R.dimen.car_card_margin);
- maxWidthLayout.setLayoutParams(params);
- }
- mRecyclerView = (CarRecyclerView) findViewById(R.id.recycler_view);
- boolean fadeLastItem = a.getBoolean(R.styleable.PagedListView_fadeLastItem, false);
- mRecyclerView.setFadeLastItem(fadeLastItem);
- boolean offsetRows = a.getBoolean(R.styleable.PagedListView_offsetRows, false);
- a.recycle();
-
- mMaxPages = getDefaultMaxPages();
-
- mLayoutManager = new CarLayoutManager(context);
- mLayoutManager.setOffsetRows(offsetRows);
- mLayoutManager.setItemsChangedListener(mItemsChangedListener);
- mRecyclerView.setLayoutManager(mLayoutManager);
- mRecyclerView.addItemDecoration(mDecor);
- mRecyclerView.setOnScrollListener(mOnScrollListener);
- mRecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 12);
- mRecyclerView.setItemAnimator(new CarItemAnimator(mLayoutManager));
-
- mScrollBarView = (PagedScrollBarView) findViewById(R.id.paged_scroll_view);
- mScrollBarView.setPaginationListener(new PagedScrollBarView.PaginationListener() {
- @Override
- public void onPaginate(int direction) {
- if (direction == PagedScrollBarView.PaginationListener.PAGE_UP) {
- mRecyclerView.pageUp();
- } else if (direction == PagedScrollBarView.PaginationListener.PAGE_DOWN) {
- mRecyclerView.pageDown();
- } else {
- Log.e(TAG, "Unknown pagination direction (" + direction + ")");
- }
- }
- });
-
- setAutoDayNightMode();
- updatePaginationButtons(false /*animate*/);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mHandler.removeCallbacks(mUpdatePaginationRunnable);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent e) {
- if (e.getAction() == MotionEvent.ACTION_DOWN) {
- // The user has interacted with the list using touch. All movements will now paginate
- // the list.
- mLayoutManager.setRowOffsetMode(CarLayoutManager.ROW_OFFSET_MODE_PAGE);
- }
- return super.onInterceptTouchEvent(e);
- }
-
- @Override
- public void requestChildFocus(View child, View focused) {
- super.requestChildFocus(child, focused);
- // The user has interacted with the list using the controller. Movements through the list
- // will now be one row at a time.
- mLayoutManager.setRowOffsetMode(CarLayoutManager.ROW_OFFSET_MODE_INDIVIDUAL);
- }
-
- public int positionOf(@Nullable View v) {
- if (v == null || v.getParent() != mRecyclerView) {
- return -1;
- }
- return mLayoutManager.getPosition(v);
- }
-
- @NonNull
- public CarRecyclerView getRecyclerView() {
- return mRecyclerView;
- }
-
- public void scrollToPosition(int position) {
- mLayoutManager.scrollToPosition(position);
-
- // Sometimes #scrollToPosition doesn't change the scroll state so we need to make sure
- // the pagination arrows actually get updated.
- mHandler.post(mUpdatePaginationRunnable);
- }
-
- /**
- * Sets the adapter for the list.
- * <p>It <em>must</em> implement {@link ItemCap}, otherwise, will throw
- * an {@link IllegalArgumentException}.
- */
- public void setAdapter(
- @NonNull RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter) {
- if (!(adapter instanceof ItemCap)) {
- throw new IllegalArgumentException("ERROR: adapter "
- + "[" + adapter.getClass().getCanonicalName() + "] MUST implement ItemCap");
- }
-
- mAdapter = adapter;
- mRecyclerView.setAdapter(adapter);
- tryUpdateMaxPages();
- }
-
- @NonNull
- public CarLayoutManager getLayoutManager() {
- return mLayoutManager;
- }
-
- @Nullable
- @SuppressWarnings("unchecked")
- public RecyclerView.Adapter<? extends RecyclerView.ViewHolder> getAdapter() {
- return mRecyclerView.getAdapter();
- }
-
- public void setMaxPages(int maxPages) {
- mMaxPages = maxPages;
- tryUpdateMaxPages();
- }
-
- public int getMaxPages() {
- return mMaxPages;
- }
-
- public void resetMaxPages() {
- mMaxPages = getDefaultMaxPages();
- }
-
- public void setDefaultItemDecoration(Decoration decor) {
- removeDefaultItemDecoration();
- mDecor = decor;
- addItemDecoration(mDecor);
- }
-
- public void removeDefaultItemDecoration() {
- mRecyclerView.removeItemDecoration(mDecor);
- }
-
- public void addItemDecoration(@NonNull RecyclerView.ItemDecoration decor) {
- mRecyclerView.addItemDecoration(decor);
- }
-
- public void removeItemDecoration(@NonNull RecyclerView.ItemDecoration decor) {
- mRecyclerView.removeItemDecoration(decor);
- }
-
- public void setAutoDayNightMode() {
- mScrollBarView.setAutoDayNightMode();
- mDecor.updateDividerColor();
- }
-
- public void setLightMode() {
- mScrollBarView.setLightMode();
- mDecor.updateDividerColor();
- }
-
- public void setDarkMode() {
- mScrollBarView.setDarkMode();
- mDecor.updateDividerColor();
- }
-
- public void setOnScrollBarListener(OnScrollBarListener listener) {
- mOnScrollBarListener = listener;
- }
-
- /** Returns the page the given position is on, starting with page 0. */
- public int getPage(int position) {
- if (mRowsPerPage == -1) {
- return -1;
- }
- return position / mRowsPerPage;
- }
-
- /** Returns the default number of pages the list should have */
- protected int getDefaultMaxPages() {
- // assume list shown in response to a click, so, reduce number of clicks by one
- //return ProjectionUtils.getMaxClicks(getContext().getContentResolver()) - 1;
- return 5;
- }
-
- private void tryUpdateMaxPages() {
- if (mAdapter == null) {
- return;
- }
-
- View firstChild = mLayoutManager.getChildAt(0);
- int firstRowHeight = firstChild == null ? 0 : firstChild.getHeight();
- mRowsPerPage = firstRowHeight == 0 ? 1 : getHeight() / firstRowHeight;
-
- int newMaxItems;
- if (mMaxPages < 0) {
- newMaxItems = -1;
- } else if (mMaxPages == 0) {
- // At the last click of 6 click limit, we show one more warning item at the top of menu.
- newMaxItems = mRowsPerPage + 1;
- } else {
- newMaxItems = mRowsPerPage * mMaxPages;
- }
-
- int originalCount = mAdapter.getItemCount();
- ((ItemCap) mAdapter).setMaxItems(newMaxItems);
- int newCount = mAdapter.getItemCount();
- if (newCount < originalCount) {
- mAdapter.notifyItemRangeChanged(newCount, originalCount);
- } else if (newCount > originalCount) {
- mAdapter.notifyItemInserted(originalCount);
- }
- }
-
- @Override
- public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- // if a late item is added to the top of the layout after the layout is stabilized, causing
- // the former top item to be pushed to the 2nd page, the focus will still be on the former
- // top item. Since our car layout manager tries to scroll the viewport so that the focused
- // item is visible, the view port will be on the 2nd page. That means the newly added item
- // will not be visible, on the first page.
-
- // what we want to do is: if the formerly focused item is the first one in the list, any
- // item added above it will make the focus to move to the new first item.
- // if the focus is not on the formerly first item, then we don't need to do anything. Let
- // the layout manager do the job and scroll the viewport so the currently focused item
- // is visible.
-
- // we need to calculate whether we want to request focus here, before the super call,
- // because after the super call, the first born might be changed.
- View focusedChild = mLayoutManager.getFocusedChild();
- View firstBorn = mLayoutManager.getChildAt(0);
-
- super.onLayout(changed, left, top, right, bottom);
-
- if (mAdapter != null) {
- int itemCount = mAdapter.getItemCount();
- // if () {
- Log.d(TAG, String.format(
- "onLayout hasFocus: %s, mLastItemCount: %s, itemCount: %s, focusedChild: " +
- "%s, firstBorn: %s, isInTouchMode: %s, mNeedsFocus: %s",
- hasFocus(), mLastItemCount, itemCount, focusedChild, firstBorn,
- isInTouchMode(), mNeedsFocus));
- // }
- tryUpdateMaxPages();
- // This is a workaround for missing focus because isInTouchMode() is not always
- // returning the right value.
- // This is okay for the Engine release since focus is always showing.
- // However, in Tala and Fender, we want to show focus only when the user uses
- // hardware controllers, so we need to revisit this logic. b/22990605.
- if (mNeedsFocus && itemCount > 0) {
- if (focusedChild == null) {
- requestFocusFromTouch();
- }
- mNeedsFocus = false;
- }
- if (itemCount > mLastItemCount && focusedChild == firstBorn &&
- getContext().getResources().getBoolean(R.bool.has_wheel)) {
- requestFocusFromTouch();
- }
- mLastItemCount = itemCount;
- }
- updatePaginationButtons(true /*animate*/);
- }
-
- @Override
- public boolean requestFocus(int direction, Rect rect) {
- if (getContext().getResources().getBoolean(R.bool.has_wheel)) {
- mNeedsFocus = true;
- }
- return super.requestFocus(direction, rect);
- }
-
- public View findViewByPosition(int position) {
- return mLayoutManager.findViewByPosition(position);
- }
-
- private void updatePaginationButtons(boolean animate) {
- boolean isAtTop = mLayoutManager.isAtTop();
- boolean isAtBottom = mLayoutManager.isAtBottom();
- if (isAtTop && isAtBottom) {
- mScrollBarView.setVisibility(View.INVISIBLE);
- } else {
- mScrollBarView.setVisibility(View.VISIBLE);
- }
- mScrollBarView.setUpEnabled(!isAtTop);
- mScrollBarView.setDownEnabled(!isAtBottom);
-
- mScrollBarView.setParameters(
- mRecyclerView.computeVerticalScrollRange(),
- mRecyclerView.computeVerticalScrollOffset(),
- mRecyclerView.computeVerticalScrollExtent(),
- animate);
- invalidate();
- }
-
- private final RecyclerView.OnScrollListener mOnScrollListener =
- new RecyclerView.OnScrollListener() {
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (mOnScrollBarListener != null) {
- if (!mLayoutManager.isAtTop() && mLayoutManager.isAtBottom()) {
- mOnScrollBarListener.onReachBottom();
- }
- if (mLayoutManager.isAtTop() || !mLayoutManager.isAtBottom()) {
- mOnScrollBarListener.onLeaveBottom();
- }
- }
- updatePaginationButtons(false);
- }
-
- @Override
- public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
- if (newState == RecyclerView.SCROLL_STATE_IDLE) {
- mHandler.postDelayed(mPaginationRunnable, PAGINATION_HOLD_DELAY_MS);
- }
- }
- };
- private final Runnable mPaginationRunnable = new Runnable() {
- @Override
- public void run() {
- boolean upPressed = mScrollBarView.isUpPressed();
- boolean downPressed = mScrollBarView.isDownPressed();
- if (upPressed && downPressed) {
- // noop
- } else if (upPressed) {
- mRecyclerView.pageUp();
- } else if (downPressed) {
- mRecyclerView.pageDown();
- }
- }
- };
-
- private final Runnable mUpdatePaginationRunnable = new Runnable() {
- @Override
- public void run() {
- updatePaginationButtons(true /*animate*/);
- }
- };
-
- private final CarLayoutManager.OnItemsChangedListener mItemsChangedListener =
- new CarLayoutManager.OnItemsChangedListener() {
- @Override
- public void onItemsChanged() {
- updatePaginationButtons(true /*animate*/);
- }
- };
-
- abstract static public class OnScrollBarListener {
- public void onReachBottom() {}
- public void onLeaveBottom() {}
- }
-
- public static class Decoration extends RecyclerView.ItemDecoration {
- protected final Paint mPaint;
- protected final int mDividerHeight;
- protected final Context mContext;
-
-
- public Decoration(Context context) {
- mContext = context;
- mPaint = new Paint();
- updateDividerColor();
- mDividerHeight = mContext.getResources()
- .getDimensionPixelSize(R.dimen.car_divider_height);
- }
-
- @Override
- public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
- final int left = getLeft(parent.getChildAt(0));
- final int right = parent.getWidth() - parent.getPaddingRight();
- int top;
- int bottom;
-
- c.drawRect(left, 0, right, mDividerHeight, mPaint);
-
- final int childCount = parent.getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = parent.getChildAt(i);
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
- .getLayoutParams();
- bottom = child.getBottom() - params.bottomMargin;
- top = bottom - mDividerHeight;
- if (top > 0) {
- c.drawRect(left, top, right, bottom, mPaint);
- }
- }
- }
-
- /**
- * Updates the list divider color which may have changed due to a day night transition.
- */
- public void updateDividerColor() {
- mPaint.setColor(mContext.getResources().getColor(R.color.car_list_divider));
- }
-
- /**
- * Find the left edge of the decoration line. It should be left aligned with the left edge
- * of the first {@link android.widget.TextView}.
- */
- private int getLeft(View root) {
- if (root == null) {
- return 0;
- }
- View view = findTextView(root);
- if (view == null) {
- view = root;
- }
- int left = 0;
- while (view != null && view != root) {
- left += view.getLeft();
- view = (View) view.getParent();
- }
- return left;
- }
-
- private TextView findTextView(View root) {
- if (root == null) {
- return null;
- }
- if (root instanceof TextView) {
- return (TextView) root;
- }
- if (root instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) root;
- final int childCount = parent.getChildCount();
- for(int i = 0; i < childCount; i++) {
- TextView tv = findTextView(parent.getChildAt(i));
- if (tv != null) {
- return tv;
- }
- }
- }
- return null;
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/PagedScrollBarView.java b/car-support-lib/src/android/support/car/ui/PagedScrollBarView.java
deleted file mode 100644
index d6ddfb6..0000000
--- a/car-support-lib/src/android/support/car/ui/PagedScrollBarView.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.content.Context;
-import android.graphics.PorterDuff;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-/**
- * A custom view to provide list scroll behaviour -- up/down buttons and scroll indicator.
- *
- * @hide
- */
-public class PagedScrollBarView extends FrameLayout
- implements View.OnClickListener, View.OnLongClickListener {
- private static final float BUTTON_DISABLED_ALPHA = 0.2f;
-
- /**
- * Listener for when the list should paginate.
- */
- public interface PaginationListener {
- int PAGE_UP = 0;
- int PAGE_DOWN = 1;
-
- /** Called when the linked view should be paged in the given direction */
- void onPaginate(int direction);
- }
-
- private final ImageView mUpButton;
- private final ImageView mDownButton;
- private final ImageView mScrollThumb;
- /** The "filler" view between the up and down buttons */
- private final View mFiller;
- private final Interpolator mPaginationInterpolator = new AccelerateDecelerateInterpolator();
- private final int mMinThumbLength;
- private final int mMaxThumbLength;
- private PaginationListener mPaginationListener;
-
- public PagedScrollBarView(
- Context context, AttributeSet attrs) {
- this(context, attrs, 0 /*defStyleAttrs*/, 0 /*defStyleRes*/);
- }
-
- public PagedScrollBarView(
- Context context, AttributeSet attrs, int defStyleAttrs) {
- this(context, attrs, defStyleAttrs, 0 /*defStyleRes*/);
- }
-
- public PagedScrollBarView(
- Context context, AttributeSet attrs, int defStyleAttrs, int defStyleRes) {
- super(context, attrs, defStyleAttrs, defStyleRes);
-
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(
- R.layout.car_paged_scrollbar_buttons, this /*root*/, true /*attachToRoot*/);
-
- mUpButton = (ImageView) findViewById(R.id.page_up);
- mUpButton.setImageDrawable(context.getDrawable(R.drawable.ic_up_button));
- mUpButton.setOnClickListener(this);
- mUpButton.setOnLongClickListener(this);
- mDownButton = (ImageView) findViewById(R.id.page_down);
- mDownButton.setImageDrawable(context.getDrawable(R.drawable.ic_down_button));
- mDownButton.setOnClickListener(this);
- mDownButton.setOnLongClickListener(this);
-
- mScrollThumb = (ImageView) findViewById(R.id.scrollbar_thumb);
- mScrollThumb.setAlpha(0.5f);
-
- mFiller = findViewById(R.id.filler);
-
- mMinThumbLength = getResources().getDimensionPixelSize(R.dimen.min_thumb_height);
- mMaxThumbLength = getResources().getDimensionPixelSize(R.dimen.max_thumb_height);
-
- if (!context.getResources().getBoolean(R.bool.car_true_for_touch)) {
- // Don't show the pagination buttons if there isn't touch.
- mUpButton.setVisibility(View.GONE);
- mDownButton.setVisibility(View.GONE);
- }
- }
-
- @Override
- public void onClick(View v) {
- dispatchPageClick(v);
- }
-
- @Override
- public boolean onLongClick(View v) {
- dispatchPageClick(v);
- return true;
- }
-
- public void setPaginationListener(PaginationListener listener) {
- mPaginationListener = listener;
- }
-
- /** Returns {@code true} if the "up" button is pressed */
- public boolean isUpPressed() {
- return mUpButton.isPressed();
- }
-
- /** Returns {@code true} if the "down" button is pressed */
- public boolean isDownPressed() {
- return mDownButton.isPressed();
- }
-
- /** Sets the range, offset and extent of the scroll bar. See {@link android.view.View}. */
- protected void setParameters(int range, int offset, int extent, boolean animate) {
- final int size = mFiller.getHeight() - mFiller.getPaddingTop() - mFiller.getPaddingBottom();
-
- int thumbLength = extent * size / range;
- thumbLength = Math.max(Math.min(thumbLength, mMaxThumbLength), mMinThumbLength);
-
- int thumbOffset = size - thumbLength;
- if (isDownEnabled()) {
- // We need to adjust the offset so that it fits into the possible space inside the
- // filler with regarding to the constraints set by mMaxThumbLength and mMinThumbLength.
- thumbOffset = (size - thumbLength) * offset / range;
- }
-
- // Sets the size of the thumb and request a redraw if needed.
- final ViewGroup.LayoutParams lp = mScrollThumb.getLayoutParams();
- if (lp.height != thumbLength) {
- lp.height = thumbLength;
- mScrollThumb.requestLayout();
- }
-
- moveY(mScrollThumb, thumbOffset, animate);
- }
-
- /** Sets auto day/night mode */
- protected void setAutoDayNightMode() {
- int color = getResources().getColor(R.color.car_tint);
- mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
- mUpButton.setBackgroundResource(R.drawable.car_pagination_background);
- mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
- mDownButton.setBackgroundResource(R.drawable.car_pagination_background);
-
- mScrollThumb.setBackgroundColor(color);
- }
-
- /** Sets auto light mode */
- protected void setLightMode() {
- int color = getResources().getColor(R.color.car_tint_light);
- mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
- mUpButton.setBackgroundResource(R.drawable.car_pagination_background_light);
- mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
- mDownButton.setBackgroundResource(R.drawable.car_pagination_background_light);
-
- mScrollThumb.setBackgroundColor(color);
- }
-
- /** Sets auto dark mode */
- protected void setDarkMode() {
- int color = getResources().getColor(R.color.car_tint_dark);
- mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
- mUpButton.setBackgroundResource(R.drawable.car_pagination_background_dark);
- mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
- mDownButton.setBackgroundResource(R.drawable.car_pagination_background_dark);
-
- mScrollThumb.setBackgroundColor(color);
- }
-
- protected void setUpEnabled(boolean enabled) {
- mUpButton.setEnabled(enabled);
- mUpButton.setAlpha(enabled ? 1f : BUTTON_DISABLED_ALPHA);
- }
-
- protected void setDownEnabled(boolean enabled) {
- mDownButton.setEnabled(enabled);
- mDownButton.setAlpha(enabled ? 1f : BUTTON_DISABLED_ALPHA);
- }
-
- protected boolean isDownEnabled() {
- return mDownButton.isEnabled();
- }
-
- private void dispatchPageClick(View v) {
- final PaginationListener listener = mPaginationListener;
- if (listener == null) {
- return;
- }
-
- final int direction = (v.getId() == R.id.page_up)
- ? PaginationListener.PAGE_UP : PaginationListener.PAGE_DOWN;
- listener.onPaginate(direction);
- }
-
- /** Moves the given view to the specified 'y' position. */
- private void moveY(final View view, float newPosition, boolean animate) {
- final int duration = animate ? 200 : 0;
- view.animate()
- .y(newPosition)
- .setDuration(duration)
- .setInterpolator(mPaginationInterpolator)
- .start();
- }
-}
\ No newline at end of file
diff --git a/car-support-lib/src/android/support/car/ui/PathClippingView.java b/car-support-lib/src/android/support/car/ui/PathClippingView.java
deleted file mode 100644
index 812977a..0000000
--- a/car-support-lib/src/android/support/car/ui/PathClippingView.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.graphics.Path;
-
-/**
- * Interface for a view that can apply a clip given to it in the form of a {@link android.graphics.Path}.
- * @hide
- */
-public interface PathClippingView {
- /**
- * Notify listener of a new clip path.
- * @param clipPath Clipping path. If {@code null}, clip should no longer be performed.
- */
- void setClipPath(Path clipPath);
-
-}
diff --git a/car-support-lib/src/android/support/car/ui/QuantumInterpolator.java b/car-support-lib/src/android/support/car/ui/QuantumInterpolator.java
deleted file mode 100644
index cc10d4a..0000000
--- a/car-support-lib/src/android/support/car/ui/QuantumInterpolator.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-import android.animation.TimeInterpolator;
-
-/**
- * Interpolator that can animate any of the quantum curves.
- * You can also specify
- * @hide
- */
-public class QuantumInterpolator implements TimeInterpolator {
-
- /**
- * Lookup table values.
- * Generated using a Bezier curve from (0,0) to (1,1) with control points:
- * P0 (0,0)
- * P1 (0.4, 0)
- * P2 (0.2, 1.0)
- * P3 (1.0, 1.0)
- *
- * Values sampled with x at regular intervals between 0 and 1.
- *
- * These values were generated using:
- * ./scripts/bezier_interpolator_values_gen.py 0.4 0.2
- */
- public static final float[] FAST_OUT_SLOW_IN = new float[] {
- 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
- 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
- 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
- 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
- 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
- 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
- 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
- 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
- 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
- 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
- };
-
- /**
- * These values were generated using:
- * ./scripts/bezier_interpolator_values_gen.py 0.0 0.2
- */
- public static final float[] LINEAR_OUT_SLOW_IN = new float[] {
- 0.0029f, 0.043f, 0.0785f, 0.1147f, 0.1476f, 0.1742f, 0.2024f, 0.2319f, 0.2575f, 0.2786f,
- 0.3055f, 0.3274f, 0.3498f, 0.3695f, 0.3895f, 0.4096f, 0.4299f, 0.4474f, 0.4649f,
- 0.4824f, 0.5f, 0.5176f, 0.5322f, 0.5468f, 0.5643f, 0.5788f, 0.5918f, 0.6048f, 0.6191f,
- 0.6333f, 0.6446f, 0.6573f, 0.6698f, 0.6808f, 0.6918f, 0.704f, 0.7148f, 0.7254f, 0.7346f,
- 0.7451f, 0.7554f, 0.7655f, 0.7731f, 0.783f, 0.7916f, 0.8f, 0.8084f, 0.8166f, 0.8235f,
- 0.8315f, 0.8393f, 0.8459f, 0.8535f, 0.8599f, 0.8672f, 0.8733f, 0.8794f, 0.8853f,
- 0.8911f, 0.8967f, 0.9023f, 0.9077f, 0.9121f, 0.9173f, 0.9224f, 0.9265f, 0.9313f,
- 0.9352f, 0.9397f, 0.9434f, 0.9476f, 0.9511f, 0.9544f, 0.9577f, 0.9614f, 0.9644f,
- 0.9673f, 0.9701f, 0.9727f, 0.9753f, 0.9777f, 0.98f, 0.9818f, 0.9839f, 0.9859f, 0.9877f,
- 0.9891f, 0.9907f, 0.9922f, 0.9933f, 0.9946f, 0.9957f, 0.9966f, 0.9974f, 0.9981f,
- 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
- };
-
- /**
- * These values were generated using:
- * ./scripts/bezier_interpolator_values_gen.py 0.0 0.2
- */
- public static final float[] FAST_OUT_LINEAR_IN = new float[] {
- 0.0f, 0.0002f, 0.0008f, 0.0019f, 0.0032f, 0.0049f, 0.0069f, 0.0093f, 0.0119f, 0.0149f,
- 0.0182f, 0.0218f, 0.0257f, 0.0299f, 0.0344f, 0.0392f, 0.0443f, 0.0496f, 0.0552f,
- 0.0603f, 0.0656f, 0.0719f, 0.0785f, 0.0853f, 0.0923f, 0.0986f, 0.1051f, 0.1128f,
- 0.1206f, 0.1287f, 0.1359f, 0.1433f, 0.1519f, 0.1607f, 0.1696f, 0.1776f, 0.1857f,
- 0.1952f, 0.2048f, 0.2145f, 0.2232f, 0.2319f, 0.2421f, 0.2523f, 0.2627f, 0.2733f,
- 0.2826f, 0.2919f, 0.3027f, 0.3137f, 0.3247f, 0.3358f, 0.3469f, 0.3582f, 0.3695f,
- 0.3809f, 0.3924f, 0.4039f, 0.4154f, 0.427f, 0.4386f, 0.4503f, 0.4619f, 0.4751f, 0.4883f,
- 0.5f, 0.5117f, 0.5264f, 0.5381f, 0.5497f, 0.5643f, 0.5759f, 0.5904f, 0.6033f, 0.6162f,
- 0.6305f, 0.6446f, 0.6587f, 0.6698f, 0.6836f, 0.7f, 0.7134f, 0.7267f, 0.7425f, 0.7554f,
- 0.7706f, 0.7855f, 0.8f, 0.8143f, 0.8281f, 0.8438f, 0.8588f, 0.8733f, 0.8892f, 0.9041f,
- 0.9215f, 0.9344f, 0.9518f, 0.9667f, 0.9826f, 0.9993f
-
- };
-
- private final float[] mValues;
- private final float mStepSize;
- private final float mStartTime;
- private final float mEndTime;
-
- public QuantumInterpolator(float[] values, float pre, float during, float post) {
- super();
- mValues = values;
- mStepSize = 1.0f / (mValues.length - 1);
- mStartTime = pre / (pre + during + post);
- mEndTime = mStartTime + (during / (pre + during + post));
- }
-
- @Override
- public float getInterpolation(float input) {
- return getInterpolation(input, mStartTime, mEndTime);
- }
-
- public float getReverseInterpolation(float input) {
- return getInterpolation(input, 1 - mEndTime, 1 - mStartTime);
- }
-
- public float getInterpolation(float input, float startTime, float endTime) {
- if (input <= startTime) {
- return 0.0f;
- } else if (input >= endTime) {
- return 1.0f;
- }
-
- input = (input - startTime) / (endTime - startTime);
-
- int position = Math.min(
- (int)(input * (mValues.length - 1)),
- mValues.length - 2);
-
- float quantized = position * mStepSize;
- float difference = input - quantized;
- float weight = difference / mStepSize;
-
- return mValues[position] + weight * (mValues[position + 1] - mValues[position]);
- }
-}
diff --git a/car-support-lib/src/android/support/car/ui/ReversibleInterpolator.java b/car-support-lib/src/android/support/car/ui/ReversibleInterpolator.java
deleted file mode 100644
index ff9843d..0000000
--- a/car-support-lib/src/android/support/car/ui/ReversibleInterpolator.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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 android.support.car.ui;
-
-
-import android.animation.TimeInterpolator;
-import android.support.annotation.NonNull;
-
-/**
- * Interpolator that can provide custom interpolations for forward and reverse animations
- * @hide
- */
-public class ReversibleInterpolator {
-
- private final TimeInterpolator mForwardInterpolator;
- private final TimeInterpolator mReverseInterpolator;
-
- public ReversibleInterpolator(@NonNull TimeInterpolator forwardInterpolator,
- @NonNull TimeInterpolator reverseInterpolator) {
- mForwardInterpolator = forwardInterpolator;
- mReverseInterpolator = reverseInterpolator;
- }
-
- public float getForwardInterpolation(float input) {
- return mForwardInterpolator.getInterpolation(input);
- }
-
- public float getReverseInterpolation(float input) {
- return mReverseInterpolator.getInterpolation(input);
- }
-}
diff --git a/car-ui-provider/res/drawable-hdpi/ic_google.png b/car-ui-provider/res/drawable-hdpi/ic_google.png
deleted file mode 100644
index 6e79734..0000000
--- a/car-ui-provider/res/drawable-hdpi/ic_google.png
+++ /dev/null
Binary files differ
diff --git a/car-ui-provider/res/drawable-hdpi/ic_googleg.png b/car-ui-provider/res/drawable-hdpi/ic_googleg.png
deleted file mode 100644
index 0d4158f..0000000
--- a/car-ui-provider/res/drawable-hdpi/ic_googleg.png
+++ /dev/null
Binary files differ
diff --git a/car-ui-provider/res/drawable-mdpi/ic_google.png b/car-ui-provider/res/drawable-mdpi/ic_google.png
deleted file mode 100644
index b00365a..0000000
--- a/car-ui-provider/res/drawable-mdpi/ic_google.png
+++ /dev/null
Binary files differ
diff --git a/car-ui-provider/res/drawable-mdpi/ic_googleg.png b/car-ui-provider/res/drawable-mdpi/ic_googleg.png
deleted file mode 100644
index 4871a0f..0000000
--- a/car-ui-provider/res/drawable-mdpi/ic_googleg.png
+++ /dev/null
Binary files differ
diff --git a/car-ui-provider/res/drawable-xhdpi/ic_google.png b/car-ui-provider/res/drawable-xhdpi/ic_google.png
deleted file mode 100644
index db17d6f..0000000
--- a/car-ui-provider/res/drawable-xhdpi/ic_google.png
+++ /dev/null
Binary files differ
diff --git a/car-ui-provider/res/drawable-xhdpi/ic_googleg.png b/car-ui-provider/res/drawable-xhdpi/ic_googleg.png
deleted file mode 100644
index addab88..0000000
--- a/car-ui-provider/res/drawable-xhdpi/ic_googleg.png
+++ /dev/null
Binary files differ
diff --git a/car-ui-provider/res/drawable-xxhdpi/ic_google.png b/car-ui-provider/res/drawable-xxhdpi/ic_google.png
deleted file mode 100644
index a27a8bc..0000000
--- a/car-ui-provider/res/drawable-xxhdpi/ic_google.png
+++ /dev/null
Binary files differ
diff --git a/car-ui-provider/res/drawable-xxhdpi/ic_googleg.png b/car-ui-provider/res/drawable-xxhdpi/ic_googleg.png
deleted file mode 100644
index 0e89bbb..0000000
--- a/car-ui-provider/res/drawable-xxhdpi/ic_googleg.png
+++ /dev/null
Binary files differ
diff --git a/car-ui-provider/res/layout/car_activity.xml b/car-ui-provider/res/layout/car_activity.xml
deleted file mode 100644
index 041afd3..0000000
--- a/car-ui-provider/res/layout/car_activity.xml
+++ /dev/null
@@ -1,192 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_height="match_parent"
- android:layout_width="match_parent">
-
- <ImageView
- android:id="@+id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop" />
-
- <android.car.ui.provider.CarDrawerLayout
- android:id="@+id/drawer_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <FrameLayout
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <FrameLayout
- android:id="@+id/drawer"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="left"
- android:layout_marginEnd="96dp"
- android:background="@color/car_card"
- android:paddingTop="@dimen/lens_header_height" >
-
- <android.car.ui.provider.PagedListView
- android:id="@+id/list_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
-
- <ProgressBar
- android:id="@+id/progress"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_gravity="center"
- android:indeterminate="true" />
-
- <android.support.v7.widget.CardView
- android:id="@+id/truncated_list_card"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_list_truncated_list_card_height"
- android:layout_gravity="bottom"
- android:layout_marginLeft="@dimen/car_list_truncated_list_padding"
- android:layout_marginRight="@dimen/car_list_truncated_list_padding"
- android:layout_marginBottom="@dimen/car_list_truncated_list_padding"
- android:visibility="gone"
- app:cardBackgroundColor="@color/car_blue_grey_800"
- app:cardCornerRadius="@dimen/car_card_view_corner_radius"
- app:cardElevation="@dimen/car_card_view_elevation" >
-
- <TextView
- style="@style/CarTruncatedList"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="@dimen/car_list_truncated_list_icon_padding"
- android:drawablePadding="@dimen/car_list_truncated_list_drawable_padding"
- android:gravity="center_vertical"
- android:drawableLeft="@drawable/ic_remove_circle"
- android:text="@string/truncated_list" />
-
- </android.support.v7.widget.CardView>
- </FrameLayout>
- </android.car.ui.provider.CarDrawerLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/lens_header_height">
-
- <FrameLayout
- android:layout_width="@dimen/car_drawer_button_container_width"
- android:layout_height="match_parent">
-
- <ImageView
- android:id="@+id/car_drawer_button"
- android:layout_width="@dimen/car_drawer_header_menu_button_size"
- android:layout_height="@dimen/car_drawer_header_menu_button_size"
- android:background="@drawable/car_header_button_background"
- android:focusable="false"
- android:scaleType="center"
- android:layout_gravity="center" />
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/car_drawer_title_container"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" >
- <TextView
- android:id="@+id/car_drawer_title"
- style="@style/CarTitle.Light"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:ellipsize="end"
- android:focusable="false"
- android:gravity="center_vertical"
- android:singleLine="true"
- />
- </FrameLayout>
- <android.support.v7.widget.CardView
- android:id="@+id/car_search_box"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_marginBottom="16dp"
- android:layout_marginTop="16dp"
- android:gravity="center_vertical"
- app:cardBackgroundColor="@android:color/transparent" >
-
- <FrameLayout
- android:id="@+id/car_search_box_contents"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:animateLayoutChanges="false">
-
- <FrameLayout
- android:id="@+id/car_search_box_search_logo_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="16dp" >
-
- <ImageView
- android:id="@+id/car_search_box_search_logo"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:paddingTop="6dp" />
- </FrameLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/car_search_box_super_logo"
- android:layout_width="@dimen/car_list_item_icon_size_small"
- android:layout_height="@dimen/car_list_item_icon_size_small"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="24dp"
- android:scaleType="center" />
- <!--
- Use a 0x0 drawable for textSelectHandle; null drawable crashes, so does
- transparent color. Also set textCursorDrawable to null because this forces
- Android to render a cursor using the text color instead of not rendering
- one at all. See
- http://stackoverflow.com/questions/21397977/android-edit-text-cursor-is-not-visible
- -->
-
- <android.support.car.input.CarRestrictedEditText
- android:id="@+id/car_search_box_edit_text"
- style="@style/CarTitle.Dark"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical"
- android:imeOptions="actionSearch"
- android:paddingEnd="16dp"
- android:paddingStart="36dp"
- android:singleLine="true"
- android:textCursorDrawable="@null"
- android:textSelectHandle="@drawable/car_empty" />
-
- <FrameLayout
- android:id="@+id/car_search_box_end_view"
- android:layout_width="wrap_content"
- android:layout_height="match_parent" />
- </LinearLayout>
- </FrameLayout>
- </android.support.v7.widget.CardView>
- </LinearLayout>
-
-</FrameLayout>
diff --git a/car-ui-provider/res/layout/car_paged_recycler_view.xml b/car-ui-provider/res/layout/car_paged_recycler_view.xml
deleted file mode 100644
index a0f9537..0000000
--- a/car-ui-provider/res/layout/car_paged_recycler_view.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<!-- Cloned from car ui lib for use in CarUiProvider. Must be kept in sync. -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <android.car.ui.provider.MaxWidthLayout
- android:id="@+id/max_width_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/car_drawer_button_container_width">
- <android.car.ui.provider.CarRecyclerView
- android:id="@+id/recycler_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal"
- android:clipChildren="false"/>
- </android.car.ui.provider.MaxWidthLayout>
- <!-- The scroll bar should be drawn ontop of the centered recycler view-->
- <FrameLayout
- android:layout_width="@dimen/car_drawer_button_container_width"
- android:layout_height="match_parent">
- <android.car.ui.provider.PagedScrollBarView
- android:id="@+id/paged_scroll_view"
- android:layout_width="@dimen/car_paged_list_view_pagination_width"
- android:layout_height="match_parent"
- android:paddingBottom="16dp"
- android:paddingTop="16dp"
- android:layout_gravity="center_horizontal"
- android:visibility="invisible"/>
- </FrameLayout>
-</merge>
\ No newline at end of file
diff --git a/car-ui-provider/res/values-h600dp/dimens.xml b/car-ui-provider/res/values-h600dp/dimens.xml
deleted file mode 100644
index c972bad..0000000
--- a/car-ui-provider/res/values-h600dp/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <dimen name="car_drawer_header_height">192dp</dimen>
- <dimen name="car_drawer_header_menu_button_size">152dp</dimen>
- <dimen name="car_drawer_button_margin_right">12dp</dimen>
- <dimen name="lens_header_height">148dp</dimen>
-</resources>
diff --git a/car-ui-provider/res/values/dimens.xml b/car-ui-provider/res/values/dimens.xml
deleted file mode 100644
index 378e6f8..0000000
--- a/car-ui-provider/res/values/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <dimen name="car_drawer_header_menu_button_size">96dp</dimen>
-
- <!-- The top margin before the start of content in an application. -->
- <dimen name="lens_header_height">72dp</dimen>
-</resources>
diff --git a/car-ui-provider/res/values/strings.xml b/car-ui-provider/res/values/strings.xml
deleted file mode 100644
index c6a70d7..0000000
--- a/car-ui-provider/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <string name="app_name">CarUiProvider</string>
-</resources>
\ No newline at end of file
diff --git a/car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java b/car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java
deleted file mode 100644
index 5e70420..0000000
--- a/car-ui-provider/src/android/car/ui/provider/CarDrawerLayout.java
+++ /dev/null
@@ -1,1466 +0,0 @@
-/*
- * 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 android.car.ui.provider;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.car.ui.CarUiResourceLoader;
-import android.support.car.ui.QuantumInterpolator;
-import android.support.car.ui.R;
-import android.support.car.ui.ReversibleInterpolator;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewGroupCompat;
-import android.support.v4.widget.ViewDragHelper;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Acts as a top-level container for window content that allows for
- * interactive "drawer" views to be pulled out from the edge of the window.
- *
- * <p>Drawer positioning and layout is controlled using the <code>android:layout_gravity</code>
- * attribute on child views corresponding to which side of the view you want the drawer
- * to emerge from: left or right. (Or start/end on platform versions that support layout direction.)
- * </p>
- *
- * <p> To use CarDrawerLayout, add your drawer view as the first view in the CarDrawerLayout
- * element and set the <code>layout_gravity</code> appropriately. Drawers commonly use
- * <code>match_parent</code> for height with a fixed width. Add the content views as sibling views
- * after the drawer view.</p>
- *
- * <p>{@link DrawerListener} can be used to monitor the state and motion of drawer views.
- * Avoid performing expensive operations such as layout during animation as it can cause
- * stuttering; try to perform expensive operations during the {@link #STATE_IDLE} state.
- * {@link SimpleDrawerListener} offers default/no-op implementations of each callback method.</p>
- */
-public class CarDrawerLayout extends ViewGroup {
- /**
- * Indicates that any drawers are in an idle, settled state. No animation is in progress.
- */
- public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;
-
- /**
- * The drawer is unlocked.
- */
- public static final int LOCK_MODE_UNLOCKED = 0;
-
- /**
- * The drawer is locked closed. The user may not open it, though
- * the app may open it programmatically.
- */
- public static final int LOCK_MODE_LOCKED_CLOSED = 1;
-
- /**
- * The drawer is locked open. The user may not close it, though the app
- * may close it programmatically.
- */
- public static final int LOCK_MODE_LOCKED_OPEN = 2;
-
- private static final float MAX_SCRIM_ALPHA = 0.8f;
-
- private static final boolean SCRIM_ENABLED = true;
-
- private static final boolean SHADOW_ENABLED = true;
-
- /**
- * Minimum velocity that will be detected as a fling
- */
- private static final int MIN_FLING_VELOCITY = 400; // dips per second
-
- /**
- * Experimental feature.
- */
- private static final boolean ALLOW_EDGE_LOCK = false;
-
- private static final boolean EDGE_DRAG_ENABLED = false;
-
- private static final boolean CHILDREN_DISALLOW_INTERCEPT = true;
-
- private static final float TOUCH_SLOP_SENSITIVITY = 1.f;
-
- private static final int[] LAYOUT_ATTRS = new int[] {
- android.R.attr.layout_gravity
- };
-
- public static final int DEFAULT_SCRIM_COLOR = 0xff262626;
-
- private int mScrimColor = DEFAULT_SCRIM_COLOR;
- private final Paint mScrimPaint = new Paint();
- private final Paint mEdgeHighlightPaint = new Paint();
-
- private final ViewDragHelper mDragger;
-
- private final Runnable mInvalidateRunnable = new Runnable() {
- @Override
- public void run() {
- requestLayout();
- invalidate();
- }
- };
-
- // view faders who will be given different colors as the drawer opens
- private final Set<ViewFaderHolder> mViewFaders;
- private final ReversibleInterpolator mViewFaderInterpolator;
- private final ReversibleInterpolator mDrawerFadeInterpolator;
- private final Handler mHandler = new Handler();
-
- private int mEndingViewColor;
- private int mStartingViewColor;
- private int mDrawerState;
- private boolean mInLayout;
- /** Whether we have done a layout yet. Used to initialize some view-related state. */
- private boolean mFirstLayout = true;
- private boolean mHasInflated;
- private int mLockModeLeft;
- private int mLockModeRight;
- private boolean mChildrenCanceledTouch;
- private DrawerListener mDrawerListener;
- private DrawerControllerListener mDrawerControllerListener;
- private Drawable mShadow;
- private View mDrawerView;
- private View mContentView;
- private boolean mNeedsFocus;
- /** Whether or not the drawer started open for the current gesture */
- private boolean mStartedOpen;
- private boolean mHasWheel;
-
- /**
- * Listener for monitoring events about drawers.
- */
- public interface DrawerListener {
- /**
- * Called when a drawer's position changes.
- * @param drawerView The child view that was moved
- * @param slideOffset The new offset of this drawer within its range, from 0-1
- */
- void onDrawerSlide(View drawerView, float slideOffset);
-
- /**
- * Called when a drawer has settled in a completely open state.
- * The drawer is interactive at this point.
- *
- * @param drawerView Drawer view that is now open
- */
- void onDrawerOpened(View drawerView);
-
- /**
- * Called when a drawer has settled in a completely closed state.
- *
- * @param drawerView Drawer view that is now closed
- */
- void onDrawerClosed(View drawerView);
-
- /**
- * Called when a drawer is starting to open.
- *
- * @param drawerView Drawer view that is opening
- */
- void onDrawerOpening(View drawerView);
-
- /**
- * Called when a drawer is starting to close.
- *
- * @param drawerView Drawer view that is closing
- */
- void onDrawerClosing(View drawerView);
-
- /**
- * Called when the drawer motion state changes. The new state will
- * be one of {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
- *
- * @param newState The new drawer motion state
- */
- void onDrawerStateChanged(int newState);
- }
-
- /**
- * Used to execute when the drawer needs to handle state that the underlying views would like
- * to handle in a specific way.
- */
- public interface DrawerControllerListener {
- void onBack();
- boolean onScroll();
- }
-
- /**
- * Stub/no-op implementations of all methods of {@link DrawerListener}.
- * Override this if you only care about a few of the available callback methods.
- */
- public static abstract class SimpleDrawerListener implements DrawerListener {
- @Override
- public void onDrawerSlide(View drawerView, float slideOffset) {
- }
-
- @Override
- public void onDrawerOpened(View drawerView) {
- }
-
- @Override
- public void onDrawerClosed(View drawerView) {
- }
-
- @Override
- public void onDrawerOpening(View drawerView) {
- }
-
- @Override
- public void onDrawerClosing(View drawerView) {
- }
-
- @Override
- public void onDrawerStateChanged(int newState) {
- }
- }
-
- /**
- * Sets the color of (or tints) a view (or views).
- */
- public interface ViewFader {
- void setColor(int color);
- }
-
- public CarDrawerLayout(Context context) {
- this(context, null);
- }
-
- public CarDrawerLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public CarDrawerLayout(final Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- mViewFaders = new HashSet<>();
- mEndingViewColor = getResources().getColor(R.color.car_tint);
-
- mEdgeHighlightPaint.setColor(getResources().getColor(android.R.color.black));
-
- final float density = getResources().getDisplayMetrics().density;
- final float minVel = MIN_FLING_VELOCITY * density;
-
- ViewDragCallback viewDragCallback = new ViewDragCallback();
- mDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, viewDragCallback);
- mDragger.setMinVelocity(minVel);
- viewDragCallback.setDragger(mDragger);
-
- ViewGroupCompat.setMotionEventSplittingEnabled(this, false);
-
- if (SHADOW_ENABLED) {
- setDrawerShadow(CarUiResourceLoader.getDrawable(context, "drawer_shadow"));
- }
-
- Resources.Theme theme = context.getTheme();
- TypedArray ta = theme.obtainStyledAttributes(new int[] {
- android.R.attr.colorPrimaryDark
- });
- setScrimColor(ta.getColor(0, context.getResources().getColor(R.color.car_grey_900)));
-
- mViewFaderInterpolator = new ReversibleInterpolator(
- new QuantumInterpolator(QuantumInterpolator.FAST_OUT_SLOW_IN, 0.25f, 0.25f, 0.5f),
- new QuantumInterpolator(QuantumInterpolator.FAST_OUT_SLOW_IN, 0.43f, 0.14f, 0.43f)
- );
- mDrawerFadeInterpolator = new ReversibleInterpolator(
- new QuantumInterpolator(QuantumInterpolator.FAST_OUT_SLOW_IN, 0.625f, 0.25f, 0.125f),
- new QuantumInterpolator(QuantumInterpolator.FAST_OUT_LINEAR_IN, 0.58f, 0.14f, 0.28f)
- );
-
- mHasWheel = CarUiResourceLoader.getBoolean(context, "has_wheel", false);
- }
-
- @Override
- public boolean dispatchKeyEvent(@NonNull KeyEvent keyEvent) {
- int action = keyEvent.getAction();
- int keyCode = keyEvent.getKeyCode();
- final View drawerView = findDrawerView();
- if (drawerView != null && getDrawerLockMode(drawerView) == LOCK_MODE_UNLOCKED) {
- if (isDrawerOpen()) {
- if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
- || keyCode == KeyEvent.KEYCODE_SOFT_RIGHT) {
- closeDrawer();
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_BACK
- && action == KeyEvent.ACTION_UP
- && mDrawerControllerListener != null) {
- mDrawerControllerListener.onBack();
- return true;
- } else {
- return drawerView.dispatchKeyEvent(keyEvent);
- }
- }
- }
-
- return mContentView.dispatchKeyEvent(keyEvent);
- }
-
- @Override
- public boolean dispatchGenericMotionEvent(MotionEvent ev) {
- final View drawerView = findDrawerView();
- if (drawerView != null
- && ev.getAction() == MotionEvent.ACTION_SCROLL
- && mDrawerControllerListener != null
- && mDrawerControllerListener.onScroll()) {
- return true;
- }
- return super.dispatchGenericMotionEvent(ev);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mHasInflated = true;
- setAutoDayNightMode();
-
- setOnGenericMotionListener(new OnGenericMotionListener() {
- @Override
- public boolean onGenericMotion(View view, MotionEvent event) {
- if (getChildCount() == 0) {
- return false;
- }
- if (isDrawerOpen()) {
- View drawerView = findDrawerView();
- ViewGroup viewGroup = (ViewGroup) ((FrameLayout) drawerView).getChildAt(0);
- return viewGroup.getChildAt(0).onGenericMotionEvent(event);
- }
- View contentView = findContentView();
- ViewGroup viewGroup = (ViewGroup) ((FrameLayout) contentView).getChildAt(0);
- return viewGroup.getChildAt(0).onGenericMotionEvent(event);
- }
- });
- }
-
- /**
- * Set a simple drawable used for the left or right shadow.
- * The drawable provided must have a nonzero intrinsic width.
- *
- * @param shadowDrawable Shadow drawable to use at the edge of a drawer
- */
- public void setDrawerShadow(Drawable shadowDrawable) {
- mShadow = shadowDrawable;
- invalidate();
- }
-
-
-
- /**
- * Set a color to use for the scrim that obscures primary content while a drawer is open.
- *
- * @param color Color to use in 0xAARRGGBB format.
- */
- public void setScrimColor(int color) {
- mScrimColor = color;
- invalidate();
- }
-
- /**
- * Set a listener to be notified of drawer events.
- *
- * @param listener Listener to notify when drawer events occur
- * @see DrawerListener
- */
- public void setDrawerListener(DrawerListener listener) {
- mDrawerListener = listener;
- }
-
- public void setDrawerControllerListener(DrawerControllerListener listener) {
- mDrawerControllerListener = listener;
- }
-
- /**
- * Enable or disable interaction with all drawers.
- *
- * <p>This allows the application to restrict the user's ability to open or close
- * any drawer within this layout. DrawerLayout will still respond to calls to
- * {@link #openDrawer()}, {@link #closeDrawer()} and friends if a drawer is locked.</p>
- *
- * <p>Locking drawers open or closed will implicitly open or close
- * any drawers as appropriate.</p>
- *
- * @param lockMode The new lock mode for the given drawer. One of {@link #LOCK_MODE_UNLOCKED},
- * {@link #LOCK_MODE_LOCKED_CLOSED} or {@link #LOCK_MODE_LOCKED_OPEN}.
- */
- public void setDrawerLockMode(int lockMode) {
- LayoutParams lp = (LayoutParams) findDrawerView().getLayoutParams();
- setDrawerLockMode(lockMode, lp.gravity);
- }
-
- /**
- * Enable or disable interaction with the given drawer.
- *
- * <p>This allows the application to restrict the user's ability to open or close
- * the given drawer. DrawerLayout will still respond to calls to {@link #openDrawer()},
- * {@link #closeDrawer()} and friends if a drawer is locked.</p>
- *
- * <p>Locking a drawer open or closed will implicitly open or close
- * that drawer as appropriate.</p>
- *
- * @param lockMode The new lock mode for the given drawer. One of {@link #LOCK_MODE_UNLOCKED},
- * {@link #LOCK_MODE_LOCKED_CLOSED} or {@link #LOCK_MODE_LOCKED_OPEN}.
- * @param edgeGravity Gravity.LEFT, RIGHT, START or END.
- * Expresses which drawer to change the mode for.
- *
- * @see #LOCK_MODE_UNLOCKED
- * @see #LOCK_MODE_LOCKED_CLOSED
- * @see #LOCK_MODE_LOCKED_OPEN
- */
- public void setDrawerLockMode(int lockMode, int edgeGravity) {
- final int absGravity = GravityCompat.getAbsoluteGravity(edgeGravity,
- ViewCompat.getLayoutDirection(this));
- if (absGravity == Gravity.LEFT) {
- mLockModeLeft = lockMode;
- } else if (absGravity == Gravity.RIGHT) {
- mLockModeRight = lockMode;
- }
- if (lockMode != LOCK_MODE_UNLOCKED) {
- // Cancel interaction in progress
- mDragger.cancel();
- }
- switch (lockMode) {
- case LOCK_MODE_LOCKED_OPEN:
- openDrawer();
- break;
- case LOCK_MODE_LOCKED_CLOSED:
- closeDrawer();
- break;
- // default: do nothing
- }
- }
-
- /**
- * All view faders will be light when the drawer is open and fade to dark and it closes.
- * NOTE: this will clear any existing view faders.
- */
- public void setLightMode() {
- mStartingViewColor = getResources().getColor(R.color.car_title_light);
- mEndingViewColor = getResources().getColor(R.color.car_tint);
- updateViewFaders();
- }
-
- /**
- * All view faders will be dark when the drawer is open and stay that way when it closes.
- * NOTE: this will clear any existing view faders.
- */
- public void setDarkMode() {
- mStartingViewColor = getResources().getColor(R.color.car_title_dark);
- mEndingViewColor = getResources().getColor(R.color.car_tint);
- updateViewFaders();
- }
-
- /**
- * All view faders will be dark during the day and light at night.
- * NOTE: this will clear any existing view faders.
- */
- public void setAutoDayNightMode() {
- mStartingViewColor = getResources().getColor(R.color.car_title);
- mEndingViewColor = getResources().getColor(R.color.car_tint);
- updateViewFaders();
- }
-
- private void resetViewFaders() {
- mViewFaders.clear();
- }
-
- /**
- * Check the lock mode of the given drawer view.
- *
- * @param drawerView Drawer view to check lock mode
- * @return one of {@link #LOCK_MODE_UNLOCKED}, {@link #LOCK_MODE_LOCKED_CLOSED} or
- * {@link #LOCK_MODE_LOCKED_OPEN}.
- */
- public int getDrawerLockMode(View drawerView) {
- final int absGravity = getDrawerViewAbsoluteGravity(drawerView);
- if (absGravity == Gravity.LEFT) {
- return mLockModeLeft;
- } else if (absGravity == Gravity.RIGHT) {
- return mLockModeRight;
- }
- return LOCK_MODE_UNLOCKED;
- }
-
- /**
- * Resolve the shared state of all drawers from the component ViewDragHelpers.
- * Should be called whenever a ViewDragHelper's state changes.
- */
- private void updateDrawerState(int activeState) {
- View drawerView = findDrawerView();
- if (drawerView != null && activeState == STATE_IDLE) {
- if (onScreen() == 0) {
- dispatchOnDrawerClosed(drawerView);
- } else if (onScreen() == 1) {
- dispatchOnDrawerOpened(drawerView);
- }
- }
-
- if (mDragger.getViewDragState() != mDrawerState) {
- mDrawerState = mDragger.getViewDragState();
-
- if (mDrawerListener != null) {
- mDrawerListener.onDrawerStateChanged(mDragger.getViewDragState());
- }
- }
- }
-
- private void dispatchOnDrawerClosed(View drawerView) {
- final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
- if (lp.knownOpen) {
- lp.knownOpen = false;
- if (mDrawerListener != null) {
- mDrawerListener.onDrawerClosed(drawerView);
- }
- }
- }
-
- private void dispatchOnDrawerOpened(View drawerView) {
- final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
- if (!lp.knownOpen) {
- lp.knownOpen = true;
- if (mDrawerListener != null) {
- mDrawerListener.onDrawerOpened(drawerView);
- }
- }
- }
-
- private void dispatchOnDrawerSlide(View drawerView, float slideOffset) {
- if (mDrawerListener != null) {
- mDrawerListener.onDrawerSlide(drawerView, slideOffset);
- }
- }
-
- private void dispatchOnDrawerOpening(View drawerView) {
- if (mDrawerListener != null) {
- mDrawerListener.onDrawerOpening(drawerView);
- }
- }
-
- private void dispatchOnDrawerClosing(View drawerView) {
- if (mDrawerListener != null) {
- mDrawerListener.onDrawerClosing(drawerView);
- }
- }
-
- private void setDrawerViewOffset(View drawerView, float slideOffset) {
- if (slideOffset == onScreen()) {
- return;
- }
-
- LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
- lp.onScreen = slideOffset;
- dispatchOnDrawerSlide(drawerView, slideOffset);
- }
-
- private float onScreen() {
- return ((LayoutParams) findDrawerView().getLayoutParams()).onScreen;
- }
-
- /**
- * @return the absolute gravity of the child drawerView, resolved according
- * to the current layout direction
- */
- private int getDrawerViewAbsoluteGravity(View drawerView) {
- final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
- return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this));
- }
-
- private boolean checkDrawerViewAbsoluteGravity(View drawerView, int checkFor) {
- final int absGravity = getDrawerViewAbsoluteGravity(drawerView);
- return (absGravity & checkFor) == checkFor;
- }
-
- /**
- * @return the drawer view
- */
- private View findDrawerView() {
- if (mDrawerView != null) {
- return mDrawerView;
- }
-
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final int childAbsGravity = getDrawerViewAbsoluteGravity(child);
- if (childAbsGravity != Gravity.NO_GRAVITY) {
- mDrawerView = child;
- return child;
- }
- }
- throw new IllegalStateException("No drawer view found.");
- }
-
- /**
- * @return the content. NOTE: this is the view with no gravity.
- */
- private View findContentView() {
- if (mContentView != null) {
- return mContentView;
- }
-
- final int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; --i) {
- final View child = getChildAt(i);
- if (isDrawerView(child)) {
- continue;
- }
- mContentView = child;
- return child;
- }
- throw new IllegalStateException("No content view found.");
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- }
-
- @Override
- public boolean requestFocus(int direction, Rect rect) {
- // Optimally we want to check isInTouchMode(), but that value isn't always correct.
- if (mHasWheel) {
- mNeedsFocus = true;
- }
- return super.requestFocus(direction, rect);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mFirstLayout = true;
- // There needs to be a layout pending if we're not going to animate the drawer until the
- // next layout, so make it so.
- requestLayout();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
- if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
- if (isInEditMode()) {
- // Don't crash the layout editor. Consume all of the space if specified
- // or pick a magic number from thin air otherwise.
- // TODO Better communication with tools of this bogus state.
- // It will crash on a real device.
- if (widthMode == MeasureSpec.UNSPECIFIED) {
- widthSize = 300;
- }
- else if (heightMode == MeasureSpec.UNSPECIFIED) {
- heightSize = 300;
- }
- } else {
- throw new IllegalArgumentException(
- "DrawerLayout must be measured with MeasureSpec.EXACTLY.");
- }
- }
-
- setMeasuredDimension(widthSize, heightSize);
-
- View view = findContentView();
- LayoutParams lp = ((LayoutParams) view.getLayoutParams());
- // Content views get measured at exactly the layout's size.
- final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
- widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
- final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
- heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
- view.measure(contentWidthSpec, contentHeightSpec);
-
- view = findDrawerView();
- lp = ((LayoutParams) view.getLayoutParams());
- final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
- lp.leftMargin + lp.rightMargin,
- lp.width);
- final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
- lp.topMargin + lp.bottomMargin,
- lp.height);
- view.measure(drawerWidthSpec, drawerHeightSpec);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- mInLayout = true;
- final int width = r - l;
-
- View contentView = findContentView();
- View drawerView = findDrawerView();
-
- LayoutParams drawerLp = (LayoutParams) drawerView.getLayoutParams();
- LayoutParams contentLp = (LayoutParams) contentView.getLayoutParams();
-
- int contentRight = contentLp.getMarginStart() + getWidth();
- contentView.layout(contentRight - contentView.getMeasuredWidth(),
- contentLp.topMargin, contentRight,
- contentLp.topMargin + contentView.getMeasuredHeight());
-
- final int childHeight = drawerView.getMeasuredHeight();
- int onScreen = (int) (drawerView.getWidth() * drawerLp.onScreen);
- int offset;
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
- offset = onScreen - drawerView.getWidth();
- } else {
- offset = width - onScreen;
- }
- drawerView.layout(drawerLp.getMarginStart() + offset, drawerLp.topMargin,
- width - drawerLp.getMarginEnd() + offset,
- childHeight + drawerLp.topMargin);
- updateDrawerAlpha();
- updateViewFaders();
- if (mFirstLayout) {
-
- // TODO(b/15394507): Normally, onMeasure()/onLayout() are called three times when
- // you create CarDrawerLayout, but when you pop it back it's only called once which
- // leaves us in a weird state. This is a pretty ugly hack to fix that.
- mHandler.post(mInvalidateRunnable);
-
- mFirstLayout = false;
- }
-
- if (mNeedsFocus) {
- if (initializeFocus()) {
- mNeedsFocus = false;
- }
- }
-
- mInLayout = false;
- }
-
- private boolean initializeFocus() {
- // Only request focus if the current view that needs focus doesn't already have it. This
- // prevents some nasty bugs where focus ends up snapping to random elements and also saves
- // a bunch of cycles in the average case.
- mDrawerView.setFocusable(false);
- mContentView.setFocusable(false);
- boolean needFocus = !mDrawerView.hasFocus() && !mContentView.hasFocus();
- if (!needFocus) {
- return true;
- }
-
- // Find something in the hierarchy to give focus to.
- List<View> focusables;
- boolean drawerOpen = isDrawerOpen();
- if (drawerOpen) {
- focusables = mDrawerView.getFocusables(FOCUS_DOWN);
- } else {
- focusables = mContentView.getFocusables(FOCUS_DOWN);
- }
-
- // The 2 else cases here are a catch all for when nothing is focusable in view hierarchy.
- // If you don't have anything focusable on screen, key events will not be delivered to
- // the view hierarchy and you end up getting stuck without being able to open / close the
- // drawer or launch gsa.
-
- if (!focusables.isEmpty()) {
- focusables.get(0).requestFocus();
- return true;
- } else if (drawerOpen) {
- mDrawerView.setFocusable(true);
- } else {
- mContentView.setFocusable(true);
- }
- return false;
- }
-
- @Override
- public void requestLayout() {
- if (!mInLayout) {
- super.requestLayout();
- }
- }
-
- @Override
- public void computeScroll() {
- if (mDragger.continueSettling(true)) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- private static boolean hasOpaqueBackground(View v) {
- final Drawable bg = v.getBackground();
- return bg != null && bg.getOpacity() == PixelFormat.OPAQUE;
- }
-
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- final int height = getHeight();
- final boolean drawingContent = isContentView(child);
- int clipLeft = findContentView().getLeft();
- int clipRight = findContentView().getRight();
- final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;
-
- final int restoreCount = canvas.save();
- if (drawingContent) {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View v = getChildAt(i);
- if (v == child || v.getVisibility() != VISIBLE ||
- !hasOpaqueBackground(v) || !isDrawerView(v) ||
- v.getHeight() < height) {
- continue;
- }
-
- if (checkDrawerViewAbsoluteGravity(v, Gravity.LEFT)) {
- final int vright = v.getRight();
- if (vright > clipLeft) {
- clipLeft = vright;
- }
- } else {
- final int vleft = v.getLeft();
- if (vleft < clipRight) {
- clipRight = vleft;
- }
- }
- }
- canvas.clipRect(clipLeft, 0, clipRight, getHeight());
- }
- final boolean result = super.drawChild(canvas, child, drawingTime);
- canvas.restoreToCount(restoreCount);
-
- if (drawingContent) {
- int scrimAlpha = SCRIM_ENABLED ?
- (int) (baseAlpha * Math.max(0, Math.min(1, onScreen())) * MAX_SCRIM_ALPHA) : 0;
-
- if (scrimAlpha > 0) {
- int color = scrimAlpha << 24 | (mScrimColor & 0xffffff);
- mScrimPaint.setColor(color);
-
- canvas.drawRect(clipLeft, 0, clipRight, getHeight(), mScrimPaint);
-
- canvas.drawRect(clipLeft - 1, 0, clipLeft, getHeight(), mEdgeHighlightPaint);
- }
-
- LayoutParams drawerLp = (LayoutParams) findDrawerView().getLayoutParams();
- if (mShadow != null
- && checkDrawerViewAbsoluteGravity(findDrawerView(), Gravity.LEFT)) {
- final int offScreen = (int) ((1 - drawerLp.onScreen) * findDrawerView().getWidth());
- final int drawerRight = getWidth() - drawerLp.getMarginEnd() - offScreen;
- final int shadowWidth = mShadow.getIntrinsicWidth();
- final float alpha =
- Math.max(0, Math.min((float) drawerRight / mDragger.getEdgeSize(), 1.f));
- mShadow.setBounds(drawerRight, child.getTop(),
- drawerRight + shadowWidth, child.getBottom());
- mShadow.setAlpha((int) (255 * alpha * alpha * alpha));
- mShadow.draw(canvas);
- } else if (mShadow != null
- && checkDrawerViewAbsoluteGravity(findDrawerView(),Gravity.RIGHT)) {
- final int onScreen = (int) (findDrawerView().getWidth() * drawerLp.onScreen);
- final int drawerLeft = drawerLp.getMarginStart() + getWidth() - onScreen;
- final int shadowWidth = mShadow.getIntrinsicWidth();
- final float alpha =
- Math.max(0, Math.min((float) onScreen / mDragger.getEdgeSize(), 1.f));
- canvas.save();
- canvas.translate(2 * drawerLeft - shadowWidth, 0);
- canvas.scale(-1.0f, 1.0f);
- mShadow.setBounds(drawerLeft - shadowWidth, child.getTop(),
- drawerLeft, child.getBottom());
- mShadow.setAlpha((int) (255 * alpha * alpha * alpha * alpha));
- mShadow.draw(canvas);
- canvas.restore();
- }
- }
- return result;
- }
-
- private boolean isContentView(View child) {
- return child == findContentView();
- }
-
- private boolean isDrawerView(View child) {
- return child == findDrawerView();
- }
-
- private void updateDrawerAlpha() {
- float alpha;
- if (mStartedOpen) {
- alpha = mDrawerFadeInterpolator.getReverseInterpolation(onScreen());
- } else {
- alpha = mDrawerFadeInterpolator.getForwardInterpolation(onScreen());
- }
- ViewGroup drawerView = (ViewGroup) findDrawerView();
- int drawerChildCount = drawerView.getChildCount();
- for (int i = 0; i < drawerChildCount; i++) {
- drawerView.getChildAt(i).setAlpha(alpha);
- }
- }
-
- /**
- * Add a view fader whose color will be set as the drawer opens and closes.
- */
- public void addViewFader(ViewFader viewFader) {
- addViewFader(viewFader, mStartingViewColor, mEndingViewColor);
- }
-
- public void addViewFader(ViewFader viewFader, int startingColor, int endingColor) {
- mViewFaders.add(new ViewFaderHolder(viewFader, startingColor, endingColor));
- updateViewFaders();
- }
-
- public void removeViewFader(ViewFader viewFader) {
- for (Iterator<ViewFaderHolder> it = mViewFaders.iterator(); it.hasNext(); ) {
- ViewFaderHolder viewFaderHolder = it.next();
- if (viewFaderHolder.viewFader.equals(viewFader)) {
- it.remove();
- }
- }
- }
-
- private void updateViewFaders() {
- if (!mHasInflated) {
- return;
- }
-
- float fadeProgress;
- if (mStartedOpen) {
- fadeProgress = mViewFaderInterpolator.getReverseInterpolation(onScreen());
- } else {
- fadeProgress = mViewFaderInterpolator.getForwardInterpolation(onScreen());
- }
- for (Iterator<ViewFaderHolder> it = mViewFaders.iterator(); it.hasNext(); ) {
- ViewFaderHolder viewFaderHolder = it.next();
- int startingColor = viewFaderHolder.startingColor;
- int endingColor = viewFaderHolder.endingColor;
- int alpha = weightedAverage(Color.alpha(startingColor),
- Color.alpha(endingColor), fadeProgress);
- int red = weightedAverage(Color.red(startingColor),
- Color.red(endingColor), fadeProgress);
- int green = weightedAverage(Color.green(startingColor),
- Color.green(endingColor), fadeProgress);
- int blue = weightedAverage(Color.blue(startingColor),
- Color.blue(endingColor), fadeProgress);
- viewFaderHolder.viewFader.setColor(alpha << 24 | red << 16 | green << 8 | blue);
- }
- }
-
- private int weightedAverage(int starting, int ending, float weight) {
- return (int) ((1f - weight) * starting + weight * ending);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- final int action = MotionEventCompat.getActionMasked(ev);
-
- // "|" used deliberately here; both methods should be invoked.
- final boolean interceptForDrag = mDragger.shouldInterceptTouchEvent(ev);
-
- boolean interceptForTap = false;
-
- switch (action) {
- case MotionEvent.ACTION_DOWN: {
- final float x = ev.getX();
- final float y = ev.getY();
- if (onScreen() > 0 && isContentView(mDragger.findTopChildUnder((int) x, (int) y))) {
- interceptForTap = true;
- }
- mChildrenCanceledTouch = false;
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- mChildrenCanceledTouch = false;
- }
- }
-
- return interceptForDrag || interceptForTap || mChildrenCanceledTouch;
- }
-
- @Override
- public boolean onTouchEvent(@NonNull MotionEvent ev) {
- mDragger.processTouchEvent(ev);
- final int absGravity = getDrawerViewAbsoluteGravity(findDrawerView());
- final int edge;
- if (absGravity == Gravity.LEFT) {
- edge = ViewDragHelper.EDGE_LEFT;
- } else {
- edge = ViewDragHelper.EDGE_RIGHT;
- }
-
- // don't allow views behind the drawer to be touched
- boolean drawerPartiallyOpen = onScreen() > 0;
- return mDragger.isEdgeTouched(edge) ||
- mDragger.getCapturedView() != null ||
- drawerPartiallyOpen;
- }
-
- @Override
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- if (CHILDREN_DISALLOW_INTERCEPT) {
- // If we have an edge touch we want to skip this and track it for later instead.
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
-
- View drawerView = findDrawerView();
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
-
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.RIGHT)) {
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
- }
-
- /**
- * Open the drawer view by animating it into view.
- */
- public void openDrawer() {
- ViewGroup drawerView = (ViewGroup) findDrawerView();
- mStartedOpen = false;
-
- if (hasWindowFocus()) {
- int left;
- LayoutParams drawerLp = (LayoutParams) drawerView.getLayoutParams();
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
- left = drawerLp.getMarginStart();
- } else {
- left = drawerLp.getMarginStart() + getWidth() - drawerView.getWidth();
- }
- mDragger.smoothSlideViewTo(drawerView, left, drawerView.getTop());
- dispatchOnDrawerOpening(drawerView);
- } else {
- final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
- lp.onScreen = 1.f;
- dispatchOnDrawerOpened(drawerView);
- }
-
- ViewGroup contentView = (ViewGroup) findContentView();
- contentView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- drawerView.setDescendantFocusability(ViewGroup. FOCUS_AFTER_DESCENDANTS);
-
- View focusable = drawerView.getChildAt(0);
- if (focusable != null) {
- focusable.requestFocus();
- }
- invalidate();
- }
-
- /**
- * Close the specified drawer view by animating it into view.
- */
- public void closeDrawer() {
- ViewGroup drawerView = (ViewGroup) findDrawerView();
- if (!isDrawerView(drawerView)) {
- throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
- }
- mStartedOpen = true;
-
- // Don't trigger the close drawer animation if drawer is not open.
- if (hasWindowFocus() && isDrawerOpen()) {
- int left;
- LayoutParams drawerLp = (LayoutParams) drawerView.getLayoutParams();
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
- left = drawerLp.getMarginStart() - drawerView.getWidth();
- } else {
- left = drawerLp.getMarginStart() + getWidth();
- }
- mDragger.smoothSlideViewTo(drawerView, left, drawerView.getTop());
- dispatchOnDrawerClosing(drawerView);
- } else {
- final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
- lp.onScreen = 0.f;
- dispatchOnDrawerClosed(drawerView);
- }
-
- ViewGroup contentView = (ViewGroup) findContentView();
- drawerView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- contentView.setDescendantFocusability(ViewGroup. FOCUS_AFTER_DESCENDANTS);
-
- if (!isInTouchMode()) {
- List<View> focusables = contentView.getFocusables(FOCUS_DOWN);
- if (focusables.size() > 0) {
- View candidate = focusables.get(0);
- candidate.requestFocus();
- }
- }
- invalidate();
- }
-
- @Override
- public void addFocusables(@NonNull ArrayList<View> views, int direction, int focusableMode) {
- boolean drawerOpen = isDrawerOpen();
- if (drawerOpen) {
- findDrawerView().addFocusables(views, direction, focusableMode);
- } else {
- findContentView().addFocusables(views, direction, focusableMode);
- }
- }
-
- /**
- * Check if the given drawer view is currently in an open state.
- * To be considered "open" the drawer must have settled into its fully
- * visible state. To check for partial visibility use
- * {@link #isDrawerVisible(android.view.View)}.
- *
- * @return true if the given drawer view is in an open state
- * @see #isDrawerVisible(android.view.View)
- */
- public boolean isDrawerOpen() {
- return ((LayoutParams) findDrawerView().getLayoutParams()).knownOpen;
- }
-
- /**
- * Check if a given drawer view is currently visible on-screen. The drawer
- * may be fully extended or anywhere in between.
- *
- * @param drawer Drawer view to check
- * @return true if the given drawer is visible on-screen
- * @see #isDrawerOpen()
- */
- public boolean isDrawerVisible(View drawer) {
- if (!isDrawerView(drawer)) {
- throw new IllegalArgumentException("View " + drawer + " is not a drawer");
- }
- return onScreen() > 0;
- }
-
- /**
- * Check if a given drawer view is currently visible on-screen. The drawer
- * may be fully extended or anywhere in between.
- * If there is no drawer with the given gravity this method will return false.
- *
- * @return true if the given drawer is visible on-screen
- */
- public boolean isDrawerVisible() {
- final View drawerView = findDrawerView();
- return drawerView != null && isDrawerVisible(drawerView);
- }
-
- @Override
- protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- }
-
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams
- ? new LayoutParams((LayoutParams) p)
- : p instanceof MarginLayoutParams
- ? new LayoutParams((MarginLayoutParams) p)
- : new LayoutParams(p);
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams && super.checkLayoutParams(p);
- }
-
- @Override
- public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- private boolean hasVisibleDrawer() {
- return findVisibleDrawer() != null;
- }
-
- private View findVisibleDrawer() {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (isDrawerView(child) && isDrawerVisible(child)) {
- return child;
- }
- }
- return null;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- SavedState ss = null;
- if (state.getClass().getClassLoader() != getClass().getClassLoader()) {
- // Class loader mismatch, recreate from parcel.
- Parcel stateParcel = Parcel.obtain();
- state.writeToParcel(stateParcel, 0);
- ss = SavedState.CREATOR.createFromParcel(stateParcel);
- } else {
- ss = (SavedState) state;
- }
- super.onRestoreInstanceState(ss.getSuperState());
-
- if (ss.openDrawerGravity != Gravity.NO_GRAVITY) {
- openDrawer();
- }
-
- setDrawerLockMode(ss.lockModeLeft, Gravity.LEFT);
- setDrawerLockMode(ss.lockModeRight, Gravity.RIGHT);
- }
-
- @Override
- protected Parcelable onSaveInstanceState() {
- final Parcelable superState = super.onSaveInstanceState();
-
- final SavedState ss = new SavedState(superState);
-
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (!isDrawerView(child)) {
- continue;
- }
-
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp.knownOpen) {
- ss.openDrawerGravity = lp.gravity;
- // Only one drawer can be open at a time.
- break;
- }
- }
-
- ss.lockModeLeft = mLockModeLeft;
- ss.lockModeRight = mLockModeRight;
-
- return ss;
- }
-
- /**
- * State persisted across instances
- */
- protected static class SavedState extends BaseSavedState {
- int openDrawerGravity = Gravity.NO_GRAVITY;
- int lockModeLeft = LOCK_MODE_UNLOCKED;
- int lockModeRight = LOCK_MODE_UNLOCKED;
-
- public SavedState(Parcel in) {
- super(in);
- openDrawerGravity = in.readInt();
- lockModeLeft = in.readInt();
- lockModeRight = in.readInt();
- }
-
- public SavedState(Parcelable superState) {
- super(superState);
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(openDrawerGravity);
- dest.writeInt(lockModeLeft);
- dest.writeInt(lockModeRight);
- }
-
- @SuppressWarnings("hiding")
- public static final Creator<SavedState> CREATOR =
- new Creator<SavedState>() {
- @Override
- public SavedState createFromParcel(Parcel source) {
- return new SavedState(source);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- private class ViewDragCallback extends ViewDragHelper.Callback {
- @SuppressWarnings("hiding")
- private ViewDragHelper mDragger;
-
- public void setDragger(ViewDragHelper dragger) {
- mDragger = dragger;
- }
-
- @Override
- public boolean tryCaptureView(View child, int pointerId) {
- CarDrawerLayout.LayoutParams lp = (LayoutParams) findDrawerView().getLayoutParams();
- int edges = EDGE_DRAG_ENABLED ? ViewDragHelper.EDGE_ALL : 0;
- boolean captured = isContentView(child) &&
- getDrawerLockMode(child) == LOCK_MODE_UNLOCKED &&
- (lp.knownOpen || mDragger.isEdgeTouched(edges));
- if (captured && lp.knownOpen) {
- mStartedOpen = true;
- } else if (captured && !lp.knownOpen) {
- mStartedOpen = false;
- }
- // We want dragging starting on the content view to drag the drawer. Therefore when
- // touch events try to capture the content view, we force capture of the drawer view.
- if (captured) {
- mDragger.captureChildView(findDrawerView(), pointerId);
- }
- return false;
- }
-
- @Override
- public void onViewDragStateChanged(int state) {
- updateDrawerState(state);
- }
-
- @Override
- public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
- float offset;
- View drawerView = findDrawerView();
- final int drawerWidth = drawerView.getWidth();
- // This reverses the positioning shown in onLayout.
- if (checkDrawerViewAbsoluteGravity(findDrawerView(), Gravity.LEFT)) {
- offset = (float) (left + drawerWidth) / drawerWidth;
- } else {
- offset = (float) (getWidth() - left) / drawerWidth;
- }
- setDrawerViewOffset(findDrawerView(), offset);
-
- updateDrawerAlpha();
-
- updateViewFaders();
- invalidate();
- }
-
- @Override
- public void onViewReleased(View releasedChild, float xvel, float yvel) {
- final View drawerView = findDrawerView();
- final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
- int left;
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
- // Open the drawer if they are swiping right or if they are not currently moving but
- // have moved the drawer in the current gesture and released the drawer when it was
- // fully open.
- // Close otherwise.
- left = xvel > 0 ? lp.getMarginStart() : lp.getMarginStart() - drawerView.getWidth();
- } else {
- // See comment for left drawer.
- left = xvel < 0 ? lp.getMarginStart() + getWidth() - drawerView.getWidth()
- : lp.getMarginStart() + getWidth();
- }
-
- mDragger.settleCapturedViewAt(left, releasedChild.getTop());
- invalidate();
- }
-
- @Override
- public boolean onEdgeLock(int edgeFlags) {
- if (ALLOW_EDGE_LOCK) {
- if (!isDrawerOpen()) {
- closeDrawer();
- }
- return true;
- }
- return false;
- }
-
- @Override
- public void onEdgeDragStarted(int edgeFlags, int pointerId) {
- View drawerView = findDrawerView();
- if ((edgeFlags & ViewDragHelper.EDGE_LEFT) == ViewDragHelper.EDGE_LEFT) {
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.RIGHT)) {
- drawerView = null;
- }
- } else {
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
- drawerView = null;
- }
- }
-
- if (drawerView != null && getDrawerLockMode(drawerView) == LOCK_MODE_UNLOCKED) {
- mDragger.captureChildView(drawerView, pointerId);
- }
- }
-
- @Override
- public int getViewHorizontalDragRange(View child) {
- return child.getWidth();
- }
-
- @Override
- public int clampViewPositionHorizontal(View child, int left, int dx) {
- final View drawerView = findDrawerView();
- LayoutParams drawerLp = (LayoutParams) drawerView.getLayoutParams();
- if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
- return Math.max(drawerLp.getMarginStart() - drawerView.getWidth(),
- Math.min(left, drawerLp.getMarginStart()));
- } else {
- return Math.max(drawerLp.getMarginStart() + getWidth() - drawerView.getWidth(),
- Math.min(left, drawerLp.getMarginStart() + getWidth()));
- }
- }
-
- @Override
- public int clampViewPositionVertical(View child, int top, int dy) {
- return child.getTop();
- }
- }
-
- public static class LayoutParams extends MarginLayoutParams {
-
- public int gravity = Gravity.NO_GRAVITY;
- float onScreen;
- boolean knownOpen;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
-
- final TypedArray a = c.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
- gravity = a.getInt(0, Gravity.NO_GRAVITY);
- a.recycle();
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(int width, int height, int gravity) {
- this(width, height);
- this.gravity = gravity;
- }
-
- public LayoutParams(LayoutParams source) {
- super(source);
- gravity = source.gravity;
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- }
-
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
- }
-
- private static final class ViewFaderHolder {
- public final ViewFader viewFader;
- public final int startingColor;
- public final int endingColor;
-
- public ViewFaderHolder(ViewFader viewFader, int startingColor, int endingColor) {
- this.viewFader = viewFader;
- this.startingColor = startingColor;
- this.endingColor = endingColor;
- }
-
- }
-}
diff --git a/car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java b/car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java
deleted file mode 100644
index 04fcd63..0000000
--- a/car-ui-provider/src/android/car/ui/provider/CarRecyclerView.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 android.car.ui.provider;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-/**
- * Clone of {@link android.support.car.ui.CarRecyclerView} to be used by CarUiProvider.
- * Workaround for b/25595320
- */
-public class CarRecyclerView extends android.support.car.ui.CarRecyclerView {
- public CarRecyclerView(Context context) {
- super(context);
- }
-
- public CarRecyclerView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public CarRecyclerView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-}
diff --git a/car-ui-provider/src/android/car/ui/provider/CarUiEntry.java b/car-ui-provider/src/android/car/ui/provider/CarUiEntry.java
deleted file mode 100644
index 3668e03..0000000
--- a/car-ui-provider/src/android/car/ui/provider/CarUiEntry.java
+++ /dev/null
@@ -1,544 +0,0 @@
-/*
- * 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 android.car.ui.provider;
-
-import android.car.app.menu.CarMenuCallbacks;
-import android.car.app.menu.RootMenu;
-import android.car.app.menu.SearchBoxEditListener;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.BitmapDrawable;
-import android.os.Bundle;
-import android.support.car.input.CarRestrictedEditText;
-import android.support.car.ui.DrawerArrowDrawable;
-import android.support.car.ui.PagedListView;
-import android.support.v7.widget.CardView;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.widget.EditText;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.view.View;
-import android.view.LayoutInflater;
-
-import android.support.car.ui.R;
-
-public class CarUiEntry extends android.car.app.menu.CarUiEntry {
- private static final String TAG = "Embedded_CarUiEntry";
-
- // These values and setSearchBoxMode exist rather than separate methods to make sure exactly the
- // same set of things get set for each mode, just to different values.
- /** The search box is not visible. */
- private static final int SEARCH_BOX_MODE_NONE = 0;
- /** The small search box is shown in the header beneath the microphone button. */
- private static final int SEARCH_BOX_MODE_SMALL = 1;
- /** The whole header between the menu button and the microphone button is taken up by the
- * search box. */
- private static final int SEARCH_BOX_MODE_LARGE = 2;
-
- private View mContentView;
- private ImageView mMenuButton;
- private TextView mTitleView;
- private CardView mTruncatedListCardView;
- private CarDrawerLayout mDrawerLayout;
- private DrawerController mDrawerController;
- private PagedListView mListView;
- private DrawerArrowDrawable mDrawerArrowDrawable;
- private CarRestrictedEditText mCarRestrictedEditText;
- private SearchBoxClickListener mSearchBoxClickListener;
-
- private View mSearchBox;
- private View mSearchBoxContents;
- private View mSearchBoxSearchLogoContainer;
- private ImageView mSearchBoxSearchLogo;
- private ImageView mSearchBoxSuperSearchLogo;
- private FrameLayout mSearchBoxEndView;
- private View mTitleContainer;
- private SearchBoxEditListener mSearchBoxEditListener;
-
- public interface SearchBoxClickListener {
- /**
- * The user clicked the search box while it was in small mode.
- */
- void onClick();
- }
-
- public CarUiEntry(Context providerContext, Context appContext) {
- super(providerContext, appContext);
- }
-
- @Override
- public View getContentView() {
- LayoutInflater inflater = LayoutInflater.from(mUiLibContext);
- mContentView = inflater.inflate(R.layout.car_activity, null);
- mDrawerLayout = (CarDrawerLayout) mContentView.findViewById(R.id.drawer_container);
- adjustDrawer();
- mMenuButton = (ImageView) mContentView.findViewById(R.id.car_drawer_button);
- mTitleView = (TextView) mContentView.findViewById(R.id.car_drawer_title);
- mTruncatedListCardView = (CardView) mContentView.findViewById(R.id.truncated_list_card);
- mDrawerArrowDrawable = new DrawerArrowDrawable(mUiLibContext);
- restoreMenuDrawable();
- mListView = (PagedListView) mContentView.findViewById(R.id.list_view);
- mListView.setOnScrollBarListener(mOnScrollBarListener);
- mMenuButton.setOnClickListener(mMenuListener);
- mDrawerController = new DrawerController(this, mMenuButton,
- mDrawerLayout, mListView, mTruncatedListCardView);
- mTitleContainer = mContentView.findViewById(R.id.car_drawer_title_container);
-
- mSearchBoxEndView = (FrameLayout) mContentView.findViewById(R.id.car_search_box_end_view);
- mSearchBox = mContentView.findViewById(R.id.car_search_box);
- mSearchBoxContents = mContentView.findViewById(R.id.car_search_box_contents);
- mSearchBoxSearchLogoContainer = mContentView.findViewById(
- R.id.car_search_box_search_logo_container);
- mSearchBoxSearchLogoContainer.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mSearchBoxClickListener != null) {
- mSearchBoxClickListener.onClick();
- }
- }
- });
- mSearchBoxSearchLogo = (ImageView) mContentView.findViewById(
- R.id.car_search_box_search_logo);
- mSearchBoxSearchLogo.setImageDrawable(mUiLibContext.getResources()
- .getDrawable(R.drawable.ic_google));
- mSearchBoxSuperSearchLogo = (ImageView) mContentView.findViewById(
- R.id.car_search_box_super_logo);
- mSearchBoxSuperSearchLogo.setImageDrawable(mUiLibContext.getResources()
- .getDrawable(R.drawable.ic_googleg));
-
- mCarRestrictedEditText = (CarRestrictedEditText) mContentView.findViewById(
- R.id.car_search_box_edit_text);
- mCarRestrictedEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
- if (mSearchBoxEditListener != null) {
- mSearchBoxEditListener.onSearch(mCarRestrictedEditText.getText().toString());
- }
- return false;
- }
- });
- mCarRestrictedEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence text, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence text, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable text) {
- if (mSearchBoxEditListener != null) {
- mSearchBoxEditListener.onEdit(text.toString());
- }
- }
- });
- setSearchBoxMode(SEARCH_BOX_MODE_NONE);
- return mContentView;
- }
-
- private final View.OnClickListener mMenuListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- CarUiEntry.this.mDrawerController.openDrawer();
- }
- };
-
- @Override
- public void setCarMenuCallbacks(CarMenuCallbacks callbacks){
- RootMenu rootMenu = callbacks.getRootMenu(null);
- if (rootMenu != null) {
- mDrawerController.setRootAndCallbacks(
- rootMenu.getId(), callbacks);
- mDrawerController.setDrawerEnabled(true);
- } else {
- hideMenuButton();
- }
- }
-
- @Override
- public int getFragmentContainerId() {
- return R.id.container;
- }
-
- @Override
- public void setBackground(Bitmap bitmap) {
- BitmapDrawable bd = new BitmapDrawable(mUiLibContext.getResources(), bitmap);
- ImageView bg = (ImageView) mContentView.findViewById(R.id.background);
- bg.setBackground(bd);
- }
-
- @Override
- public void hideMenuButton() {
- mMenuButton.setVisibility(View.GONE);
- }
-
- @Override
- public void restoreMenuDrawable() {
- mMenuButton.setImageDrawable(mDrawerArrowDrawable);
- }
-
- public void setMenuButtonBitmap(Bitmap bitmap) {
- mMenuButton.setImageDrawable(new BitmapDrawable(mUiLibContext.getResources(), bitmap));
- }
-
- @Override
- public void setScrimColor(int color) {
- mDrawerLayout.setScrimColor(color);
- }
-
- @Override
- public void setTitle(CharSequence title) {
- mDrawerController.setTitle(title);
- }
-
- @Override
- public void closeDrawer() {
- mDrawerController.closeDrawer();
- }
-
- @Override
- public void openDrawer() {
- mDrawerController.openDrawer();
- }
-
- @Override
- public void showMenu(String id, String title) {
- mDrawerController.showMenu(id, title);
- }
-
-
- @Override
- public void setMenuButtonColor(int color) {
- setViewColor(mMenuButton, color);
- setViewColor(mTitleView, color);
- }
-
- @Override
- public void showTitle() {
- mTitleView.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideTitle() {
- mTitleView.setVisibility(View.GONE);
- }
-
- @Override
- public void setLightMode() {
- mDrawerController.setLightMode();
- }
-
- @Override
- public void setDarkMode() {
- mDrawerController.setDarkMode();
- }
-
- @Override
- public void setAutoLightDarkMode() {
- mDrawerController.setAutoLightDarkMode();
- }
-
- @Override
- public void showToast(String msg, long duration) {
- // TODO: add toast support
- }
-
- @Override
- public CharSequence getSearchBoxText() {
- return mCarRestrictedEditText.getText();
- }
-
- @Override
- public EditText startInput(String hint,
- View.OnClickListener searchBoxClickListener) {
- mSearchBoxClickListener = wrapSearchBoxClickListener(searchBoxClickListener);
- setSearchBoxModeLarge(hint);
- return mCarRestrictedEditText;
- }
-
-
- @Override
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- if (mDrawerController != null) {
- mDrawerController.restoreState(savedInstanceState);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- if (mDrawerController != null) {
- mDrawerController.saveState(outState);
- }
- }
-
- @Override
- public void onStart() {
-
- }
-
- @Override
- public void onResume() {
-
- }
-
- @Override
- public void onPause() {
-
- }
-
- @Override
- public void onStop() {
-
- }
-
- /**
- * Sets the colors of all the parts of the search box (regardless of whether it is currently
- * showing).
- */
- @Override
- public void setSearchBoxColors(int backgroundColor, int searchLogoColor, int textColor,
- int hintTextColor) {
- // set background color of mSearchBox to get rid of the animation artifact in b/23767062
- mSearchBox.setBackgroundColor(backgroundColor);
- mSearchBoxContents.setBackgroundColor(backgroundColor);
- mSearchBoxSearchLogo.setColorFilter(searchLogoColor, PorterDuff.Mode.SRC_IN);
- mCarRestrictedEditText.setTextColor(textColor);
- mCarRestrictedEditText.setHintTextColor(hintTextColor);
- }
-
- /**
- * Sets the view to be displayed at the end of the search box, or null to clear any existing
- * views.
- */
- @Override
- public void setSearchBoxEndView(View endView) {
- if (endView == null) {
- mSearchBoxEndView.removeAllViews();
- } else if (mSearchBoxEndView.getChildCount() == 0) {
- mSearchBoxEndView.addView(endView);
- } else if (mSearchBoxEndView.getChildAt(0) != endView) {
- mSearchBoxEndView.removeViewAt(0);
- mSearchBoxEndView.addView(endView);
- }
- }
-
- @Override
- public void showSearchBox(final View.OnClickListener listener) {
- setSearchBoxMode(SEARCH_BOX_MODE_SMALL);
- mSearchBoxClickListener = wrapSearchBoxClickListener(listener);
- }
-
- @Override
- public void stopInput() {
- setSearchBoxMode(SEARCH_BOX_MODE_NONE);
- }
-
- @Override
- public void setSearchBoxEditListener(SearchBoxEditListener listener) {
- mSearchBoxEditListener = listener;
- }
-
-
- /**
- * Set the progress of the animated {@link DrawerArrowDrawable}.
- * @param progress 0f displays a menu button
- * 1f displays a back button
- * anything in between will be an interpolation of the drawable between
- * back and menu
- */
- public void setMenuProgress(float progress) {
- mDrawerArrowDrawable.setProgress(progress);
- }
-
- private void setSearchBoxModeLarge(String hint) {
- mCarRestrictedEditText.setHint(hint);
- setSearchBoxMode(SEARCH_BOX_MODE_LARGE);
- }
-
- public void setTitleText(CharSequence title) {
- mTitleView.setText(title);
- }
-
- /**
- * Sets all the view visibilities and layout params for a search box mode.
- */
- private void setSearchBoxMode(int searchBoxMode) {
- // Set the visibility and width of the search box, and whether the rest of the header sits
- // beside or beneath the microphone button.
- LinearLayout.LayoutParams searchBoxLayoutParams =
- (LinearLayout.LayoutParams) mSearchBox.getLayoutParams();
- if (searchBoxMode == SEARCH_BOX_MODE_LARGE) {
- int screenWidth = mAppContext.getResources().getDisplayMetrics().widthPixels;
- int searchBoxMargin = mUiLibContext.getResources()
- .getDimensionPixelSize(R.dimen.car_drawer_header_menu_button_size);
- int maxSearchBoxWidth = mUiLibContext.getResources().getDimensionPixelSize(
- R.dimen.car_card_max_width);
- int searchBoxMarginStart = 0;
- int searchBoxMarginEnd = searchBoxMargin;
- // If the width of search bar is larger than max card width, we adjust margin to fix it.
- if (screenWidth - searchBoxMargin * 2 > maxSearchBoxWidth) {
- searchBoxMarginEnd = (screenWidth - maxSearchBoxWidth) / 2;
- searchBoxMarginStart = searchBoxMarginEnd - searchBoxMargin;
- }
- searchBoxLayoutParams.width = 0;
- searchBoxLayoutParams.weight = 1.0f;
- searchBoxLayoutParams.setMarginStart(searchBoxMarginStart);
- searchBoxLayoutParams.setMarginEnd(searchBoxMarginEnd);
- } else if (searchBoxMode == SEARCH_BOX_MODE_SMALL) {
- searchBoxLayoutParams.width = mUiLibContext.getResources().getDimensionPixelSize(
- R.dimen.car_app_layout_search_box_small_width);
- searchBoxLayoutParams.weight = 0.0f;
- searchBoxLayoutParams.setMarginStart(mUiLibContext.getResources()
- .getDimensionPixelOffset(R.dimen.car_app_layout_search_box_small_margin));
- searchBoxLayoutParams.setMarginEnd(mUiLibContext.getResources().getDimensionPixelOffset(
- R.dimen.car_app_layout_search_box_small_margin));
- } else {
- searchBoxLayoutParams.width = mUiLibContext.getResources().getDimensionPixelSize(
- R.dimen.car_app_layout_search_box_small_width);
- searchBoxLayoutParams.weight = 0.0f;
- searchBoxLayoutParams.setMarginStart(mUiLibContext.getResources().getDimensionPixelSize(
- R.dimen.car_drawer_header_menu_button_size));
- searchBoxLayoutParams.setMarginEnd(-searchBoxLayoutParams.width);
- }
- mSearchBox.setLayoutParams(searchBoxLayoutParams);
-
- // Animate the visibility of the contents of the search box - either the Search logo or the
- // edit text is visible (the super logo also is visible when the edit text is visible).
- View searchBoxEditTextContainer = (View) mCarRestrictedEditText.getParent();
- if (searchBoxMode == SEARCH_BOX_MODE_SMALL) {
- if (mSearchBoxSearchLogoContainer.getVisibility() != View.VISIBLE) {
- mSearchBoxSearchLogoContainer.setAlpha(0f);
- mSearchBoxSearchLogoContainer.setVisibility(View.VISIBLE);
- }
- // 300ms delay to stagger the fade in behind the fade out animation.
- mSearchBoxSearchLogoContainer.animate().alpha(1f).setStartDelay(300);
- // Animate the container so it includes the super G logo.
- if (searchBoxEditTextContainer.getVisibility() == View.VISIBLE) {
- searchBoxEditTextContainer.animate().alpha(0f).setStartDelay(0)
- .withEndAction(mSetEditTextGoneRunnable);
- }
- } else if (searchBoxMode == SEARCH_BOX_MODE_LARGE) {
- if (searchBoxEditTextContainer.getVisibility() != View.VISIBLE) {
- searchBoxEditTextContainer.setAlpha(0f);
- searchBoxEditTextContainer.setVisibility(View.VISIBLE);
- }
- searchBoxEditTextContainer.animate().alpha(1f).setStartDelay(300);
- if (mSearchBoxSearchLogoContainer.getVisibility() == View.VISIBLE) {
- mSearchBoxSearchLogoContainer.animate().alpha(0f).setStartDelay(0)
- .withEndAction(mSetSearchBoxLogoGoneRunnable);
- }
- } else {
- searchBoxEditTextContainer.setVisibility(View.GONE);
- }
-
- // Set the visibility of the title and status containers.
- if (searchBoxMode == SEARCH_BOX_MODE_LARGE) {
- mTitleContainer.setVisibility(View.GONE);
- } else {
- mTitleContainer.setVisibility(View.VISIBLE);
- }
- }
-
-
- private final Runnable mSetEditTextGoneRunnable = new Runnable() {
- @Override
- public void run() {
- ((View) mCarRestrictedEditText.getParent()).setVisibility(View.GONE);
- }
- };
-
- private final Runnable mSetSearchBoxLogoGoneRunnable = new Runnable() {
- @Override
- public void run() {
- mSearchBoxSearchLogoContainer.setVisibility(View.GONE);
- }
- };
-
-
- private SearchBoxClickListener wrapSearchBoxClickListener(final View.OnClickListener listener) {
- return new SearchBoxClickListener() {
- @Override
- public void onClick() {
- listener.onClick(null);
- }
- };
- }
-
-
- private static void setViewColor(View view, int color) {
- if (view instanceof TextView) {
- ((TextView) view).setTextColor(color);
- } else if (view instanceof ImageView) {
- ImageView imageView = (ImageView) view;
- PorterDuffColorFilter filter =
- new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
- imageView.setColorFilter(filter);
- } else {
- if (Log.isLoggable(TAG, Log.WARN)) {
- Log.w(TAG, "Setting color is only supported for TextView and ImageView.");
- }
- }
- }
-
- private void adjustDrawer() {
- Resources resources = mUiLibContext.getResources();
- float width = resources.getDisplayMetrics().widthPixels;
- CarDrawerLayout.LayoutParams layoutParams = new CarDrawerLayout.LayoutParams(
- CarDrawerLayout.LayoutParams.MATCH_PARENT,
- CarDrawerLayout.LayoutParams.MATCH_PARENT);
- layoutParams.gravity = Gravity.LEFT;
- // 1. If the screen width is larger than 800dp, the drawer width is kept as 704dp;
- // 2. Else the drawer width is adjusted to keep the margin end of drawer as 96dp.
-
-// if (width > resources.getDimension(R.dimen.car_standard_width)) {
-// layoutParams.setMarginEnd(
-// (int) (width - resources.getDimension(R.dimen.car_drawer_standard_width)));
-// } else {
-// layoutParams.setMarginEnd(
-// (int) resources.getDimension(R.dimen.car_card_margin));
-// }
- // TODO: For UX, need to update max drawer width for the large screen use case. The previous
- // 704dp width no longer works.
- layoutParams.setMarginEnd((int) resources.getDimension(R.dimen.car_card_margin));
- mContentView.findViewById(R.id.drawer).setLayoutParams(layoutParams);
- }
-
- private final PagedListView.OnScrollBarListener mOnScrollBarListener =
- new PagedListView.OnScrollBarListener() {
-
- @Override
- public void onReachBottom() {
- if (mDrawerController.isTruncatedList()) {
- mTruncatedListCardView.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onLeaveBottom() {
- mTruncatedListCardView.setVisibility(View.GONE);
- }
- };
-}
diff --git a/car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java b/car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java
deleted file mode 100644
index 894938b..0000000
--- a/car-ui-provider/src/android/car/ui/provider/DrawerApiAdapter.java
+++ /dev/null
@@ -1,492 +0,0 @@
-/*
- * 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 android.car.ui.provider;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.SystemProperties;
-import android.support.car.ui.CarListItemViewHolder;
-import android.support.car.ui.PagedListView;
-import android.support.car.ui.R;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.RemoteViews;
-import android.widget.TextView;
-
-import android.support.car.app.menu.CarMenu;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.FLAG_BROWSABLE;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.FLAG_FIRSTITEM;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_EMPTY_PLACEHOLDER;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_FLAGS;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_ID;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_LEFTICON;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_REMOTEVIEWS;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_RIGHTICON;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_RIGHTTEXT;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_TEXT;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_TITLE;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_WIDGET;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_WIDGET_STATE;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.WIDGET_CHECKBOX;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.WIDGET_TEXT_VIEW;
-
-public class DrawerApiAdapter extends RecyclerView.Adapter<CarListItemViewHolder>
- implements PagedListView.ItemCap {
- private static final String TAG = "CAR.UI.ADAPTER";
- private static final String INDEX_OUT_OF_BOUNDS_MESSAGE = "invalid item position";
- private static final String KEY_ID_UNAVAILABLE_CATEGORY = "UNAVAILABLE_CATEGORY";
- private static final String UNLIMITED_MODE_PROPERTY = "android.car.drawer.unlimited";
-
- public interface OnItemSelectedListener {
- void onItemClicked(Bundle item, int position);
- boolean onItemLongClicked(Bundle item);
- }
-
- private final Map<String, Integer> mIdToPosMap = new HashMap<>();
-
- private final Object mItemsLock = new Object();
- private List<Bundle> mItems;
- private boolean mIsCapped;
- private OnItemSelectedListener mListener;
- private int mMaxItems;
- private boolean mUseSmallHolder;
- private boolean mNoLeftIcon;
- private boolean mIsEmptyPlaceholder;
- private int mFirstItemIndex = 0;
-
- private final Handler mHandler = new Handler();
-
- public DrawerApiAdapter() {
- setHasStableIds(true);
- }
-
- @Override
- public int getItemViewType(int position) {
- Bundle item;
- try {
- item = mItems.get(position);
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, INDEX_OUT_OF_BOUNDS_MESSAGE, e);
- return 0;
- }
-
- if (KEY_ID_UNAVAILABLE_CATEGORY.equals(item.getString(KEY_ID))) {
- return R.layout.car_unavailable_category;
- }
-
- if (item.containsKey(KEY_EMPTY_PLACEHOLDER) && item.getBoolean(KEY_EMPTY_PLACEHOLDER)) {
- return R.layout.car_list_item_empty;
- }
-
- int flags = item.getInt(KEY_FLAGS);
- if ((flags & FLAG_BROWSABLE) != 0 || item.containsKey(KEY_RIGHTICON)) {
- return R.layout.car_imageview;
- }
-
- if (!item.containsKey(KEY_WIDGET)) {
- return 0;
- }
-
- switch (item.getInt(KEY_WIDGET)) {
- case WIDGET_CHECKBOX:
- return R.layout.car_menu_checkbox;
- case WIDGET_TEXT_VIEW:
- return R.layout.car_textview;
- default:
- return 0;
- }
- }
-
- @Override
- public CarListItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- View view;
- if (viewType == R.layout.car_unavailable_category ||
- viewType == R.layout.car_list_item_empty) {
- view = inflater.inflate(viewType, parent, false);
- } else {
- view = inflater.inflate(R.layout.car_menu_list_item, parent, false);
- }
- return new CarListItemViewHolder(view, viewType);
- }
-
- @Override
- public void setMaxItems(int maxItems) {
- if (SystemProperties.getBoolean(UNLIMITED_MODE_PROPERTY, false)) {
- mMaxItems = PagedListView.ItemCap.UNLIMITED;
- } else {
- mMaxItems = maxItems;
- }
- }
-
- @Override
- public void onBindViewHolder(final CarListItemViewHolder holder, final int position) {
- if (holder.getItemViewType() == R.layout.car_list_item_empty) {
- onBindEmptyPlaceHolder(holder, position);
- } else if (holder.getItemViewType() == R.layout.car_unavailable_category) {
- onBindUnavailableCategoryView(holder);
- } else {
- onBindNormalView(holder, position);
- if (mIsCapped) {
- // Disable all menu items if it is under unavailable category case.
- // TODO(b/24163545): holder.itemView.setAlpha() doesn't work all the time,
- // which makes some items are gray out, the others are not.
- setHolderStatus(holder, false, 0.3f);
- } else {
- setHolderStatus(holder, true, 1.0f);
- }
- }
-
- holder.itemView.setTag(position);
- holder.itemView.setOnClickListener(mOnClickListener);
- holder.itemView.setOnLongClickListener(mOnLongClickListener);
-
- // Ensure correct day/night mode colors are set and not out of sync.
- setDayNightModeColors(holder);
- }
-
- @Override
- public int getItemCount() {
- synchronized (mItemsLock) {
- if (mItems != null) {
- return mMaxItems != PagedListView.ItemCap.UNLIMITED ?
- Math.min(mItems.size(), mMaxItems) : mItems.size();
- }
- }
- return 0;
- }
-
- @Override
- public long getItemId(int position) {
- synchronized (mItemsLock) {
- if (mItems != null) {
- try {
- return mItems.get(position).getString(KEY_ID).hashCode();
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, "invalid item index", e);
- return RecyclerView.NO_ID;
- }
- }
- }
- return super.getItemId(position);
- }
-
- public synchronized void setItems(List<Bundle> items, boolean isCapped) {
- synchronized (mItemsLock) {
- mItems = items;
- }
- mIsCapped = isCapped;
- mFirstItemIndex = 0;
- if (mItems != null) {
- mIdToPosMap.clear();
- mUseSmallHolder = true;
- mNoLeftIcon = true;
- mIsEmptyPlaceholder = false;
- int index = 0;
- for (Bundle bundle : items) {
- if (bundle.containsKey(KEY_EMPTY_PLACEHOLDER)
- && bundle.getBoolean(KEY_EMPTY_PLACEHOLDER)) {
- mIsEmptyPlaceholder = true;
- if (items.size() != 1) {
- throw new IllegalStateException("Empty placeholder should be the only"
- + "item showing in the menu list!");
- }
- }
-
- if (bundle.containsKey(KEY_TEXT) || bundle.containsKey(KEY_REMOTEVIEWS)) {
- mUseSmallHolder = false;
- }
- if (bundle.containsKey(KEY_LEFTICON)) {
- mNoLeftIcon = false;
- }
- if (bundle.containsKey(KEY_FLAGS) &&
- (bundle.getInt(KEY_FLAGS) & FLAG_FIRSTITEM) != 0) {
- mFirstItemIndex = index;
- }
- mIdToPosMap.put(bundle.getString(KEY_ID), index);
- index++;
- }
- }
- notifyDataSetChanged();
- }
-
- public int getMaxItemsNumber() {
- return mMaxItems;
- }
-
- public void setItemSelectedListener(OnItemSelectedListener listener) {
- mListener = listener;
- }
-
- public int getFirstItemIndex() {
- return mFirstItemIndex;
- }
-
- public boolean isEmptyPlaceholder() {
- return mIsEmptyPlaceholder;
- }
-
- public void onChildChanged(RecyclerView.ViewHolder holder, Bundle bundle) {
- synchronized (mItemsLock) {
- // The holder will be null if the view has not been bound yet
- if (holder != null) {
- int position = holder.getAdapterPosition();
- if (position >= 0 && mItems != null && position < mItems.size()) {
- final Bundle oldBundle;
- try {
- oldBundle = mItems.get(position);
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, INDEX_OUT_OF_BOUNDS_MESSAGE, e);
- return;
- }
- oldBundle.putAll(bundle);
- notifyItemChanged(position);
- }
- } else {
- String id = bundle.getString(KEY_ID);
- int position = mIdToPosMap.get(id);
- if (position >= 0 && mItems != null && position < mItems.size()) {
- final Bundle item;
- try {
- item = mItems.get(position);
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, INDEX_OUT_OF_BOUNDS_MESSAGE, e);
- return;
- }
- if (id.equals(item.getString(KEY_ID))) {
- item.putAll(bundle);
- notifyItemChanged(position);
- }
- }
- }
- }
- }
-
- public void setDayNightModeColors(RecyclerView.ViewHolder viewHolder) {
- CarListItemViewHolder holder = (CarListItemViewHolder) viewHolder;
- Context context = holder.itemView.getContext();
- holder.itemView.setBackgroundResource(R.drawable.car_list_item_background);
- if (holder.getItemViewType() == R.layout.car_unavailable_category) {
- holder.title.setTextAppearance(context, R.style.CarUnavailableCategory);
- if (holder.text != null) {
- holder.text.setTextAppearance(context, R.style.CarUnavailableCategory);
- }
- holder.icon.setImageTintList(ColorStateList
- .valueOf(context.getResources().getColor(R.color.car_unavailable_category)));
- } else {
- holder.title.setTextAppearance(context, R.style.CarBody1);
- if (holder.text != null) {
- holder.text.setTextAppearance(context, R.style.CarBody2);
- }
- if (holder.rightCheckbox != null) {
- holder.rightCheckbox.setButtonTintList(
- ColorStateList.valueOf(context.getResources().getColor(R.color.car_tint)));
- } else if (holder.rightImage != null) {
- Object tag = holder.rightImage.getTag();
- if (tag != null && (int) tag != -1) {
- holder.rightImage.setImageResource((int) tag);
- }
- }
- }
- }
-
- private void onBindEmptyPlaceHolder(final CarListItemViewHolder holder, final int position) {
- maybeSetText(position, KEY_TITLE, holder.title);
- if (!mNoLeftIcon) {
- maybeSetBitmap(position, KEY_LEFTICON, holder.icon);
- holder.iconContainer.setVisibility(View.VISIBLE);
- } else {
- holder.iconContainer.setVisibility(View.GONE);
- }
- }
-
- private void onBindUnavailableCategoryView(final CarListItemViewHolder holder) {
- mNoLeftIcon = false;
- holder.itemView.setEnabled(false);
- }
-
- private void onBindNormalView(final CarListItemViewHolder holder, final int position) {
- maybeSetText(position, KEY_TITLE, holder.title);
- maybeSetText(position, KEY_TEXT, holder.text);
- final Bundle item;
- try {
- item = new Bundle(mItems.get(position));
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, INDEX_OUT_OF_BOUNDS_MESSAGE, e);
- return;
- }
- final int flags = item.getInt(KEY_FLAGS);
- if ((flags & FLAG_BROWSABLE) != 0) {
- // Set the resource id as the tag so we can reload it on day/night mode change.
- // If the tag is -1 or not set, then assume the app will send an updated bitmap
- holder.rightImage.setTag(R.drawable.ic_chevron_right);
- holder.rightImage.setImageResource(R.drawable.ic_chevron_right);
- } else if (holder.rightImage != null) {
- maybeSetBitmap(position, KEY_RIGHTICON, holder.rightImage);
- }
-
- if (holder.rightCheckbox != null) {
- holder.rightCheckbox.setChecked(item.getBoolean(
- KEY_WIDGET_STATE, false));
- holder.rightCheckbox.setOnClickListener(mOnClickListener);
- holder.rightCheckbox.setTag(position);
- }
- if (holder.rightText != null) {
- maybeSetText(position, KEY_RIGHTTEXT, holder.rightText);
- }
- if (!mNoLeftIcon) {
- maybeSetBitmap(position, KEY_LEFTICON, holder.icon);
- holder.iconContainer.setVisibility(View.VISIBLE);
- } else {
- holder.iconContainer.setVisibility(View.GONE);
- }
- if (item.containsKey(KEY_REMOTEVIEWS)) {
- holder.remoteViewsContainer.setVisibility(View.VISIBLE);
- RemoteViews views = item.getParcelable(KEY_REMOTEVIEWS);
- View view = views.apply(holder.remoteViewsContainer.getContext(),
- holder.remoteViewsContainer);
- holder.remoteViewsContainer.removeAllViews();
- holder.remoteViewsContainer.addView(view);
- } else {
- holder.remoteViewsContainer.removeAllViews();
- holder.remoteViewsContainer.setVisibility(View.GONE);
- }
-
- // Set the view holder size
- Resources r = holder.itemView.getResources();
- ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
- params.height = mUseSmallHolder ?
- r.getDimensionPixelSize(R.dimen.car_list_item_height_small) :
- r.getDimensionPixelSize(R.dimen.car_list_item_height);
- holder.itemView.setLayoutParams(params);
-
- // Set Icon size
- params = holder.iconContainer.getLayoutParams();
- params.height = params.width = mUseSmallHolder ?
- r.getDimensionPixelSize(R.dimen.car_list_item_small_icon_size) :
- r.getDimensionPixelSize(R.dimen.car_list_item_icon_size);
-
- }
-
- private void maybeSetText(int position, String key, TextView view) {
- Bundle item;
- try {
- item = mItems.get(position);
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, INDEX_OUT_OF_BOUNDS_MESSAGE, e);
- return;
- }
- if (item.containsKey(key)) {
- view.setText(item.getString(key));
- view.setVisibility(View.VISIBLE);
- } else {
- view.setVisibility(View.GONE);
- }
- }
-
- private void maybeSetBitmap(int position, String key, ImageView view) {
- Bundle item;
- try {
- item = mItems.get(position);
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, INDEX_OUT_OF_BOUNDS_MESSAGE, e);
- return;
- }
- if (item.containsKey(key)) {
- view.setImageBitmap((Bitmap) item.getParcelable(key));
- view.setVisibility(View.VISIBLE);
- view.setTag(-1);
- } else {
- view.setVisibility(View.GONE);
- }
- }
-
- private void setHolderStatus(final CarListItemViewHolder holder,
- boolean isEnabled, float alpha) {
- holder.itemView.setEnabled(isEnabled);
- if (holder.icon != null) {
- holder.icon.setAlpha(alpha);
- }
- if (holder.title != null) {
- holder.title.setAlpha(alpha);
- }
- if (holder.text != null) {
- holder.text.setAlpha(alpha);
- }
- if (holder.rightCheckbox != null) {
- holder.rightCheckbox.setAlpha(alpha);
- }
- if (holder.rightImage != null) {
- holder.rightImage.setAlpha(alpha);
- }
- if (holder.rightText != null) {
- holder.rightText.setAlpha(alpha);
- }
- }
-
- private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- final Bundle item;
- int position = (int) view.getTag();
- try {
- item = mItems.get(position);
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, INDEX_OUT_OF_BOUNDS_MESSAGE, e);
- return;
- }
- View right = view.findViewById(R.id.right_item);
- if (right != null && view != right && right instanceof CompoundButton) {
- ((CompoundButton) right).toggle();
- }
- if (mListener != null) {
- mListener.onItemClicked(item, position);
- }
- }
- };
-
- private final View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View view) {
- final Bundle item;
- try {
- item = mItems.get((int) view.getTag());
- } catch (IndexOutOfBoundsException e) {
- Log.w(TAG, INDEX_OUT_OF_BOUNDS_MESSAGE, e);
- return true;
- }
- final String id = item.getString(KEY_ID);
- if (mListener != null) {
- return mListener.onItemLongClicked(item);
- }
- return false;
- }
- };
-}
diff --git a/car-ui-provider/src/android/car/ui/provider/DrawerController.java b/car-ui-provider/src/android/car/ui/provider/DrawerController.java
deleted file mode 100644
index 241224a..0000000
--- a/car-ui-provider/src/android/car/ui/provider/DrawerController.java
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
- * 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 android.car.ui.provider;
-
-import android.car.app.menu.CarMenuCallbacks;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.os.Bundle;
-import android.support.car.ui.PagedListView;
-import android.support.car.ui.R;
-import android.support.v7.widget.CardView;
-import android.support.v7.widget.RecyclerView;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.ProgressBar;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.Stack;
-
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.FLAG_BROWSABLE;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_FLAGS;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_ID;
-import static android.car.app.menu.CarMenuConstants.MenuItemConstants.KEY_TITLE;
-
-/**
- * Controls the drawer for SDK app
- */
-public class DrawerController
- implements CarDrawerLayout.DrawerListener, DrawerApiAdapter.OnItemSelectedListener,
- CarDrawerLayout.DrawerControllerListener {
- private static final String TAG = "CAR.UI.DrawerController";
- // Qualify with full package name to make it less likely there will be a collision
- private static final String KEY_IDS = "android.support.car.ui.drawer.sdk.IDS";
- private static final String KEY_DRAWERSTATE =
- "android.support.car.ui.drawer.sdk.DRAWER_STATE";
- private static final String KEY_TITLES = "android.support.car.ui.drawer.sdk.TITLES";
- private static final String KEY_ROOT = "android.support.car.ui.drawer.sdk.ROOT";
- private static final String KEY_ID_UNAVAILABLE_CATEGORY = "UNAVAILABLE_CATEGORY";
- private static final String KEY_CLICK_STACK =
- "android.support.car.ui.drawer.sdk.CLICK_STACK";
- private static final String KEY_MAX_PAGES =
- "android.support.car.ui.drawer.sdk.MAX_PAGES";
- private static final String KEY_IS_CAPPED =
- "android.support.car.ui.drawer.sdk.IS_CAPPED";
-
- /** Drawer is in Auto dark/light mode */
- private static final int MODE_AUTO = 0;
- /** Drawer is in Light mode */
- private static final int MODE_LIGHT = 1;
- /** Drawer is in Dark mode */
- private static final int MODE_DARK = 2;
-
- private final Stack<String> mSubscriptionIds = new Stack<>();
- private final Stack<CharSequence> mTitles = new Stack<>();
- private final SubscriptionCallbacks mSubscriptionCallbacks = new SubscriptionCallbacks();
- // Named to be consistent with CarDrawerFragment to make copying code easier and less error
- // prone
- private final CarDrawerLayout mContainer;
- private final PagedListView mListView;
-// private final CardView mTruncatedListCardView;
- private final ProgressBar mProgressBar;
- private final Context mContext;
- private final ViewAnimationController mPlvAnimationController;
- private final CardView mTruncatedListCardView;
- private final Stack<Integer> mClickCountStack = new Stack<>();
-
- private CarMenuCallbacks mCarMenuCallbacks;
- private DrawerApiAdapter mAdapter;
- private int mScrimColor = CarDrawerLayout.DEFAULT_SCRIM_COLOR;
- private boolean mIsDrawerOpen;
- private boolean mIsDrawerAnimating;
- private boolean mIsCapped;
- private int mItemsNumber;
- private int mDrawerMode;
- private CharSequence mContentTitle;
- private String mRootId;
- private boolean mRestartedFromDayNightMode;
- private CarUiEntry mUiEntry;
-
- public DrawerController(CarUiEntry uiEntry, View menuButton, CarDrawerLayout drawerLayout,
- PagedListView listView, CardView cardView) {
- //mCarAppLayout = appLayout;
- menuButton.setOnClickListener(mMenuClickListener);
- mContainer = drawerLayout;
- mListView = listView;
- mUiEntry = uiEntry;
- mTruncatedListCardView = cardView;
- mListView.setDefaultItemDecoration(new DrawerMenuListDecoration(mListView.getContext()));
- mProgressBar = (ProgressBar) mContainer.findViewById(R.id.progress);
- mContext = mListView.getContext();
- mPlvAnimationController = new ViewAnimationController(
- mListView, R.anim.car_list_in, R.anim.sdk_list_out, R.anim.car_list_pop_out);
- mRootId = null;
-
- mContainer.setDrawerListener(this);
- mContainer.setDrawerControllerListener(this);
- setAutoLightDarkMode();
- }
-
-
- @Override
- public void onDrawerOpened(View drawerView) {
- mIsDrawerOpen = true;
- mIsDrawerAnimating = false;
- mUiEntry.setMenuProgress(1.0f);
- // This can be null on day/night mode changes
- if (mCarMenuCallbacks != null) {
- mCarMenuCallbacks.onCarMenuOpened();
- }
- }
-
- @Override
- public void onDrawerClosed(View drawerView) {
- mIsDrawerOpen = false;
- mIsDrawerAnimating = false;
- clearMenu();
- mUiEntry.setMenuProgress(0);
- mUiEntry.setTitle(mContentTitle);
- // This can be null on day/night mode changes
- if (mCarMenuCallbacks != null) {
- mCarMenuCallbacks.onCarMenuClosed();
- }
- }
-
- @Override
- public void onDrawerStateChanged(int newState) {
- }
-
- @Override
- public void onDrawerOpening(View drawerView) {
- mIsDrawerAnimating = true;
- // This can be null on day/night mode changes
- if (mCarMenuCallbacks != null) {
- mCarMenuCallbacks.onCarMenuOpening();
- }
- }
-
- @Override
- public void onDrawerSlide(View drawerView, float slideOffset) {
- mUiEntry.setMenuProgress(slideOffset);
- }
-
- @Override
- public void onDrawerClosing(View drawerView) {
- mIsDrawerAnimating = true;
- // This can be null on day/night mode changes
- if (mCarMenuCallbacks != null) {
- mCarMenuCallbacks.onCarMenuClosing();
- }
- }
-
- @Override
- public void onItemClicked(Bundle item, int position) {
- // Don't allow selection while animating
- if (mPlvAnimationController.isAnimating()) {
- return;
- }
- int flags = item.getInt(KEY_FLAGS);
- String id = item.getString(KEY_ID);
-
- // Page number is 0 index, + 1 for the actual click.
- int clicksUsed = mListView.getPage(position) + 1;
- mClickCountStack.push(clicksUsed);
- mListView.setMaxPages(mListView.getMaxPages() - clicksUsed);
- mCarMenuCallbacks.onItemClicked(id);
- if ((flags & FLAG_BROWSABLE) != 0) {
- if (mListView.getMaxPages() == 0) {
- mIsCapped = true;
- }
- CharSequence title = item.getString(KEY_TITLE);
- if (TextUtils.isEmpty(title)) {
- title = mContentTitle;
- }
- mUiEntry.setTitleText(title);
- mTitles.push(title);
- if (!mSubscriptionIds.isEmpty()) {
- mPlvAnimationController.enqueueExitAnimation(mClearAdapterRunnable);
- }
- mProgressBar.setVisibility(View.VISIBLE);
- if (!mSubscriptionIds.isEmpty()) {
- mCarMenuCallbacks.unsubscribe(mSubscriptionIds.peek(), mSubscriptionCallbacks);
- }
- mSubscriptionIds.push(id);
- subscribe(id);
- } else {
- closeDrawer();
- }
- }
-
- @Override
- public boolean onItemLongClicked(Bundle item) {
- return mCarMenuCallbacks.onItemLongClicked(item.getString(KEY_ID));
- }
-
- @Override
- public void onBack() {
- backOrClose();
- }
-
- @Override
- public boolean onScroll() {
- // Consume scroll event if we are animating.
- return mPlvAnimationController.isAnimating();
- }
-
- public void setTitle(CharSequence title) {
- Log.d(TAG, "setTitle in drawer" + title);
- if (!TextUtils.isEmpty(title)) {
- mContentTitle = title;
- mUiEntry.showTitle();
- mUiEntry.setTitleText(title);
- } else {
- mUiEntry.hideTitle();
- }
- }
-
- public void setRootAndCallbacks(String rootId, CarMenuCallbacks callbacks) {
- mAdapter = new DrawerApiAdapter();
- mAdapter.setItemSelectedListener(this);
- mListView.setAdapter(mAdapter);
- mCarMenuCallbacks = callbacks;
- // HACK: Due to the handler, setRootId will be called after onRestoreState.
- // If onRestoreState has been called, the root id will already be set. So nothing to do.
- if (mSubscriptionIds.isEmpty()) {
- setRootId(rootId);
- } else {
- subscribe(mSubscriptionIds.peek());
- openDrawer();
- }
- }
-
- public void saveState(Bundle out) {
- out.putStringArray(KEY_IDS, mSubscriptionIds.toArray(new String[mSubscriptionIds.size()]));
- out.putStringArray(KEY_TITLES, mTitles.toArray(new String[mTitles.size()]));
- out.putString(KEY_ROOT, mRootId);
- out.putBoolean(KEY_DRAWERSTATE, mIsDrawerOpen);
- out.putIntegerArrayList(KEY_CLICK_STACK, new ArrayList<Integer>(mClickCountStack));
- out.putBoolean(KEY_IS_CAPPED, mIsCapped);
- out.putInt(KEY_MAX_PAGES, mListView.getMaxPages());
- }
-
- public void restoreState(Bundle in) {
- if (in != null) {
- // Restore subscribed CarMenu ids
- String[] ids = in.getStringArray(KEY_IDS);
- mSubscriptionIds.clear();
- if (ids != null) {
- mSubscriptionIds.addAll(Arrays.asList(ids));
- }
- // Restore drawer titles if there are any
- String[] titles = in.getStringArray(KEY_TITLES);
- mTitles.clear();
- if (titles != null) {
- mTitles.addAll(Arrays.asList(titles));
- }
- if (!mTitles.isEmpty()) {
- mUiEntry.setTitleText(mTitles.peek());
- }
- mRootId = in.getString(KEY_ROOT);
- mIsDrawerOpen = in.getBoolean(KEY_DRAWERSTATE);
- ArrayList<Integer> clickCount = in.getIntegerArrayList(KEY_CLICK_STACK);
- mClickCountStack.clear();
- if (clickCount != null) {
- mClickCountStack.addAll(clickCount);
- }
- mIsCapped = in.getBoolean(KEY_IS_CAPPED);
- mListView.setMaxPages(in.getInt(KEY_MAX_PAGES));
- if (!mRestartedFromDayNightMode && mIsDrawerOpen) {
- closeDrawer();
- }
- }
- }
-
- public void setScrimColor(int color) {
- mScrimColor = color;
- mContainer.setScrimColor(color);
- updateViewFaders();
- }
-
- public void setAutoLightDarkMode() {
- mDrawerMode = MODE_AUTO;
- mContainer.setAutoDayNightMode();
- updateViewFaders();
- }
-
- public void setLightMode() {
- mDrawerMode = MODE_LIGHT;
- mContainer.setLightMode();
- updateViewFaders();
- }
-
- public void setDarkMode() {
- mDrawerMode = MODE_DARK;
- mContainer.setDarkMode();
- updateViewFaders();
- }
-
- public void openDrawer() {
- // If we have no root, then we can't open the drawer.
- if (mRootId == null) {
- return;
- }
- mContainer.openDrawer();
- }
-
- public void closeDrawer() {
- if (mRootId == null) {
- return;
- }
- mTruncatedListCardView.setVisibility(View.GONE);
- mPlvAnimationController.stopAndClearAnimations();
- mContainer.closeDrawer();
- mUiEntry.setTitle(mContentTitle);
- }
-
- public void setDrawerEnabled(boolean enabled) {
- if (enabled) {
- mContainer.setDrawerLockMode(CarDrawerLayout.LOCK_MODE_UNLOCKED);
- } else {
- mContainer.setDrawerLockMode(CarDrawerLayout.LOCK_MODE_LOCKED_CLOSED);
- }
- }
-
- public void showMenu(String id, String title) {
- // The app wants to show the menu associated with the given id. Create a fake item using the
- // given inputs and then pretend as if the user clicked on the item, so that the drawer
- // will subscribe to that menu id, set the title appropriately, and properly handle the
- // subscription stack.
- Bundle bundle = new Bundle();
- bundle.putString(KEY_ID, id);
- bundle.putString(KEY_TITLE, title);
- bundle.putInt(KEY_FLAGS, FLAG_BROWSABLE);
- onItemClicked(bundle, 0 /* position */);
- }
-
- public void setRootId(String rootId) {
- mRootId = rootId;
- }
-
- public void setRestartedFromDayNightMode(boolean restarted) {
- mRestartedFromDayNightMode = restarted;
- }
-
- public boolean isTruncatedList() {
- int maxItems = mAdapter.getMaxItemsNumber();
- return maxItems != PagedListView.ItemCap.UNLIMITED && mItemsNumber > maxItems;
- }
-
- private void clearMenu() {
- if (!mSubscriptionIds.isEmpty()) {
- mCarMenuCallbacks.unsubscribe(mSubscriptionIds.peek(), mSubscriptionCallbacks);
- mSubscriptionIds.clear();
- mTitles.clear();
- }
- mListView.setVisibility(View.GONE);
- mListView.resetMaxPages();
- mClickCountStack.clear();
- mIsCapped = false;
- }
-
- /**
- * Check if the drawer is inside of a CarAppLayout and add the relevant views if it is,
- * automagically add view faders for the correct views
- */
- private void updateViewFaders() {
- mContainer.removeViewFader(mStatusViewViewFader);
- mContainer.addViewFader(mStatusViewViewFader);
- }
-
- private void subscribe(String id) {
- mProgressBar.setVisibility(View.VISIBLE);
- mCarMenuCallbacks.subscribe(id, mSubscriptionCallbacks);
- }
-
- private final CarDrawerLayout.ViewFader mStatusViewViewFader = new CarDrawerLayout.ViewFader() {
- @Override
- public void setColor(int color) {
- mUiEntry.setMenuButtonColor(color);
- }
- };
-
- private void backOrClose() {
- if (mSubscriptionIds.size() > 1) {
- mPlvAnimationController.enqueueBackAnimation(mClearAdapterRunnable);
- mProgressBar.setVisibility(View.VISIBLE);
- mCarMenuCallbacks.unsubscribe(mSubscriptionIds.pop(),
- mSubscriptionCallbacks);
- subscribe(mSubscriptionIds.peek());
- // Restore the title for this menu level.
- mTitles.pop();
- CharSequence title = mTitles.peek();
- if (TextUtils.isEmpty(title)) {
- title = mContentTitle;
- }
- mUiEntry.setTitleText(title);
- } else {
- closeDrawer();
- }
- }
-
- private final View.OnClickListener mMenuClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mIsDrawerAnimating || mCarMenuCallbacks.onMenuClicked()) {
- return;
- }
- // Check if drawer has root set.
- if (mRootId == null) {
- return;
- }
- mTruncatedListCardView.setVisibility(View.GONE);
- if (mIsDrawerOpen) {
- if (!mClickCountStack.isEmpty()) {
- mListView.setMaxPages(mListView.getMaxPages() + mClickCountStack.pop());
- }
- mIsCapped = false;
- backOrClose();
- } else {
- mSubscriptionIds.push(mRootId);
- mTitles.push(mContentTitle);
- subscribe(mRootId);
- openDrawer();
- }
- }
- };
-
- private final Runnable mClearAdapterRunnable = new Runnable() {
- @Override
- public void run() {
- mListView.setVisibility(View.GONE);
- }
- };
-
- public void updateDayNightMode() {
- mContainer.findViewById(R.id.drawer).setBackgroundColor(
- mContext.getResources().getColor(R.color.car_card));
- mListView.setAutoDayNightMode();
- switch (mDrawerMode) {
- case MODE_AUTO:
- setAutoLightDarkMode();
- break;
- case MODE_LIGHT:
- setLightMode();
- break;
- case MODE_DARK:
- setDarkMode();
- break;
- }
- updateViewFaders();
- RecyclerView rv = mListView.getRecyclerView();
- for (int i = 0; i < mAdapter.getItemCount(); ++i) {
- mAdapter.setDayNightModeColors(rv.findViewHolderForAdapterPosition(i));
- }
- }
-
- private static class ViewAnimationController implements Animation.AnimationListener {
- private final Animation mExitAnim;
- private final Animation mEnterAnim;
- private final Animation mBackAnim;
- private final View mView;
- private final Context mContext;
- private final Queue<Animation> mQueue = new LinkedList<>();
-
- private Runnable mOnEnterAnimStartRunnable;
- private Runnable mOnExitAnimCompleteRunnable;
-
- private Animation mCurrentAnimation;
-
- public ViewAnimationController(View view, int enter, int exit, int back) {
- mView = view;
- mContext = view.getContext();
-
- mEnterAnim = AnimationUtils.loadAnimation(mContext, enter);
- mExitAnim = AnimationUtils.loadAnimation(mContext, exit);
- mBackAnim = AnimationUtils.loadAnimation(mContext, back);
-
- mExitAnim.setAnimationListener(this);
- mEnterAnim.setAnimationListener(this);
- mBackAnim.setAnimationListener(this);
- }
-
- @Override
- public void onAnimationStart(Animation animation) {
- if (animation == mEnterAnim && mOnEnterAnimStartRunnable != null) {
- mOnEnterAnimStartRunnable.run();
- mOnEnterAnimStartRunnable = null;
- }
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- if ((animation == mExitAnim || animation == mBackAnim)
- && mOnExitAnimCompleteRunnable != null) {
- mOnExitAnimCompleteRunnable.run();
- mOnExitAnimCompleteRunnable = null;
- }
- Animation nextAnimation = mQueue.poll();
- if (nextAnimation != null) {
- mCurrentAnimation = animation;
- mView.startAnimation(nextAnimation);
- } else {
- mCurrentAnimation = null;
- }
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
-
- }
-
- public void enqueueEnterAnimation(Runnable r) {
- if (r != null) {
- mOnEnterAnimStartRunnable = r;
- }
- enqueueAnimation(mEnterAnim);
- }
-
- public void enqueueExitAnimation(Runnable r) {
- // If the view isn't visible, don't play the exit animation.
- // It will cause flicker.
- if (mView.getVisibility() != View.VISIBLE) {
- return;
- }
- if (r != null) {
- mOnExitAnimCompleteRunnable = r;
- }
- enqueueAnimation(mExitAnim);
- }
-
- public void enqueueBackAnimation(Runnable r) {
- // If the view isn't visible, don't play the back animation.
- if (mView.getVisibility() != View.VISIBLE) {
- return;
- }
- if (r != null) {
- mOnExitAnimCompleteRunnable = r;
- }
- enqueueAnimation(mBackAnim);
- }
-
- public synchronized void stopAndClearAnimations() {
- if (mExitAnim.hasStarted()) {
- mExitAnim.cancel();
- }
-
- if (mEnterAnim.hasStarted()) {
- mEnterAnim.cancel();
- }
-
- mQueue.clear();
- mCurrentAnimation = null;
- }
-
- public boolean isAnimating() {
- return mCurrentAnimation != null;
- }
-
- private synchronized void enqueueAnimation(final Animation animation) {
- if (mQueue.contains(animation)) {
- return;
- }
- if (mCurrentAnimation != null) {
- mQueue.add(animation);
- } else {
- mCurrentAnimation = animation;
- mView.startAnimation(animation);
- }
- }
- }
-
- private class SubscriptionCallbacks extends android.car.app.menu.SubscriptionCallbacks {
- private final Object mItemLock = new Object();
- private volatile List<Bundle> mItems;
-
- @Override
- public void onChildrenLoaded(String parentId, final List<Bundle> items) {
- if (mSubscriptionIds.isEmpty() || parentId.equals(mSubscriptionIds.peek())) {
- // Add unavailable category explanation at the first item of menu.
- if (mIsCapped) {
- Bundle extra = new Bundle();
- extra.putString(KEY_ID, KEY_ID_UNAVAILABLE_CATEGORY);
- items.add(0, extra);
- }
- mItems = items;
- mItemsNumber = mItems.size();
- mProgressBar.setVisibility(View.GONE);
- mPlvAnimationController.enqueueEnterAnimation(new Runnable() {
- @Override
- public void run() {
- synchronized (mItemLock) {
- mAdapter.setItems(mItems, mIsCapped);
- mListView.setVisibility(View.VISIBLE);
- mItems = null;
- }
- mListView.scrollToPosition(mAdapter.getFirstItemIndex());
- }
- });
- }
- }
-
- @Override
- public void onError(String id) {
- // TODO: do something useful here.
- }
-
- @Override
- public void onChildChanged(String parentId, Bundle bundle) {
- if (!mSubscriptionIds.isEmpty() && parentId.equals(mSubscriptionIds.peek())) {
- // List is still animating, so adapter hasn't been updated. Update the list that
- // needs to be set.
- String id = bundle.getString(KEY_ID);
- synchronized (mItemLock) {
- if (mItems != null) {
- for (Bundle item : mItems) {
- if (item.getString(KEY_ID).equals(id)) {
- item.putAll(bundle);
- break;
- }
- }
- return;
- }
- }
- RecyclerView rv = mListView.getRecyclerView();
- RecyclerView.ViewHolder holder = rv.findViewHolderForItemId(id.hashCode());
- mAdapter.onChildChanged(holder, bundle);
- }
- }
- }
-
- private class DrawerMenuListDecoration extends PagedListView.Decoration {
-
- public DrawerMenuListDecoration(Context context) {
- super(context);
- }
-
- @Override
- public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
- if (mAdapter != null && mAdapter.isEmptyPlaceholder()) {
- return;
- }
- super.onDrawOver(c, parent, state);
- }
- }
-}
diff --git a/car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java b/car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java
deleted file mode 100644
index 4311c5a..0000000
--- a/car-ui-provider/src/android/car/ui/provider/MaxWidthLayout.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 android.car.ui.provider;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-/**
- * Clone of {@link android.support.car.ui.MaxWidthLayout} to be used by CarUiProvider.
- * Workaround for b/25595320
- */
-public class MaxWidthLayout extends android.support.car.ui.MaxWidthLayout {
- public MaxWidthLayout(Context context) {
- super(context);
- }
-
- public MaxWidthLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public MaxWidthLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-}
\ No newline at end of file
diff --git a/car-ui-provider/src/android/car/ui/provider/PagedListView.java b/car-ui-provider/src/android/car/ui/provider/PagedListView.java
deleted file mode 100644
index 9648dd6..0000000
--- a/car-ui-provider/src/android/car/ui/provider/PagedListView.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 android.car.ui.provider;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-/**
- * Clone of {@link android.support.car.ui.PagedListView} to be used by CarUiProvider.
- * Workaround for b/25595320
- */
-public class PagedListView extends android.support.car.ui.PagedListView {
- public PagedListView(Context context, AttributeSet attrs) {
- this(context, attrs, 0 /*defStyleAttrs*/, 0 /*defStyleRes*/);
- }
-
- public PagedListView(Context context, AttributeSet attrs, int defStyleAttrs) {
- this(context, attrs, defStyleAttrs, 0 /*defStyleRes*/);
- }
-
- public PagedListView(Context context, AttributeSet attrs, int defStyleAttrs, int defStyleRes) {
- super(context, attrs, defStyleAttrs, defStyleRes);
- }
-}
diff --git a/car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java b/car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java
deleted file mode 100644
index 91a25ab..0000000
--- a/car-ui-provider/src/android/car/ui/provider/PagedScrollBarView.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 android.car.ui.provider;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-/**
- * Clone of {@link android.support.car.ui.PagedScrollBarView} to be used by CarUiProvider.
- * Workaround for b/25595320
- */
-public class PagedScrollBarView extends android.support.car.ui.PagedScrollBarView {
- public PagedScrollBarView(
- Context context, AttributeSet attrs) {
- this(context, attrs, 0 /*defStyleAttrs*/, 0 /*defStyleRes*/);
- }
-
- public PagedScrollBarView(
- Context context, AttributeSet attrs, int defStyleAttrs) {
- this(context, attrs, defStyleAttrs, 0 /*defStyleRes*/);
- }
-
- public PagedScrollBarView(
- Context context, AttributeSet attrs, int defStyleAttrs, int defStyleRes) {
- super(context, attrs, defStyleAttrs, defStyleRes);
- }
-}
diff --git a/car-usb-handler/res/values/strings.xml b/car-usb-handler/res/values/strings.xml
index 2242648..be1e7a2 100644
--- a/car-usb-handler/res/values/strings.xml
+++ b/car-usb-handler/res/values/strings.xml
@@ -26,4 +26,5 @@
<string name="usb_pref_delete_yes">Yes</string>
<string name="usb_pref_delete_cancel">Cancel</string>
<string name="usb_resolving_handlers">Getting supported handlers</string>
+ <string name="usb_unknown_device">Unknown USB device</string>
</resources>
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java b/car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java
index 5084414..288f598 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java
@@ -17,7 +17,6 @@
import android.content.ComponentName;
import android.hardware.usb.UsbDevice;
-import com.android.internal.util.Preconditions;
/**
* Settings for USB device.
@@ -34,8 +33,6 @@
private boolean mDefaultHandler;
UsbDeviceSettings(String serialNumber, int vid, int pid) {
- Preconditions.checkNotNull(serialNumber);
-
mSerialNumber = serialNumber;
mVid = vid;
mPid = pid;
@@ -96,7 +93,15 @@
* Checks if setting matches {@code UsbDevice}.
*/
public boolean matchesDevice(UsbDevice device) {
- return getSerialNumber().equals(device.getSerialNumber());
+ String deviceSerial = device.getSerialNumber();
+ if (AoapInterface.isDeviceInAoapMode(device)) {
+ return mAoap && deviceSerial.equals(mSerialNumber);
+ } else if (deviceSerial == null) {
+ return mVid == device.getVendorId() && mPid == device.getProductId();
+ } else {
+ return mVid == device.getVendorId() && mPid == device.getProductId()
+ && deviceSerial.equals(mSerialNumber);
+ }
}
/**
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbHostController.java b/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
index b705928..5c61a98 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
@@ -68,10 +68,10 @@
public void onReceive(Context context, Intent intent) {
if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
- unsetActiveDeviceIfSerialMatch(device);
+ unsetActiveDeviceIfMatch(device);
} else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
- setActiveDeviceIfSerialMatch(device);
+ setActiveDeviceIfMatch(device);
}
}
};
@@ -79,9 +79,6 @@
@GuardedBy("this")
private UsbDevice mActiveDevice;
- @GuardedBy("this")
- private String mProcessingDeviceSerial;
-
public UsbHostController(Context context, UsbHostControllerCallbacks callbacks) {
mContext = context;
mCallback = callbacks;
@@ -96,17 +93,17 @@
}
- private synchronized void setActiveDeviceIfSerialMatch(UsbDevice device) {
- if (device != null && device.getSerialNumber() != null
- && device.getSerialNumber().equals(mProcessingDeviceSerial)) {
+ private synchronized void setActiveDeviceIfMatch(UsbDevice device) {
+ if (mActiveDevice != null && device != null
+ && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
mActiveDevice = device;
}
}
- private synchronized void unsetActiveDeviceIfSerialMatch(UsbDevice device) {
+ private synchronized void unsetActiveDeviceIfMatch(UsbDevice device) {
mHandler.requestDeviceRemoved();
- if (mActiveDevice != null && mActiveDevice.getSerialNumber() != null
- && mActiveDevice.getSerialNumber().equals(device.getSerialNumber())) {
+ if (mActiveDevice != null && device != null
+ && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
mActiveDevice = null;
}
}
@@ -114,7 +111,6 @@
private synchronized boolean startDeviceProcessingIfNull(UsbDevice device) {
if (mActiveDevice == null) {
mActiveDevice = device;
- mProcessingDeviceSerial = device.getSerialNumber();
return true;
}
return false;
@@ -122,7 +118,6 @@
private synchronized void stopDeviceProcessing() {
mActiveDevice = null;
- mProcessingDeviceSerial = null;
}
private synchronized UsbDevice getActiveDevice() {
@@ -131,8 +126,22 @@
private boolean deviceMatchedActiveDevice(UsbDevice device) {
UsbDevice activeDevice = getActiveDevice();
- return activeDevice != null && activeDevice.getSerialNumber() != null
- && activeDevice.getSerialNumber().equals(device.getSerialNumber());
+ return activeDevice != null && UsbUtil.isDevicesMatching(activeDevice, device);
+ }
+
+ private String generateTitle() {
+ String manufacturer = mActiveDevice.getManufacturerName();
+ String product = mActiveDevice.getProductName();
+ if (manufacturer == null && product == null) {
+ return mContext.getString(R.string.usb_unknown_device);
+ }
+ if (manufacturer != null && product != null) {
+ return manufacturer + " " + product;
+ }
+ if (manufacturer != null) {
+ return manufacturer;
+ }
+ return product;
}
/**
@@ -147,7 +156,7 @@
mCallback.optionsUpdated(mEmptyList);
mCallback.processingStateChanged(true);
- UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device.getSerialNumber());
+ UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device);
if (settings != null && mUsbResolver.dispatch(
mActiveDevice, settings.getHandler(), settings.getAoap())) {
if (LOCAL_LOGV) {
@@ -156,7 +165,7 @@
}
return;
}
- mCallback.titleChanged(device.getManufacturerName() + " " + device.getProductName());
+ mCallback.titleChanged(generateTitle());
mUsbResolver.resolve(device);
}
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java b/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
index 157c92f..1b251f8 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
@@ -22,6 +22,7 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.hardware.usb.UsbDevice;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -47,26 +48,43 @@
mDbHelper = new UsbSettingsDbHelper(context);
}
+ private Cursor queryFor(SQLiteDatabase db, UsbDevice device) {
+ String serial = device.getSerialNumber();
+ String selection;
+ String[] selectionArgs;
+ if (AoapInterface.isDeviceInAoapMode(device)) {
+ selection = COLUMN_SERIAL + " = ? AND " + COLUMN_AOAP + " = 1";
+ selectionArgs = new String[] {serial};
+ } else if (serial == null) {
+ selection = COLUMN_SERIAL + " IS NULL AND "
+ + COLUMN_VID + " = ? AND " + COLUMN_PID + " = ?";
+ selectionArgs = new String[] {
+ Integer.toString(device.getVendorId()),
+ Integer.toString(device.getProductId())};
+ } else {
+ selection =
+ COLUMN_SERIAL + " = ? AND " + COLUMN_VID + " = ? AND " + COLUMN_PID + " = ?";
+ selectionArgs = new String[] {
+ device.getSerialNumber(),
+ Integer.toString(device.getVendorId()),
+ Integer.toString(device.getProductId())};
+ }
+ return db.query(TABLE_USB_SETTINGS, null, selection, selectionArgs, null, null, null);
+ }
+
/**
* Returns settings for {@serialNumber} or null if it doesn't exist.
*/
@Nullable
- public UsbDeviceSettings getSettings(String serialNumber) {
+ public UsbDeviceSettings getSettings(UsbDevice device) {
try (SQLiteDatabase db = mDbHelper.getReadableDatabase();
- Cursor resultCursor = db.query(
- TABLE_USB_SETTINGS,
- null,
- COLUMN_SERIAL + " = ?",
- new String[]{serialNumber},
- null,
- null,
- null)) {
+ Cursor resultCursor = queryFor(db, device)) {
if (resultCursor.getCount() > 1) {
- throw new RuntimeException("Querying for serial number: " + serialNumber
+ throw new RuntimeException("Querying for device: " + device
+ " returned " + resultCursor.getCount() + " results");
}
if (resultCursor.getCount() == 0) {
- Log.w(TAG, "Usb setting missing for device serial: " + serialNumber);
+ Log.w(TAG, "Usb setting missing for device: " + device);
return null;
}
List<UsbDeviceSettings> settings = constructSettings(resultCursor);
@@ -168,7 +186,7 @@
private static class UsbSettingsDbHelper extends SQLiteOpenHelper {
- private static final int DATABASE_VERSION = 1;
+ private static final int DATABASE_VERSION = 2;
private static final String DATABASE_NAME = "usb_devices.db";
UsbSettingsDbHelper(Context context) {
@@ -177,20 +195,47 @@
@Override
public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE_USB_SETTINGS + " ("
+ createTable(db, TABLE_USB_SETTINGS);
+ createSerialIndex(db);
+ }
+
+ private void createTable(SQLiteDatabase db, String tableName) {
+ db.execSQL("CREATE TABLE " + tableName + " ("
+ COLUMN_SERIAL + " TEXT,"
+ COLUMN_VID + " INTEGER,"
+ COLUMN_PID + " INTEGER,"
+ COLUMN_NAME + " TEXT, "
+ COLUMN_HANDLER + " TEXT,"
+ COLUMN_AOAP + " INTEGER,"
- + COLUMN_DEFAULT_HANDLER + " INTEGER," + "PRIMARY KEY (" + COLUMN_SERIAL
+ + COLUMN_DEFAULT_HANDLER + " INTEGER,"
+ + "PRIMARY KEY (" + COLUMN_SERIAL + ", " + COLUMN_VID + ", " + COLUMN_PID
+ "))");
}
+ private void createSerialIndex(SQLiteDatabase db) {
+ db.execSQL("CREATE INDEX " + TABLE_USB_SETTINGS + "_" + COLUMN_SERIAL + " ON "
+ + TABLE_USB_SETTINGS + "(" + COLUMN_SERIAL + ")");
+ }
+
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- // Do nothing at this point. Not required for v1 database.
+ for (; oldVersion != newVersion; oldVersion++) {
+ switch (oldVersion) {
+ case 1:
+ String tempTableName = "temp_" + TABLE_USB_SETTINGS;
+ createTable(db, tempTableName);
+ db.execSQL("INSERT INTO " + tempTableName
+ + " SELECT * FROM " + TABLE_USB_SETTINGS);
+ db.execSQL("DROP TABLE " + TABLE_USB_SETTINGS);
+ db.execSQL("ALTER TABLE " + tempTableName + " RENAME TO "
+ + TABLE_USB_SETTINGS);
+ createSerialIndex(db);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown database version " + oldVersion);
+ }
+ }
}
}
}
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index 16aa275..1c2e7ee 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -74,7 +74,6 @@
PRODUCT_PACKAGES += \
vehicle_monitor_service \
CarService \
- CarUiProvider \
CarTrustAgentService \
CarDialerApp \
CarRadioApp \
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index 3e8d776..4576c79 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -83,15 +83,18 @@
A2dpSinkService \
# EVS resources
-PRODUCT_PACKAGES += android.hardware.automotive.evs@1.0-service
PRODUCT_PACKAGES += android.automotive.evs.manager@1.0
PRODUCT_PACKAGES += evs_app
+# The following packages, or their vendor specific equivalents should be include in the device.mk
+#PRODUCT_PACKAGES += evs_app_default_resources
+#PRODUCT_PACKAGES += android.hardware.automotive.evs@1.0-service
+#PRODUCT_PACKAGES += android.hardware.automotive.evs@1.0-sample
-ifeq ($(TARGET_USES_CAR_FUTURE_FEATURES),true)
+# Device running Android is a car
+PRODUCT_COPY_FILES += \
+ frameworks/native/data/etc/android.hardware.type.automotive.xml:system/etc/permissions/android.hardware.type.automotive.xml
+
PRODUCT_PACKAGES += android.hardware.automotive.vehicle@2.1-service
-else
-PRODUCT_PACKAGES += android.hardware.automotive.vehicle@2.0-service
-endif
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_minimal.mk)
diff --git a/car_product/overlay/frameworks/base/core/res/res/drawable-night-nodpi/default_wallpaper.png b/car_product/overlay/frameworks/base/core/res/res/drawable-night-nodpi/default_wallpaper.png
new file mode 100644
index 0000000..9a4033e
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable-night-nodpi/default_wallpaper.png
Binary files differ
diff --git a/car_product/overlay/frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.png b/car_product/overlay/frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.png
index 22441ae..8d20548 100644
--- a/car_product/overlay/frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.png
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.png
Binary files differ
diff --git a/car_product/overlay/frameworks/base/core/res/res/drawable-sw600dp-night/default_wallpaper.png b/car_product/overlay/frameworks/base/core/res/res/drawable-sw600dp-night/default_wallpaper.png
new file mode 100644
index 0000000..9a4033e
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable-sw600dp-night/default_wallpaper.png
Binary files differ
diff --git a/car_product/overlay/frameworks/base/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png b/car_product/overlay/frameworks/base/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
index cc651de..8d20548 100644
--- a/car_product/overlay/frameworks/base/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
Binary files differ
diff --git a/car_product/overlay/frameworks/base/core/res/res/drawable-sw720dp-night/default_wallpaper.png b/car_product/overlay/frameworks/base/core/res/res/drawable-sw720dp-night/default_wallpaper.png
new file mode 100644
index 0000000..9a4033e
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable-sw720dp-night/default_wallpaper.png
Binary files differ
diff --git a/car_product/overlay/frameworks/base/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png b/car_product/overlay/frameworks/base/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
index 03b93aa..8d20548 100644
--- a/car_product/overlay/frameworks/base/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
Binary files differ
diff --git a/car_product/overlay/frameworks/base/core/res/res/drawable/ic_collapse_notification.xml b/car_product/overlay/frameworks/base/core/res/res/drawable/ic_collapse_notification.xml
new file mode 100644
index 0000000..0252706
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable/ic_collapse_notification.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="56dp"
+ android:height="56dp"
+ android:viewportWidth="56.0"
+ android:viewportHeight="56.0">
+ <path
+ android:pathData="M28,56C12.54,56 0,43.46 0,28C0,12.54 12.54,0 28,0C43.46,0 56,12.54 56,28C56,43.46 43.46,56 28,56ZM35.64,33L38,30.71L28,21L18,30.71L20.36,33L28,25.58L35.64,33Z"
+ android:strokeColor="#00000000"
+ android:fillColor="@android:color/black"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/car_product/overlay/frameworks/base/core/res/res/drawable/ic_expand_notification.xml b/car_product/overlay/frameworks/base/core/res/res/drawable/ic_expand_notification.xml
new file mode 100644
index 0000000..c93390c
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable/ic_expand_notification.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="56dp"
+ android:height="56dp"
+ android:viewportWidth="56.0"
+ android:viewportHeight="56.0">
+ <path
+ android:pathData="M28,0C43.46,0 56,12.54 56,28C56,43.46 43.46,56 28,56C12.54,56 0,43.46 0,28C0,12.54 12.54,0 28,0ZM20.36,23L18,25.29L28,35L38,25.29L35.64,23L28,30.42L20.36,23Z"
+ android:strokeColor="#00000000"
+ android:fillColor="@android:color/black"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/car-support-lib/res/values-w480dp/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/drawable/notification_action_bg.xml
similarity index 73%
rename from car-support-lib/res/values-w480dp/dimens.xml
rename to car_product/overlay/frameworks/base/core/res/res/drawable/notification_action_bg.xml
index b4c611a..f13b28a 100644
--- a/car-support-lib/res/values-w480dp/dimens.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/drawable/notification_action_bg.xml
@@ -13,8 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
- <dimen name="stream_margin_size">24dp</dimen>
- <dimen name="stream_content_keyline_2">116dp</dimen>
- <dimen name="stream_card_keyline_2">116dp</dimen>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#ffeeeeee" />
+ <corners
+ android:bottomRightRadius="16dp"
+ android:bottomLeftRadius="16dp"/>
+</shape>
diff --git a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action.xml b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action.xml
new file mode 100644
index 0000000..cefd830
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_action.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+<Button
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ android:id="@+id/action0"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:fontFamily="sans-serif"
+ android:gravity="start|center_vertical"
+ android:layout_marginStart="0dp"
+ android:textColor="@color/notification_default_color"
+ android:textSize="@dimen/notification_text_size"
+ android:textStyle="normal"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:paddingStart="@dimen/notification_content_margin_start"
+ android:paddingEnd="@dimen/notification_content_margin_start"
+ android:background="@drawable/notification_material_action_background" />
diff --git a/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_media_action.xml b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_media_action.xml
new file mode 100644
index 0000000..29aafd4
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/layout/notification_material_media_action.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<ImageButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:id="@+id/action0"
+ android:background="@drawable/notification_material_media_action_background"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/media_notification_action_button_size"
+ android:layout_weight="1"
+ android:padding="0dp"
+ android:gravity="center"
+ android:scaleType="fitCenter" />
diff --git a/car_product/overlay/frameworks/base/core/res/res/layout/notification_template_right_icon.xml b/car_product/overlay/frameworks/base/core/res/res/layout/notification_template_right_icon.xml
new file mode 100644
index 0000000..068e8b6
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/layout/notification_template_right_icon.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/right_icon"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="76dp"
+ android:layout_marginBottom="76dp"
+ android:layout_gravity="center_vertical|end"
+ android:scaleType="centerCrop" />
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-h600dp/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/values-h600dp/dimens.xml
new file mode 100644
index 0000000..b659bef
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/values-h600dp/dimens.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) -->
+ <dimen name="notification_title_text_size">40sp</dimen>
+
+ <!-- Size of notification text (see TextAppearance.StatusBar.EventContent) -->
+ <dimen name="notification_text_size">32sp</dimen>
+
+ <!-- The absolute size of the application icon in the notification header. -->
+ <dimen name="notification_header_icon_size">32dp</dimen>
+
+ <!-- The height of the header of a notification. -->
+ <dimen name="notification_header_height">76dp</dimen>
+
+ <!-- Top margin to accommodate for the header before the notification content. This value
+ is smaller than the notification_header_height to bring the text closer. Otherwise,
+ spacing in the font itself makes the space look too large. -->
+ <dimen name="notification_content_margin_top">68dp</dimen>
+
+ <!-- The height of the notification action list. -->
+ <dimen name="notification_action_list_height">96dp</dimen>
+
+ <!-- The size of the media actions in the media notification. -->
+ <dimen name="media_notification_action_button_size">56dp</dimen>
+</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml b/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
new file mode 100644
index 0000000..2774c9e
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, 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.
+*/
+-->
+<resources>
+ <!-- The background color for the container of notification actions. -->
+ <color name="notification_action_list">#ff11181d</color> <!-- Dark Blue Grey 800 -->
+</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-w840dp/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/values-w840dp/dimens.xml
new file mode 100644
index 0000000..ea826b4
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/values-w840dp/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<resources>
+ <!-- The margin on the start of the content view. This value should match card keyline1. -->
+ <dimen name="notification_content_margin_start">32dp</dimen>
+
+ <!-- The margin on the end of the content view with a picture. This value is the size of
+ the right icon (64dp) + notification_content_margin_end + 16dp. -->
+ <dimen name="notification_content_picture_margin">112dp</dimen>
+</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/colors.xml b/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
new file mode 100644
index 0000000..f7a7a12
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, 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.
+*/
+-->
+<resources>
+ <!-- The background color for the container of notification actions. -->
+ <color name="notification_action_list">#ffeeeeee</color> <!-- Grey 200 -->
+</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index 2a97f64..a9fb0e7 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -48,7 +48,16 @@
<integer name="config_jobSchedulerIdleWindowSlop">0</integer>
<bool name="config_supportsMultiWindow">false</bool>
+
<!-- Automotive Bluetooth pairing option -->
<bool name="enable_pbap_pce_profile">true</bool>
+ <!-- Component name of a custom ResolverActivity (Intent resolver) to be used instead of
+ the default framework version. -->
+ <string name="config_customResolverActivity" translatable="false">com.android.support.car.lenspicker/.LensResolverActivity</string>
+
+ <!-- Flag indicating that the entire notification header can be clicked to expand the
+ notification. If false, then the expand icon has to be clicked in order for the expand
+ to occur. -->
+ <bool name="config_notificationHeaderClickableForExpand">true</bool>
</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
index 136ca80..125e1fb 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
@@ -20,4 +20,88 @@
<dimen name="status_bar_height">56dp</dimen>
<dimen name="navigation_bar_height_car_mode">112dp</dimen>
<dimen name="status_bar_icon_size">40dp</dimen>
+
+ <!-- The height of the header of a notification. -->
+ <dimen name="notification_header_height">58dp</dimen>
+
+ <!-- The absolute size of the notification expand icon. -->
+ <dimen name="notification_header_expand_icon_size">55dp</dimen>
+
+ <!-- The top padding for the notification expand button. -->
+ <dimen name="notification_expand_button_padding_top">0dp</dimen>
+
+ <!-- The end margin after the application icon in the notification header -->
+ <dimen name="notification_header_icon_margin_end">10dp</dimen>
+
+ <!-- The absolute size of the application icon in the notification header. -->
+ <dimen name="notification_header_icon_size">24dp</dimen>
+
+ <!-- The margins before and after each of the items in the notification header.-->
+ <dimen name="notification_header_separating_margin">6dp</dimen>
+
+ <!-- The margins before the start of the app name in the header. -->
+ <dimen name="notification_header_app_name_margin_start">@dimen/notification_header_separating_margin</dimen>
+
+ <!-- The padding at the top of the notification header. -->
+ <dimen name="notification_header_padding_top">0dp</dimen>
+
+ <!-- The padding at the bottom of the notification header. -->
+ <dimen name="notification_header_padding_bottom">0dp</dimen>
+
+ <!-- The margin at the bottom of the notification header. -->
+ <dimen name="notification_header_margin_bottom">0dp</dimen>
+
+ <!-- The absolute height for the header in a media notification. -->
+ <dimen name="media_notification_header_height">@dimen/notification_header_height</dimen>
+
+ <!-- Top margin to accommodate for the header before the notification content. This value
+ is 8dp smaller than the notification_header_height to bring the text closer. Otherwise,
+ spacing in the font itself makes the space look too large. -->
+ <dimen name="notification_content_margin_top">55dp</dimen>
+
+ <!-- The bottom margin after the notification content.-->
+ <dimen name="notification_content_margin_bottom">24dp</dimen>
+
+ <!-- The margin on the start of the content view. This value should match card keyline1. -->
+ <dimen name="notification_content_margin_start">24dp</dimen>
+
+ <!-- The margin on the end of the content view. Keep in sync with
+ notification_content_plus_picture_margin! -->
+ <dimen name="notification_content_margin_end">@dimen/notification_content_margin_start</dimen>
+
+ <!-- The margin on the end of the content view with a picture. This value is the size of
+ the right icon (64dp) + notification_content_margin_end + 16dp. -->
+ <dimen name="notification_content_picture_margin">106dp</dimen>
+
+ <!-- The margin on the end of the content view with a picture, plus the standard
+ content end margin. -->
+ <dimen name="notification_content_plus_picture_margin_end">80dp</dimen>
+
+ <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title). -->
+ <dimen name="notification_title_text_size">32sp</dimen>
+
+ <!-- Size of notification text (see TextAppearance.StatusBar.EventContent). -->
+ <dimen name="notification_text_size">26sp</dimen>
+
+ <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2,
+ Info, Time). -->
+ <dimen name="notification_subtext_size">@dimen/notification_text_size</dimen>
+
+ <!-- The margin on top of the text of the notification. -->
+ <dimen name="notification_text_margin_top">0dp</dimen>
+
+ <!-- The height of the notification action list. -->
+ <dimen name="notification_action_list_height">76dp</dimen>
+
+ <!-- The size of the media actions in the media notification. -->
+ <dimen name="media_notification_action_button_size">36dp</dimen>
+
+ <!-- The bottom padding for the media actions container. -->
+ <dimen name="media_notification_actions_padding_bottom">0dp</dimen>
+
+ <!-- The height of the progress bar. -->
+ <dimen name="notification_progress_bar_height">25dp</dimen>
+
+ <!-- The top margin before the notification progress bar. -->
+ <dimen name="notification_progress_margin_top">16dp</dimen>
</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/styles.xml b/car_product/overlay/frameworks/base/core/res/res/values/styles.xml
index de6f6ec..7bcedb9 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/styles.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/styles.xml
@@ -64,4 +64,15 @@
<item name="fragmentFadeEnterAnimation">@animator/fragment_fade_enter</item>
<item name="fragmentFadeExitAnimation">@animator/fragment_fade_exit</item>
</style>
+
+ <!-- The style for the container of media actions in a notification. -->
+ <style name="NotificationMediaActionContainer">
+ <item name="background">@color/notification_action_list</item>
+ <item name="layout_width">match_parent</item>
+ <item name="layout_height">@dimen/notification_action_list_height</item>
+ <item name="layout_marginTop">0dp</item>
+ <item name="paddingStart">0dp</item>
+ <item name="paddingBottom">@dimen/media_notification_actions_padding_bottom</item>
+ <item name="gravity">center</item>
+ </style>
</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values-h600dp/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-h600dp/dimens.xml
new file mode 100644
index 0000000..3140afc
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-h600dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<resources>
+ <!-- The height of the header for a container containing child notifications. -->
+ <dimen name="notification_children_container_header_height">96dp</dimen>
+
+ <!-- The top margin for the notification children container in its non-expanded form. This
+ value is smaller than notification_children_container_header_height to bring the first
+ child closer so there is less wasted space. -->
+ <dimen name="notification_children_container_margin_top">88dp</dimen>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml
new file mode 100644
index 0000000..be66b5b
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<resources>
+ <color name="status_bar_background_color">#ff000000</color>
+ <color name="system_bar_background_opaque">#ff0c1013</color>
+</resources>
diff --git a/car-support-lib/res/values-w840dp/integers.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-sw600dp/dimens.xml
similarity index 83%
rename from car-support-lib/res/values-w840dp/integers.xml
rename to car_product/overlay/frameworks/base/packages/SystemUI/res/values-sw600dp/dimens.xml
index 5910519..7b85870 100644
--- a/car-support-lib/res/values-w840dp/integers.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<resources>
- <integer name="stream_num_of_columns">12</integer>
- <integer name="stream_card_default_column_span">8</integer>
+ <!-- The width of the panel holding the notification cards. -->
+ <dimen name="notification_panel_width">744dp</dimen>
</resources>
diff --git a/car-support-lib/res/values-wheel/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-w1024dp/dimens.xml
similarity index 76%
rename from car-support-lib/res/values-wheel/dimens.xml
rename to car_product/overlay/frameworks/base/packages/SystemUI/res/values-w1024dp/dimens.xml
index f9bcc38..932bae2 100644
--- a/car-support-lib/res/values-wheel/dimens.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-w1024dp/dimens.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 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.
@@ -14,6 +14,6 @@
limitations under the License.
-->
<resources>
- <!-- stream -->
- <dimen name="car_paged_list_view_scrollbar_thumb_margin">4dp</dimen>
+ <!-- The width of panel holding the notification card. -->
+ <dimen name="notification_panel_width">744dp</dimen>
</resources>
diff --git a/car-ui-provider/res/values-h480dp/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-w550dp-land/dimens.xml
similarity index 84%
rename from car-ui-provider/res/values-h480dp/dimens.xml
rename to car_product/overlay/frameworks/base/packages/SystemUI/res/values-w550dp-land/dimens.xml
index 44d9df6..2248352 100644
--- a/car-ui-provider/res/values-h480dp/dimens.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-w550dp-land/dimens.xml
@@ -14,5 +14,6 @@
limitations under the License.
-->
<resources>
- <dimen name="lens_header_height">112dp</dimen>
+ <!-- The width of panel holding the notification card. -->
+ <dimen name="notification_panel_width">522dp</dimen>
</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
index 7b6e5eb..b66ff92 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
@@ -22,4 +22,13 @@
<color name="docked_divider_background">@color/car_grey_50</color>
<color name="system_bar_background_opaque">#ff172026</color>
+
+ <color name="status_bar_background_color">#33000000</color>
+ <drawable name="system_bar_background">@color/status_bar_background_color</drawable>
+
+ <!-- The scrim color for the background of the notifications shade. -->
+ <color name="scrim_behind_color">#172026</color>
+
+ <!-- The color of the dividing line between grouped notifications. -->
+ <color name="notification_divider_color">@*android:color/notification_action_list</color>
</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
index b5aae40..a9f231c 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
@@ -22,4 +22,81 @@
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.car.CarStatusBar</string>
<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.car.CarSystemUIFactory</string>
<bool name="config_enableFullscreenUserSwitcher">true</bool>
+
+ <!-- Notifications on the car should not show the gear icon. Swiping should only dismiss the
+ cards. -->
+ <bool name="config_showNotificationGear">false</bool>
+
+ <!-- No need to draw a background around a notification because there is no gear icon. -->
+ <bool name="config_drawNotificationBackground">false</bool>
+
+ <!-- No quick settings the quick settings row should not be shown.-->
+ <bool name="config_showQuickSettingsRow">false</bool>
+
+ <!-- The quick settings are not available on the car and should not be editable. -->
+ <bool name="config_showQuickSettingsEditingIcon">false</bool>
+
+ <!-- The multi-user switcher should always be visible because quick settings cannot be
+ expanded. Thus, there is no other way to access this. -->
+ <bool name="config_alwaysShowMultiUserSwitcher">true</bool>
+
+ <!-- The quick settings should not be available for expansion in the car. -->
+ <bool name="config_showQuickSettingsExpandIndicator">false</bool>
+
+ <!-- There are no quick settings, so it should not be revealed with scrolling. -->
+ <bool name="config_enableQuickSettingsOverscrollExpansion">false</bool>
+
+ <!-- The notification shade should only be shown on a facet click and not by dragging. -->
+ <bool name="config_enableNotificationShadeDrag">false</bool>
+
+ <!-- There should not be the ability to clear all notifications with a button. -->
+ <bool name="config_enableNotificationsClearAll">false</bool>
+
+ <!-- Hide the notification shelf so that the cards in the notification center scroll smoothly
+ off-screen. -->
+ <bool name="config_showNotificationShelf">false</bool>
+
+ <!-- The notifications should always fade when being dismissed. -->
+ <bool name="config_fadeNotificationsOnDismiss">true</bool>
+
+ <!-- The entire notification row should be translated because the cards are smaller than the
+ width of the screen. If the row is not translated, then they will be clipped. -->
+ <bool name="config_translateNotificationContentsOnSwipe">false</bool>
+
+ <!-- The notifications should fade as they are being swiped off screen. -->
+ <bool name="config_fadeDependingOnAmountSwiped">true</bool>
+
+ <!-- The expand icon should be displayed at the top right corner of the notifications. -->
+ <bool name="config_showNotificationExpandButtonAtEnd">true</bool>
+
+ <!-- A notification card that has been scrolled off screen should not be clipped in height. This
+ maintains the illusion that the cards are being scrolled underneath the status bar
+ shelf. -->
+ <bool name="config_clipNotificationScrollToTop">false</bool>
+
+ <!-- The auto notification have rounded corners. Ensure that any content is clipped to these
+ corners. -->
+ <bool name="config_clipNotificationsToOutline">true</bool>
+
+ <!-- Notifications should always be in their expanded state so that the actions are visible.
+ This will make it easier for a auto user to interact with them. -->
+ <bool name="config_alwaysExpandNonGroupedNotifications">true</bool>
+
+ <!-- Auto does not allow notifications to be toggled to and from their expanded states to
+ reduce driver distraction. -->
+ <bool name="config_enableNonGroupedNotificationExpand">false</bool>
+
+ <!-- There should always be a dividing line between notifications. -->
+ <bool name="config_showDividersWhenGroupNotificationExpanded">true</bool>
+
+ <!--- Hide the dividing lines when the notification group is expanding. -->
+ <bool name="config_hideDividersDuringTransition">true</bool>
+
+ <!-- Child notifications are displayed with no dividing space between them in auto, so disable
+ the shadow. -->
+ <bool name="config_enableShadowOnChildNotifications">false</bool>
+
+ <!-- Keep the notification background when the container has been expanded. The children will
+ expand inline within the container, so it can keep its original background. -->
+ <bool name="config_showGroupNotificationBgWhenExpanded">true</bool>
</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
index 1001784..5d675fe 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
@@ -17,6 +17,10 @@
*/
-->
<resources>
+ <!-- The alpha for the scrim behind the notification shade. This value is 1 so that the
+ scrim has no transparency. -->
+ <item name="scrim_behind_alpha" format="float" type="dimen">1.0</item>
+
<!-- The amount by which to scale up the status bar icons. -->
<item name="status_bar_icon_scale_factor" format="float" type="dimen">2.3</item>
@@ -70,4 +74,66 @@
<!-- Largest size an avatar might need to be drawn in the user picker, status bar, or
quick settings header -->
<dimen name="max_avatar_size">128dp</dimen>
+
+ <!-- The width of panel holding the notification card. -->
+ <dimen name="notification_panel_width">522dp</dimen>
+
+ <!-- The width of the quick settings panel. -1 for match_parent. -->
+ <dimen name="qs_panel_width">-1px</dimen>
+
+ <!-- Height of a small notification in the status bar-->
+ <dimen name="notification_min_height">600dp</dimen>
+
+ <!-- Height of a small notification in the status bar which was used before android N -->
+ <dimen name="notification_min_height_legacy">600dp</dimen>
+
+ <!-- Height of a large notification in the status bar -->
+ <dimen name="notification_max_height">600dp</dimen>
+
+ <!-- Height of a heads up notification in the status bar for legacy custom views -->
+ <dimen name="notification_max_heads_up_height_legacy">600dp</dimen>
+
+ <!-- Height of a heads up notification in the status bar -->
+ <dimen name="notification_max_heads_up_height">600dp</dimen>
+
+ <!-- Height of the status bar header bar -->
+ <dimen name="status_bar_header_height">54dp</dimen>
+
+ <!-- The height of the divider between the individual notifications. -->
+ <dimen name="notification_divider_height">16dp</dimen>
+
+ <!-- The height of the divider between the individual notifications when the notification
+ wants it to be increased. This value is the same as notification_divider_height so that
+ the spacing between all notifications will always be the same. -->
+ <dimen name="notification_divider_height_increased">@dimen/notification_divider_height</dimen>
+
+ <!-- The alpha of the dividing line between child notifications of a notification group. -->
+ <item name="notification_divider_alpha" format="float" type="dimen">1.0</item>
+
+ <!-- The width of each individual notification card. -->
+ <dimen name="notification_child_width">522dp</dimen>
+
+ <!-- The top margin of the notification panel. -->
+ <dimen name="notification_panel_margin_top">32dp</dimen>
+
+ <!-- The bottom margin of the panel that holds the list of notifications. -->
+ <dimen name="notification_panel_margin_bottom">@dimen/notification_divider_height</dimen>
+
+ <!-- The corner radius of the shadow behind the notification. -->
+ <dimen name="notification_shadow_radius">16dp</dimen>
+
+ <!-- The amount of space below the notification list. This value is 0 so the list scrolls
+ all the way to the bottom. -->
+ <dimen name="close_handle_underlap">0dp</dimen>
+
+ <!-- The height of the divider between the individual notifications in a notification group. -->
+ <dimen name="notification_children_container_divider_height">1dp</dimen>
+
+ <!-- The height of the header for a container containing child notifications. -->
+ <dimen name="notification_children_container_header_height">76dp</dimen>
+
+ <!-- The top margin for the notification children container in its non-expanded form. This
+ value is smaller than notification_children_container_header_height to bring the first
+ child closer so there is less wasted space. -->
+ <dimen name="notification_children_container_margin_top">68dp</dimen>
</resources>
diff --git a/car_product/sepolicy/evs_app.te b/car_product/sepolicy/evs_app.te
new file mode 100644
index 0000000..0e8881e
--- /dev/null
+++ b/car_product/sepolicy/evs_app.te
@@ -0,0 +1,14 @@
+# evs app
+type evs_app, domain;
+type evs_app_exec, exec_type, file_type;
+
+allow evs_app evs_app_exec:dir search;
+allow evs_app evs_driver:binder call;
+allow evs_app evs_mock:binder call;
+allow evs_app gpu_device:chr_file ioctl;
+allow evs_app hal_graphics_allocator_default:fd use;
+allow evs_app hal_vehicle_default:binder call;
+
+init_daemon_domain(evs_app)
+
+binder_use(evs_app);
diff --git a/car_product/sepolicy/evs_driver.te b/car_product/sepolicy/evs_driver.te
new file mode 100644
index 0000000..1307616
--- /dev/null
+++ b/car_product/sepolicy/evs_driver.te
@@ -0,0 +1,12 @@
+# evs_driver mock hardware driver service
+type evs_driver, domain;
+type evs_driver_exec, exec_type, file_type;
+
+allow evs_driver hwservicemanager:binder { call transfer };
+allow evs_driver hwservicemanager_prop:file { getattr open read };
+allow evs_driver device:dir { open read };
+allow evs_driver surfaceflinger:binder call;
+
+init_daemon_domain(evs_driver)
+
+binder_use(evs_driver);
diff --git a/car_product/sepolicy/evs_manager.te b/car_product/sepolicy/evs_manager.te
new file mode 100644
index 0000000..f5c4ba8
--- /dev/null
+++ b/car_product/sepolicy/evs_manager.te
@@ -0,0 +1,11 @@
+# evs manager
+type evs_manager, domain;
+type evs_manager_exec, exec_type, file_type;
+
+allow evs_manager hwservicemanager:binder { call transfer };
+allow evs_manager hwservicemanager_prop:file { getattr open read };
+allow evs_manager evs_driver:binder call;
+
+init_daemon_domain(evs_manager)
+
+binder_use(evs_manager);
diff --git a/car_product/sepolicy/evs_mock.te b/car_product/sepolicy/evs_mock.te
new file mode 100644
index 0000000..b89b1ba
--- /dev/null
+++ b/car_product/sepolicy/evs_mock.te
@@ -0,0 +1,11 @@
+# evs_mock mock hardware driver service
+type evs_mock, domain;
+type evs_mock_exec, exec_type, file_type;
+
+allow evs_mock hwservicemanager:binder { call transfer };
+allow evs_mock hwservicemanager_prop:file { getattr open read };
+allow evs_mock hal_graphics_allocator_default:fd use;
+
+init_daemon_domain(evs_mock)
+
+binder_use(evs_mock);
diff --git a/car_product/sepolicy/file_contexts b/car_product/sepolicy/file_contexts
index 25cbef2..53759c7 100644
--- a/car_product/sepolicy/file_contexts
+++ b/car_product/sepolicy/file_contexts
@@ -6,4 +6,11 @@
/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.vehicle@2\.0-service u:object_r:hal_vehicle_default_exec:s0
/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.vehicle@2\.1-service u:object_r:hal_vehicle_default_exec:s0
+
+/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.evs@1\.0-service u:object_r:evs_mock_exec:s0
+/system/bin/android\.hardware\.automotive\.evs@1\.0-sample u:object_r:evs_driver_exec:s0
+/system/bin/android\.automotive\.evs\.manager@1\.0 u:object_r:evs_manager_exec:s0
+/system/bin/evs_app u:object_r:evs_app_exec:s0
+/system/etc/automotive/evs(/.*)? u:object_r:evs_app_exec:s0
+
###################################
diff --git a/evs/app/Android.mk b/evs/app/Android.mk
index 1978616..f5d9b6e 100644
--- a/evs/app/Android.mk
+++ b/evs/app/Android.mk
@@ -6,37 +6,75 @@
LOCAL_SRC_FILES := \
evs_app.cpp \
EvsStateControl.cpp \
- StreamHandler.cpp \
+ RenderBase.cpp \
+ RenderDirectView.cpp \
+ RenderTopView.cpp \
ConfigManager.cpp \
-
-LOCAL_C_INCLUDES += \
- frameworks/base/include \
- packages/services/Car/evs/app \
+ glError.cpp \
+ shader.cpp \
+ TexWrapper.cpp \
+ VideoTex.cpp \
+ StreamHandler.cpp \
+ WindowSurface.cpp \
+ FormatConvert.cpp \
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
libutils \
libui \
- libhwbinder \
+ libgui \
libhidlbase \
libhidltransport \
- libGLESv1_CM \
- libOpenSLES \
- libtinyalsa \
+ libEGL \
+ libGLESv2 \
libhardware \
+ libpng \
android.hardware.automotive.evs@1.0 \
android.hardware.automotive.vehicle@2.0 \
LOCAL_STATIC_LIBRARIES := \
+ libmath \
libjsoncpp \
LOCAL_STRIP_MODULE := keep_symbols
+LOCAL_INIT_RC := evs_app.rc
+
LOCAL_MODULE:= evs_app
LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -DLOG_TAG=\"EVSAPP\"
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := config.json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/automotive/evs
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CarFromTop.png
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/automotive/evs
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := LabeledChecker.png
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/automotive/evs
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := evs_app_default_resources
+LOCAL_REQUIRED_MODULES := \
+ config.json \
+ CarFromTop.png \
+ LabeledChecker.png
+include $(BUILD_PHONY_PACKAGE)
\ No newline at end of file
diff --git a/evs/app/CarFromTop.png b/evs/app/CarFromTop.png
new file mode 100644
index 0000000..11f929e
--- /dev/null
+++ b/evs/app/CarFromTop.png
Binary files differ
diff --git a/evs/app/ConfigManager.cpp b/evs/app/ConfigManager.cpp
index 48966d9..07e570d 100644
--- a/evs/app/ConfigManager.cpp
+++ b/evs/app/ConfigManager.cpp
@@ -49,24 +49,6 @@
}
-static bool ReadChildNodeAsUint(const char* groupName,
- const Json::Value& parentNode,
- const char* childName,
- unsigned* value) {
- // Must have a place to put the value!
- assert(value);
-
- Json::Value childNode = parentNode[childName];
- if (!childNode.isNumeric()) {
- printf("Missing or invalid field %s in record %s", childName, groupName);
- return false;
- }
-
- *value = childNode.asUInt();
- return true;
-}
-
-
bool ConfigManager::initialize(const char* configFileName)
{
bool complete = true;
@@ -110,8 +92,6 @@
printf("Invalid configuration format -- we expect a display description\n");
return false;
}
- complete &= ReadChildNodeAsUint("display", displayNode, "width", &mPixelWidth);
- complete &= ReadChildNodeAsUint("display", displayNode, "height", &mPixelHeight);
complete &= readChildNodeAsFloat("display", displayNode, "frontRange", &mFrontRangeInCarSpace);
complete &= readChildNodeAsFloat("display", displayNode, "rearRange", &mRearRangeInCarSpace);
}
@@ -147,7 +127,6 @@
// Get data from the configuration file
Json::Value nameNode = node.get("cameraId", "MISSING");
const char *cameraId = nameNode.asCString();
- printf("Loading camera %s\n", cameraId);
Json::Value usageNode = node.get("function", "");
const char *function = usageNode.asCString();
diff --git a/evs/app/ConfigManager.h b/evs/app/ConfigManager.h
index 1b61146..0d24919 100644
--- a/evs/app/ConfigManager.h
+++ b/evs/app/ConfigManager.h
@@ -23,8 +23,8 @@
class ConfigManager {
public:
struct CameraInfo {
- std::string cameraId = 0; // The name of the camera from the point of view of the HAL
- std::string function = 0; // The expected use for this camera ("reverse", "left", "right")
+ std::string cameraId = ""; // The name of the camera from the point of view of the HAL
+ std::string function = ""; // The expected use for this camera ("reverse", "left", "right")
float position[3] = {0}; // x, y, z -> right, fwd, up in the units of car space
float yaw = 0; // radians positive to the left (right hand rule about global z axis)
float pitch = 0; // positive upward (ie: right hand rule about local x axis)
@@ -56,7 +56,7 @@
};
float getDisplayRightLocation(float aspectRatio) const {
// Given the display aspect ratio (width over height), how far can we see to the right?
- return (getDisplayTopLocation() - getDisplayBottomLocation()) * 0.5f / aspectRatio;
+ return (getDisplayTopLocation() - getDisplayBottomLocation()) * 0.5f * aspectRatio;
};
float getDisplayLeftLocation(float aspectRatio) const {
// Given the display aspect ratio (width over height), how far can we see to the left?
@@ -83,8 +83,6 @@
float mRearExtent;
// Display information
- unsigned mPixelWidth;
- unsigned mPixelHeight;
float mFrontRangeInCarSpace; // How far the display extends in front of the car
float mRearRangeInCarSpace; // How far the display extends behind the car
diff --git a/evs/app/EvsStateControl.cpp b/evs/app/EvsStateControl.cpp
index ec29e39..79533f7 100644
--- a/evs/app/EvsStateControl.cpp
+++ b/evs/app/EvsStateControl.cpp
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#define LOG_TAG "EVSAPP"
-
#include "EvsStateControl.h"
+#include "RenderDirectView.h"
+#include "RenderTopView.h"
#include <stdio.h>
#include <string.h>
@@ -39,6 +38,7 @@
mVehicle(pVnet),
mEvs(pEvs),
mDisplay(pDisplay),
+ mConfig(config),
mCurrentState(OFF) {
// Initialize the property value containers we'll be updating (they'll be zeroed by default)
@@ -50,10 +50,10 @@
mGearValue.prop = static_cast<int32_t>(VehicleProperty::GEAR_SELECTION);
mTurnSignalValue.prop = static_cast<int32_t>(VehicleProperty::TURN_SIGNAL_STATE);
+#if 0 // This way we only ever deal with cameras which exist in the system
// Build our set of cameras for the states we support
ALOGD("Requesting camera list");
- mEvs->getCameraList([this, &config]
- (hidl_vec<CameraDesc> cameraList) {
+ mEvs->getCameraList([this, &config](hidl_vec<CameraDesc> cameraList) {
ALOGI("Camera list callback received %zu cameras",
cameraList.size());
for (auto&& cam: cameraList) {
@@ -63,17 +63,23 @@
// Check our configuration for information about this camera
// Note that a camera can have a compound function string
// such that a camera can be "right/reverse" and be used for both.
+ // If more than one camera is listed for a given function, we'll
+ // list all of them and let the UX/rendering logic use one, some
+ // or all of them as appropriate.
for (auto&& info: config.getCameras()) {
if (cam.cameraId == info.cameraId) {
// We found a match!
if (info.function.find("reverse") != std::string::npos) {
- mCameraInfo[State::REVERSE] = info;
+ mCameraList[State::REVERSE].push_back(info);
}
if (info.function.find("right") != std::string::npos) {
- mCameraInfo[State::RIGHT] = info;
+ mCameraList[State::RIGHT].push_back(info);
}
if (info.function.find("left") != std::string::npos) {
- mCameraInfo[State::LEFT] = info;
+ mCameraList[State::LEFT].push_back(info);
+ }
+ if (info.function.find("park") != std::string::npos) {
+ mCameraList[State::PARKING].push_back(info);
}
cameraConfigFound = true;
break;
@@ -86,12 +92,118 @@
}
}
);
+#else // This way we use placeholders for cameras in the configuration but not reported by EVS
+ // Build our set of cameras for the states we support
+ ALOGD("Requesting camera list");
+ for (auto&& info: config.getCameras()) {
+ if (info.function.find("reverse") != std::string::npos) {
+ mCameraList[State::REVERSE].push_back(info);
+ }
+ if (info.function.find("right") != std::string::npos) {
+ mCameraList[State::RIGHT].push_back(info);
+ }
+ if (info.function.find("left") != std::string::npos) {
+ mCameraList[State::LEFT].push_back(info);
+ }
+ if (info.function.find("park") != std::string::npos) {
+ mCameraList[State::PARKING].push_back(info);
+ }
+ }
+#endif
+
ALOGD("State controller ready");
}
-bool EvsStateControl::configureForVehicleState() {
- ALOGD("configureForVehicleState");
+bool EvsStateControl::startUpdateLoop() {
+ // Create the thread and report success if it gets started
+ mRenderThread = std::thread([this](){ updateLoop(); });
+ return mRenderThread.joinable();
+}
+
+
+void EvsStateControl::postCommand(const Command& cmd) {
+ // Push the command onto the queue watched by updateLoop
+ mLock.lock();
+ mCommandQueue.push(cmd);
+ mLock.unlock();
+
+ // Send a signal to wake updateLoop in case it is asleep
+ mWakeSignal.notify_all();
+}
+
+
+void EvsStateControl::updateLoop() {
+ ALOGD("Starting EvsStateControl update loop");
+
+ bool run = true;
+ while (run) {
+ // Process incoming commands
+ {
+ std::lock_guard <std::mutex> lock(mLock);
+ while (!mCommandQueue.empty()) {
+ const Command& cmd = mCommandQueue.front();
+ switch (cmd.operation) {
+ case Op::EXIT:
+ run = false;
+ break;
+ case Op::CHECK_VEHICLE_STATE:
+ // Just running selectStateForCurrentConditions below will take care of this
+ break;
+ case Op::TOUCH_EVENT:
+ // TODO: Implement this given the x/y location of the touch event
+ // Ignore for now
+ break;
+ }
+ mCommandQueue.pop();
+ }
+ }
+
+ // Review vehicle state and choose an appropriate renderer
+ if (!selectStateForCurrentConditions()) {
+ ALOGE("selectStateForCurrentConditions failed so we're going to die");
+ break;
+ }
+
+ // If we have an active renderer, give it a chance to draw
+ if (mCurrentRenderer) {
+ // Get the output buffer we'll use to display the imagery
+ BufferDesc tgtBuffer = {};
+ mDisplay->getTargetBuffer([&tgtBuffer](const BufferDesc& buff) {
+ tgtBuffer = buff;
+ }
+ );
+
+ if (tgtBuffer.memHandle == nullptr) {
+ ALOGE("Didn't get requested output buffer -- skipping this frame.");
+ } else {
+ // Generate our output image
+ if (!mCurrentRenderer->drawFrame(tgtBuffer)) {
+ // If drawing failed, we want to exit quickly so an app restart can happen
+ run = false;
+ }
+
+ // Send the finished image back for display
+ mDisplay->returnTargetBufferForDisplay(tgtBuffer);
+ }
+ } else {
+ // No active renderer, so sleep until somebody wakes us with another command
+ std::unique_lock<std::mutex> lock(mLock);
+ mWakeSignal.wait(lock);
+ }
+ }
+
+ ALOGW("EvsStateControl update loop ending");
+
+ // TODO: Fix it so we can exit cleanly from the main thread instead
+ printf("Shutting down app due to state control loop ending\n");
+ ALOGE("KILLING THE APP FROM THE EvsStateControl LOOP ON DRAW FAILURE!!!");
+ exit(1);
+}
+
+
+bool EvsStateControl::selectStateForCurrentConditions() {
+ ALOGV("selectStateForCurrentConditions");
static int32_t sDummyGear = int32_t(VehicleGear::GEAR_REVERSE);
static int32_t sDummySignal = int32_t(VehicleTurnSignal::NONE);
@@ -124,6 +236,7 @@
}
// Choose our desired EVS state based on the current car state
+ // TODO: Update this logic, and include user input when choosing if a view should be presented
State desiredState = OFF;
if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
desiredState = REVERSE;
@@ -131,94 +244,92 @@
desiredState = RIGHT;
} else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::LEFT)) {
desiredState = LEFT;
+ } else if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_PARK)) {
+ desiredState = PARKING;
}
- // Apply the desire state
- ALOGV("Selected state %d.", desiredState);
- configureEvsPipeline(desiredState);
+ ALOGD("Selected state %d.", desiredState);
- // Operation was successful
- return true;
+ // Apply the desire state
+ return configureEvsPipeline(desiredState);
}
StatusCode EvsStateControl::invokeGet(VehiclePropValue *pRequestedPropValue) {
- ALOGD("invokeGet");
+ ALOGV("invokeGet");
StatusCode status = StatusCode::TRY_AGAIN;
- bool called = false;
// Call the Vehicle HAL, which will block until the callback is complete
mVehicle->get(*pRequestedPropValue,
- [pRequestedPropValue, &status, &called]
+ [pRequestedPropValue, &status]
(StatusCode s, const VehiclePropValue& v) {
status = s;
*pRequestedPropValue = v;
- called = true;
}
);
- // This should be true as long as the get call is block as it should
- // TODO: Once we've got some milage on this code and the underlying HIDL services,
- // we should remove this belt-and-suspenders check for correct operation as unnecessary.
- if (!called) {
- ALOGE("VehicleNetwork query did not run as expected.");
- }
return status;
}
bool EvsStateControl::configureEvsPipeline(State desiredState) {
- ALOGD("configureEvsPipeline");
+ ALOGV("configureEvsPipeline");
if (mCurrentState == desiredState) {
// Nothing to do here...
return true;
}
- // See if we actually have to change cameras
- if (mCameraInfo[mCurrentState].cameraId != mCameraInfo[desiredState].cameraId) {
- ALOGI("Camera change required");
- ALOGD(" Current cameraId (%d) = %s", mCurrentState,
- mCameraInfo[mCurrentState].cameraId.c_str());
- ALOGD(" Desired cameraId (%d) = %s", desiredState,
- mCameraInfo[desiredState].cameraId.c_str());
+ ALOGD(" Current state %d has %zu cameras", mCurrentState,
+ mCameraList[mCurrentState].size());
+ ALOGD(" Desired state %d has %zu cameras", desiredState,
+ mCameraList[desiredState].size());
- // Yup, we need to change cameras, so close the previous one, if necessary.
- if (mCurrentCamera != nullptr) {
- mCurrentStreamHandler->blockingStopStream();
- mCurrentStreamHandler = nullptr;
- mCurrentCamera = nullptr;
+ // Since we're changing states, shut down the current renderer
+ if (mCurrentRenderer != nullptr) {
+ mCurrentRenderer->deactivate();
+ mCurrentRenderer = nullptr; // It's a smart pointer, so destructs on assignment to null
+ }
+
+ // Do we need a new direct view renderer?
+ if (mCameraList[desiredState].size() > 1 || desiredState == PARKING) {
+ // TODO: DO we want other kinds of compound view or else sequentially selected views?
+ mCurrentRenderer = std::make_unique<RenderTopView>(mEvs,
+ mCameraList[desiredState],
+ mConfig);
+ if (!mCurrentRenderer) {
+ ALOGE("Failed to construct top view renderer. Skipping state change.");
+ return false;
+ }
+ } else if (mCameraList[desiredState].size() == 1) {
+ // We have a camera assigned to this state for direct view
+ mCurrentRenderer = std::make_unique<RenderDirectView>(mEvs,
+ mCameraList[desiredState][0]);
+ if (!mCurrentRenderer) {
+ ALOGE("Failed to construct direct renderer. Skipping state change.");
+ return false;
+ }
+ }
+
+ // Now set the display state based on whether we have a video feed to show
+ if (mCurrentRenderer == nullptr) {
+ ALOGD("Turning off the display");
+ mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
+ } else {
+ // Start the camera stream
+ ALOGD("Starting camera stream");
+ if (!mCurrentRenderer->activate()) {
+ ALOGE("New renderer failed to activate");
+ return false;
}
- // Now do we need a new camera?
- if (!mCameraInfo[desiredState].cameraId.empty()) {
- // Need a new camera, so open it
- ALOGD("Open camera %s", mCameraInfo[desiredState].cameraId.c_str());
- mCurrentCamera = mEvs->openCamera(mCameraInfo[desiredState].cameraId);
-
- // If we didn't get the camera we asked for, we need to bail out and try again later
- if (mCurrentCamera == nullptr) {
- ALOGE("Failed to open EVS camera. Skipping state change.");
- return false;
- }
- }
-
- // Now set the display state based on whether we have a camera feed to show
- if (mCurrentCamera == nullptr) {
- ALOGD("Turning off the display");
- mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
- } else {
- // Create the stream handler object to receive and forward the video frames
- mCurrentStreamHandler = new StreamHandler(mCurrentCamera, mDisplay);
-
- // Start the camera stream
- ALOGD("Starting camera stream");
- mCurrentStreamHandler->startStream();
-
- // Activate the display
- ALOGD("Arming the display");
- mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
+ // Activate the display
+ ALOGD("Arming the display");
+ Return<EvsResult> result = mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
+ if (result != EvsResult::OK) {
+ ALOGE("setDisplayState returned an error (%d)", (EvsResult)result);
+ return false;
}
}
diff --git a/evs/app/EvsStateControl.h b/evs/app/EvsStateControl.h
index d8ce9e6..cfb6833 100644
--- a/evs/app/EvsStateControl.h
+++ b/evs/app/EvsStateControl.h
@@ -17,13 +17,16 @@
#ifndef CAR_EVS_APP_EVSSTATECONTROL_H
#define CAR_EVS_APP_EVSSTATECONTROL_H
+#include "StreamHandler.h"
+#include "ConfigManager.h"
+#include "RenderBase.h"
+
#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
#include <android/hardware/automotive/evs/1.0/IEvsCamera.h>
-#include "StreamHandler.h"
-#include "ConfigManager.h"
+#include <thread>
using namespace ::android::hardware::automotive::evs::V1_0;
@@ -35,6 +38,11 @@
using ::android::sp;
+/*
+ * This class runs the main update loop for the EVS application. It will sleep when it has
+ * nothing to do. It provides a thread safe way for other threads to wake it and pass commands
+ * to it.
+ */
class EvsStateControl {
public:
EvsStateControl(android::sp <IVehicle> pVnet,
@@ -43,31 +51,57 @@
const ConfigManager& config);
enum State {
- REVERSE = 0,
+ OFF = 0,
+ REVERSE,
LEFT,
RIGHT,
- OFF,
+ PARKING,
NUM_STATES // Must come last
};
- bool configureForVehicleState();
+ enum class Op {
+ EXIT,
+ CHECK_VEHICLE_STATE,
+ TOUCH_EVENT,
+ };
+
+ struct Command {
+ Op operation;
+ uint32_t arg1;
+ uint32_t arg2;
+ };
+
+ // This spawns a new thread that is expected to run continuously
+ bool startUpdateLoop();
+
+ // Safe to be called from other threads
+ void postCommand(const Command& cmd);
private:
+ void updateLoop();
StatusCode invokeGet(VehiclePropValue *pRequestedPropValue);
- bool configureEvsPipeline(State desiredState);
+ bool selectStateForCurrentConditions();
+ bool configureEvsPipeline(State desiredState); // Only call from one thread!
sp<IVehicle> mVehicle;
sp<IEvsEnumerator> mEvs;
sp<IEvsDisplay> mDisplay;
+ const ConfigManager& mConfig;
VehiclePropValue mGearValue;
VehiclePropValue mTurnSignalValue;
- ConfigManager::CameraInfo mCameraInfo[State::NUM_STATES];
- State mCurrentState;
- sp<IEvsCamera> mCurrentCamera;
+ State mCurrentState = OFF;
- sp<StreamHandler> mCurrentStreamHandler;
+ std::vector<ConfigManager::CameraInfo> mCameraList[NUM_STATES];
+ std::unique_ptr<RenderBase> mCurrentRenderer;
+
+ std::thread mRenderThread; // The thread that runs the main rendering loop
+
+ // Other threads may want to spur us into action, so we provide a thread safe way to do that
+ std::mutex mLock;
+ std::condition_variable mWakeSignal;
+ std::queue<Command> mCommandQueue;
};
diff --git a/evs/app/EvsVehicleListener.h b/evs/app/EvsVehicleListener.h
index b15ac0d..2935ab0 100644
--- a/evs/app/EvsVehicleListener.h
+++ b/evs/app/EvsVehicleListener.h
@@ -19,7 +19,11 @@
#include "EvsStateControl.h"
-
+/*
+ * This class listens for asynchronous updates from the Vehicle HAL. While the EVS
+ * applications is active, it can poll the vehicle state directly. However, when it goes to
+ * sleep, we need these notifications to bring it active again.
+ */
class EvsVehicleListener : public IVehicleCallback {
public:
// Methods from ::android::hardware::automotive::vehicle::V2_0::IVehicleCallback follow.
@@ -59,7 +63,12 @@
waitForEvents(5000);
// If we were delivered an event (or it's been a while) update as necessary
- pStateController->configureForVehicleState();
+ EvsStateControl::Command cmd = {
+ .operation = EvsStateControl::Op::CHECK_VEHICLE_STATE,
+ .arg1 = 0,
+ .arg2 = 0,
+ };
+ pStateController->postCommand(cmd);
}
}
diff --git a/evs/app/FormatConvert.cpp b/evs/app/FormatConvert.cpp
new file mode 100644
index 0000000..bd83ba3
--- /dev/null
+++ b/evs/app/FormatConvert.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "FormatConvert.h"
+
+
+// Round up to the nearest multiple of the given alignment value
+template<unsigned alignment>
+int align(int value) {
+ static_assert((alignment && !(alignment & (alignment - 1))),
+ "alignment must be a power of 2");
+
+ unsigned mask = alignment - 1;
+ return (value + mask) & ~mask;
+}
+
+
+// Limit the given value to the provided range. :)
+static inline float clamp(float v, float min, float max) {
+ if (v < min) return min;
+ if (v > max) return max;
+ return v;
+}
+
+
+static uint32_t yuvToRgbx(const unsigned char Y, const unsigned char Uin, const unsigned char Vin) {
+ // Don't use this if you want to see the best performance. :)
+ // Better to do this in a pixel shader if we really have to, but on actual
+ // embedded hardware we expect to be able to texture directly from the YUV data
+ float U = Uin - 128.0f;
+ float V = Vin - 128.0f;
+
+ float Rf = Y + 1.140f*V;
+ float Gf = Y - 0.395f*U - 0.581f*V;
+ float Bf = Y + 2.032f*U;
+ unsigned char R = (unsigned char)clamp(Rf, 0.0f, 255.0f);
+ unsigned char G = (unsigned char)clamp(Gf, 0.0f, 255.0f);
+ unsigned char B = (unsigned char)clamp(Bf, 0.0f, 255.0f);
+
+ return (R ) |
+ (G << 8) |
+ (B << 16) |
+ 0xFF000000; // Fill the alpha channel with ones
+}
+
+
+void copyNV21toRGB32(unsigned width, unsigned height,
+ uint8_t* src,
+ uint32_t* dst, unsigned dstStridePixels)
+{
+ // The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved
+ // U/V array. It assumes an even width and height for the overall image, and a horizontal
+ // stride that is an even multiple of 16 bytes for both the Y and UV arrays.
+ unsigned strideLum = align<16>(width);
+ unsigned sizeY = strideLum * height;
+ unsigned strideColor = strideLum; // 1/2 the samples, but two interleaved channels
+ unsigned offsetUV = sizeY;
+
+ uint8_t* srcY = src;
+ uint8_t* srcUV = src+offsetUV;
+
+ for (unsigned r = 0; r < height; r++) {
+ // Note that we're walking the same UV row twice for even/odd luminance rows
+ uint8_t* rowY = srcY + r*strideLum;
+ uint8_t* rowUV = srcUV + (r/2 * strideColor);
+
+ uint32_t* rowDest = dst + r*dstStridePixels;
+
+ for (unsigned c = 0; c < width; c++) {
+ unsigned uCol = (c & ~1); // uCol is always even and repeats 1:2 with Y values
+ unsigned vCol = uCol | 1; // vCol is always odd
+ rowDest[c] = yuvToRgbx(rowY[c], rowUV[uCol], rowUV[vCol]);
+ }
+ }
+}
+
+
+void copyYV12toRGB32(unsigned width, unsigned height,
+ uint8_t* src,
+ uint32_t* dst, unsigned dstStridePixels)
+{
+ // The YV12 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 U array, followed
+ // by another 1/2 x 1/2 V array. It assumes an even width and height for the overall image,
+ // and a horizontal stride that is an even multiple of 16 bytes for each of the Y, U,
+ // and V arrays.
+ unsigned strideLum = align<16>(width);
+ unsigned sizeY = strideLum * height;
+ unsigned strideColor = align<16>(strideLum/2);
+ unsigned sizeColor = strideColor * height/2;
+ unsigned offsetU = sizeY;
+ unsigned offsetV = sizeY + sizeColor;
+
+ uint8_t* srcY = src;
+ uint8_t* srcU = src+offsetU;
+ uint8_t* srcV = src+offsetV;
+
+ for (unsigned r = 0; r < height; r++) {
+ // Note that we're walking the same U and V rows twice for even/odd luminance rows
+ uint8_t* rowY = srcY + r*strideLum;
+ uint8_t* rowU = srcU + (r/2 * strideColor);
+ uint8_t* rowV = srcV + (r/2 * strideColor);
+
+ uint32_t* rowDest = dst + r*dstStridePixels;
+
+ for (unsigned c = 0; c < width; c++) {
+ rowDest[c] = yuvToRgbx(rowY[c], rowU[c], rowV[c]);
+ }
+ }
+}
+
+
+void copyYUYVtoRGB32(unsigned width, unsigned height,
+ uint8_t* src, unsigned srcStridePixels,
+ uint32_t* dst, unsigned dstStridePixels)
+{
+ uint32_t* srcWords = (uint32_t*)src;
+
+ const int srcRowPadding32 = srcStridePixels/2 - width/2; // 2 bytes per pixel, 4 bytes per word
+ const int dstRowPadding32 = dstStridePixels - width; // 4 bytes per pixel, 4 bytes per word
+
+ for (unsigned r = 0; r < height; r++) {
+ for (unsigned c = 0; c < width/2; c++) {
+ // Note: we're walking two pixels at a time here (even/odd)
+ uint32_t srcPixel = *srcWords++;
+
+ uint8_t Y1 = (srcPixel) & 0xFF;
+ uint8_t U = (srcPixel >> 8) & 0xFF;
+ uint8_t Y2 = (srcPixel >> 16) & 0xFF;
+ uint8_t V = (srcPixel >> 24) & 0xFF;
+
+ // On the RGB output, we're writing one pixel at a time
+ *(dst+0) = yuvToRgbx(Y1, U, V);
+ *(dst+1) = yuvToRgbx(Y2, U, V);
+ dst += 2;
+ }
+
+ // Skip over any extra data or end of row alignment padding
+ srcWords += srcRowPadding32;
+ dst += dstRowPadding32;
+ }
+}
+
+
+void copyMatchedInterleavedFormats(unsigned width, unsigned height,
+ void* src, unsigned srcStridePixels,
+ void* dst, unsigned dstStridePixels,
+ unsigned pixelSize) {
+ for (unsigned row = 0; row < height; row++) {
+ // Copy the entire row of pixel data
+ memcpy(dst, src, width * pixelSize);
+
+ // Advance to the next row (keeping in mind that stride here is in units of pixels)
+ src = (uint8_t*)src + srcStridePixels * pixelSize;
+ dst = (uint8_t*)dst + dstStridePixels * pixelSize;
+ }
+}
diff --git a/evs/app/FormatConvert.h b/evs/app/FormatConvert.h
new file mode 100644
index 0000000..3ff1eec
--- /dev/null
+++ b/evs/app/FormatConvert.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef EVS_VTS_FORMATCONVERT_H
+#define EVS_VTS_FORMATCONVERT_H
+
+#include <queue>
+#include <stdint.h>
+
+
+// Given an image buffer in NV21 format (HAL_PIXEL_FORMAT_YCRCB_420_SP), output 32bit RGBx values.
+// The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved
+// U/V array. It assumes an even width and height for the overall image, and a horizontal
+// stride that is an even multiple of 16 bytes for both the Y and UV arrays.
+void copyNV21toRGB32(unsigned width, unsigned height,
+ uint8_t* src,
+ uint32_t* dst, unsigned dstStridePixels);
+
+
+// Given an image buffer in YV12 format (HAL_PIXEL_FORMAT_YV12), output 32bit RGBx values.
+// The YV12 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 U array, followed
+// by another 1/2 x 1/2 V array. It assumes an even width and height for the overall image,
+// and a horizontal stride that is an even multiple of 16 bytes for each of the Y, U,
+// and V arrays.
+void copyYV12toRGB32(unsigned width, unsigned height,
+ uint8_t* src,
+ uint32_t* dst, unsigned dstStridePixels);
+
+
+// Given an image buffer in YUYV format (HAL_PIXEL_FORMAT_YCBCR_422_I), output 32bit RGBx values.
+// The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved
+// U/V array. It assumes an even width and height for the overall image, and a horizontal
+// stride that is an even multiple of 16 bytes for both the Y and UV arrays.
+void copyYUYVtoRGB32(unsigned width, unsigned height,
+ uint8_t* src, unsigned srcStrideBytes,
+ uint32_t* dst, unsigned dstStrideBytes);
+
+
+// Given an simple rectangular image buffer with an integer number of bytes per pixel,
+// copy the pixel values into a new rectangular buffer (potentially with a different stride).
+// This is typically used to copy RGBx data into an RGBx output buffer.
+void copyMatchedInterleavedFormats(unsigned width, unsigned height,
+ void* src, unsigned srcStridePixels,
+ void* dst, unsigned dstStridePixels,
+ unsigned pixelSize);
+
+#endif // EVS_VTS_FORMATCONVERT_H
diff --git a/evs/app/LabeledChecker.png b/evs/app/LabeledChecker.png
new file mode 100644
index 0000000..02da85d
--- /dev/null
+++ b/evs/app/LabeledChecker.png
Binary files differ
diff --git a/evs/app/RenderBase.cpp b/evs/app/RenderBase.cpp
new file mode 100644
index 0000000..cb6aa93
--- /dev/null
+++ b/evs/app/RenderBase.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "RenderBase.h"
+#include "glError.h"
+
+#include <log/log.h>
+#include <ui/GraphicBuffer.h>
+
+// Eventually we shouldn't need this dependency, but for now the
+// graphics allocator interface isn't fully supported on all platforms
+// and this is our work around.
+using ::android::GraphicBuffer;
+
+
+// OpenGL state shared among all renderers
+EGLDisplay RenderBase::sDisplay = EGL_NO_DISPLAY;
+EGLContext RenderBase::sContext = EGL_NO_CONTEXT;
+EGLSurface RenderBase::sDummySurface = EGL_NO_SURFACE;
+GLuint RenderBase::sFrameBuffer = -1;
+GLuint RenderBase::sColorBuffer = -1;
+GLuint RenderBase::sDepthBuffer = -1;
+EGLImageKHR RenderBase::sKHRimage = EGL_NO_IMAGE_KHR;
+unsigned RenderBase::sWidth = 0;
+unsigned RenderBase::sHeight = 0;
+float RenderBase::sAspectRatio = 0.0f;
+
+
+bool RenderBase::prepareGL() {
+ // Just trivially return success if we're already prepared
+ if (sDisplay != EGL_NO_DISPLAY) {
+ return true;
+ }
+
+ // Hardcoded to RGBx output display
+ const EGLint config_attribs[] = {
+ // Tag Value
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_NONE
+ };
+
+ // Select OpenGL ES v 3
+ const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+
+
+ // Set up our OpenGL ES context associated with the default display (though we won't be visible)
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (display == EGL_NO_DISPLAY) {
+ ALOGE("Failed to get egl display");
+ return false;
+ }
+
+ EGLint major = 0;
+ EGLint minor = 0;
+ if (!eglInitialize(display, &major, &minor)) {
+ ALOGE("Failed to initialize EGL: %s", getEGLError());
+ return false;
+ } else {
+ ALOGI("Intiialized EGL at %d.%d", major, minor);
+ }
+
+
+ // Select the configuration that "best" matches our desired characteristics
+ EGLConfig egl_config;
+ EGLint num_configs;
+ if (!eglChooseConfig(display, config_attribs, &egl_config, 1, &num_configs)) {
+ ALOGE("eglChooseConfig() failed with error: %s", getEGLError());
+ return false;
+ }
+
+
+ // Create a dummy pbuffer so we have a surface to bind -- we never intend to draw to this
+ // because attachRenderTarget will be called first.
+ EGLint surface_attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+ sDummySurface = eglCreatePbufferSurface(display, egl_config, surface_attribs);
+ if (sDummySurface == EGL_NO_SURFACE) {
+ ALOGE("Failed to create OpenGL ES Dummy surface: %s", getEGLError());
+ return false;
+ } else {
+ ALOGI("Dummy surface looks good! :)");
+ }
+
+
+ //
+ // Create the EGL context
+ //
+ EGLContext context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT, context_attribs);
+ if (context == EGL_NO_CONTEXT) {
+ ALOGE("Failed to create OpenGL ES Context: %s", getEGLError());
+ return false;
+ }
+
+
+ // Activate our render target for drawing
+ if (!eglMakeCurrent(display, sDummySurface, sDummySurface, context)) {
+ ALOGE("Failed to make the OpenGL ES Context current: %s", getEGLError());
+ return false;
+ } else {
+ ALOGI("We made our context current! :)");
+ }
+
+
+ // Report the extensions available on this implementation
+ const char* gl_extensions = (const char*) glGetString(GL_EXTENSIONS);
+ ALOGI("GL EXTENSIONS:\n %s", gl_extensions);
+
+
+ // Reserve handles for the color and depth targets we'll be setting up
+ glGenRenderbuffers(1, &sColorBuffer);
+ glGenRenderbuffers(1, &sDepthBuffer);
+
+ // Set up the frame buffer object we can modify and use for off screen rendering
+ glGenFramebuffers(1, &sFrameBuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, sFrameBuffer);
+
+
+ // Now that we're assured success, store object handles we constructed
+ sDisplay = display;
+ sContext = context;
+
+ return true;
+}
+
+
+bool RenderBase::attachRenderTarget(const BufferDesc& tgtBuffer) {
+ // Hardcoded to RGBx for now
+ if (tgtBuffer.format != HAL_PIXEL_FORMAT_RGBA_8888) {
+ ALOGE("Unsupported target buffer format");
+ return false;
+ }
+
+ // create a GraphicBuffer from the existing handle
+ sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(tgtBuffer.memHandle,
+ GraphicBuffer::CLONE_HANDLE,
+ tgtBuffer.width, tgtBuffer.height,
+ tgtBuffer.format, 1, // layer count
+ GRALLOC_USAGE_HW_RENDER,
+ tgtBuffer.stride);
+ if (pGfxBuffer.get() == nullptr) {
+ ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
+ return false;
+ }
+
+ // Get a GL compatible reference to the graphics buffer we've been given
+ EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+ EGLClientBuffer clientBuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
+ sKHRimage = eglCreateImageKHR(sDisplay, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, clientBuf,
+ eglImageAttributes);
+ if (sKHRimage == EGL_NO_IMAGE_KHR) {
+ ALOGE("error creating EGLImage for target buffer: %s", getEGLError());
+ return false;
+ }
+
+ // Construct a render buffer around the external buffer
+ glBindRenderbuffer(GL_RENDERBUFFER, sColorBuffer);
+ glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, static_cast<GLeglImageOES>(sKHRimage));
+ if (eglGetError() != EGL_SUCCESS) {
+ ALOGI("glEGLImageTargetRenderbufferStorageOES => %s", getEGLError());
+ return false;
+ }
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sColorBuffer);
+ if (eglGetError() != EGL_SUCCESS) {
+ ALOGE("glFramebufferRenderbuffer => %s", getEGLError());
+ return false;
+ }
+
+ GLenum checkResult = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (checkResult != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Offscreen framebuffer not configured successfully (%d: %s)",
+ checkResult, getGLFramebufferError());
+ return false;
+ }
+
+ // Store the size of our target buffer
+ sWidth = tgtBuffer.width;
+ sHeight = tgtBuffer.height;
+ sAspectRatio = (float)sWidth / sHeight;
+
+ // Set the viewport
+ glViewport(0, 0, sWidth, sHeight);
+
+#if 1 // We don't actually need the clear if we're going to cover the whole screen anyway
+ // Clear the color buffer
+ glClearColor(0.8f, 0.1f, 0.2f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+#endif
+
+
+ return true;
+}
+
+
+void RenderBase::detachRenderTarget() {
+ // Drop our external render target
+ if (sKHRimage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(sDisplay, sKHRimage);
+ sKHRimage = EGL_NO_IMAGE_KHR;
+ }
+}
\ No newline at end of file
diff --git a/evs/app/RenderBase.h b/evs/app/RenderBase.h
new file mode 100644
index 0000000..25474d5
--- /dev/null
+++ b/evs/app/RenderBase.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef CAR_EVS_APP_RENDERBASE_H
+#define CAR_EVS_APP_RENDERBASE_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+
+#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
+
+using namespace ::android::hardware::automotive::evs::V1_0;
+using ::android::sp;
+
+
+/*
+ * Abstract base class for the workhorse classes that handle the user interaction and display for
+ * each mode of the EVS application.
+ */
+class RenderBase {
+public:
+ virtual ~RenderBase() {};
+
+ virtual bool activate() = 0;
+ virtual void deactivate() = 0;
+
+ virtual bool drawFrame(const BufferDesc& tgtBuffer) = 0;
+
+protected:
+ static bool prepareGL();
+
+ static bool attachRenderTarget(const BufferDesc& tgtBuffer);
+ static void detachRenderTarget();
+
+ // OpenGL state shared among all renderers
+ static EGLDisplay sDisplay;
+ static EGLContext sContext;
+ static EGLSurface sDummySurface;
+ static GLuint sFrameBuffer;
+ static GLuint sColorBuffer;
+ static GLuint sDepthBuffer;
+
+ static EGLImageKHR sKHRimage;
+
+ static unsigned sWidth;
+ static unsigned sHeight;
+ static float sAspectRatio;
+};
+
+
+#endif //CAR_EVS_APP_RENDERBASE_H
diff --git a/evs/app/RenderDirectView.cpp b/evs/app/RenderDirectView.cpp
new file mode 100644
index 0000000..24eb485
--- /dev/null
+++ b/evs/app/RenderDirectView.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "RenderDirectView.h"
+#include "VideoTex.h"
+#include "glError.h"
+#include "shader.h"
+#include "shader_simpleTex.h"
+
+#include <log/log.h>
+#include <math/mat4.h>
+
+
+RenderDirectView::RenderDirectView(sp<IEvsEnumerator> enumerator,
+ const ConfigManager::CameraInfo& cam) {
+ mEnumerator = enumerator;
+ mCameraInfo = cam;
+}
+
+
+bool RenderDirectView::activate() {
+ // Ensure GL is ready to go...
+ if (!prepareGL()) {
+ ALOGE("Error initializing GL");
+ return false;
+ }
+
+ // Load our shader program if we don't have it already
+ if (!mShaderProgram) {
+ mShaderProgram = buildShaderProgram(vtxShader_simpleTexture,
+ pixShader_simpleTexture,
+ "simpleTexture");
+ if (!mShaderProgram) {
+ ALOGE("Error buliding shader program");
+ return false;
+ }
+ }
+
+ // Construct our video texture
+ mTexture.reset(createVideoTexture(mEnumerator, mCameraInfo.cameraId.c_str(), sDisplay));
+ if (!mTexture) {
+ ALOGE("Failed to set up video texture for %s (%s)",
+ mCameraInfo.cameraId.c_str(), mCameraInfo.function.c_str());
+// TODO: For production use, we may actually want to fail in this case, but not yet...
+// return false;
+ }
+
+ return true;
+}
+
+
+void RenderDirectView::deactivate() {
+ // Release our video texture
+ // We can't hold onto it because some other Render object might need the same camera
+ // TODO: If start/stop costs become a problem, we could share video textures
+ mTexture = nullptr;
+}
+
+
+bool RenderDirectView::drawFrame(const BufferDesc& tgtBuffer) {
+ // Tell GL to render to the given buffer
+ if (!attachRenderTarget(tgtBuffer)) {
+ ALOGE("Failed to attached render target");
+ return false;
+ }
+
+ // Select our screen space simple texture shader
+ glUseProgram(mShaderProgram);
+
+ // Set up the model to clip space transform (identity matrix if we're modeling in screen space)
+ GLint loc = glGetUniformLocation(mShaderProgram, "cameraMat");
+ if (loc < 0) {
+ ALOGE("Couldn't set shader parameter 'cameraMat'");
+ return false;
+ } else {
+ const android::mat4 identityMatrix;
+ glUniformMatrix4fv(loc, 1, false, identityMatrix.asArray());
+ }
+
+
+ // Bind the texture and assign it to the shader's sampler
+ mTexture->refresh();
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTexture->glId());
+
+
+ GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
+ if (sampler < 0) {
+ ALOGE("Couldn't set shader parameter 'tex'");
+ return false;
+ } else {
+ // Tell the sampler we looked up from the shader to use texture slot 0 as its source
+ glUniform1i(sampler, 0);
+ }
+
+ // We want our image to show up opaque regardless of alpha values
+ glDisable(GL_BLEND);
+
+
+ // Draw a rectangle on the screen
+ GLfloat vertsCarPos[] = { -1.0, 1.0, 0.0f, // left top in window space
+ 1.0, 1.0, 0.0f, // right top
+ -1.0, -1.0, 0.0f, // left bottom
+ 1.0, -1.0, 0.0f // right bottom
+ };
+ // TODO: We're flipping horizontally here, but should do it only for specified cameras!
+ GLfloat vertsCarTex[] = { 1.0f, 1.0f, // left top
+ 0.0f, 1.0f, // right top
+ 1.0f, 0.0f, // left bottom
+ 0.0f, 0.0f // right bottom
+ };
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+
+
+ // Wait for the rendering to finish
+ glFinish();
+
+ return true;
+}
diff --git a/evs/app/RenderDirectView.h b/evs/app/RenderDirectView.h
new file mode 100644
index 0000000..1543fce
--- /dev/null
+++ b/evs/app/RenderDirectView.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef CAR_EVS_APP_RENDERDIRECTVIEW_H
+#define CAR_EVS_APP_RENDERDIRECTVIEW_H
+
+
+#include "RenderBase.h"
+
+#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
+#include "ConfigManager.h"
+#include "VideoTex.h"
+
+
+using namespace ::android::hardware::automotive::evs::V1_0;
+
+
+/*
+ * Renders the view from a single specified camera directly to the full display.
+ */
+class RenderDirectView: public RenderBase {
+public:
+ RenderDirectView(sp<IEvsEnumerator> enumerator, const ConfigManager::CameraInfo& cam);
+
+ virtual bool activate() override;
+ virtual void deactivate() override;
+
+ virtual bool drawFrame(const BufferDesc& tgtBuffer);
+
+protected:
+ sp<IEvsEnumerator> mEnumerator;
+ ConfigManager::CameraInfo mCameraInfo;
+
+ std::unique_ptr<VideoTex> mTexture;
+
+ GLuint mShaderProgram = 0;
+};
+
+
+#endif //CAR_EVS_APP_RENDERDIRECTVIEW_H
diff --git a/evs/app/RenderPixelCopy.cpp b/evs/app/RenderPixelCopy.cpp
new file mode 100644
index 0000000..0a586a4
--- /dev/null
+++ b/evs/app/RenderPixelCopy.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "RenderPixelCopy.h"
+#include "FormatConvert.h"
+
+#include <log/log.h>
+
+
+RenderPixelCopy::RenderPixelCopy(sp<IEvsEnumerator> enumerator,
+ const ConfigManager::CameraInfo& cam) {
+ mEnumerator = enumerator;
+ mCameraInfo = cam;
+}
+
+
+bool RenderPixelCopy::activate() {
+ // Set up the camera to feed this texture
+ sp<IEvsCamera> pCamera = mEnumerator->openCamera(mCameraInfo.cameraId.c_str());
+ if (pCamera.get() == nullptr) {
+ ALOGE("Failed to allocate new EVS Camera interface");
+ return false;
+ }
+
+ // Initialize the stream that will help us update this texture's contents
+ sp<StreamHandler> pStreamHandler = new StreamHandler(pCamera);
+ if (pStreamHandler.get() == nullptr) {
+ ALOGE("failed to allocate FrameHandler");
+ return false;
+ }
+
+ // Start the video stream
+ if (!pStreamHandler->startStream()) {
+ ALOGE("start stream failed");
+ return false;
+ }
+
+ mStreamHandler = pStreamHandler;
+
+ return true;
+}
+
+
+void RenderPixelCopy::deactivate() {
+ mStreamHandler = nullptr;
+}
+
+
+bool RenderPixelCopy::drawFrame(const BufferDesc& tgtBuffer) {
+ bool success = true;
+
+ sp<android::GraphicBuffer> tgt = new android::GraphicBuffer(
+ tgtBuffer.memHandle, android::GraphicBuffer::CLONE_HANDLE,
+ tgtBuffer.width, tgtBuffer.height, tgtBuffer.format, 1, tgtBuffer.usage,
+ tgtBuffer.stride);
+
+ // Lock our target buffer for writing (should be RGBA8888 format)
+ uint32_t* tgtPixels = nullptr;
+ tgt->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)&tgtPixels);
+
+ if (tgtPixels) {
+ if (tgtBuffer.format != HAL_PIXEL_FORMAT_RGBA_8888) {
+ // We always expect 32 bit RGB for the display output for now. Is there a need for 565?
+ ALOGE("Diplay buffer is always expected to be 32bit RGBA");
+ success = false;
+ } else {
+ // Make sure we have the latest frame data
+ if (mStreamHandler->newFrameAvailable()) {
+ const BufferDesc& srcBuffer = mStreamHandler->getNewFrame();
+
+ // Lock our source buffer for reading (current expectation are for this to be NV21 format)
+ sp<android::GraphicBuffer> src = new android::GraphicBuffer(
+ srcBuffer.memHandle, android::GraphicBuffer::CLONE_HANDLE,
+ srcBuffer.width, srcBuffer.height, srcBuffer.format, 1, srcBuffer.usage,
+ srcBuffer.stride);
+ unsigned char* srcPixels = nullptr;
+ src->lock(GRALLOC_USAGE_SW_READ_OFTEN, (void**)&srcPixels);
+ if (!srcPixels) {
+ ALOGE("Failed to get pointer into src image data");
+ }
+
+ // Make sure we don't run off the end of either buffer
+ const unsigned width = std::min(tgtBuffer.width,
+ srcBuffer.width);
+ const unsigned height = std::min(tgtBuffer.height,
+ srcBuffer.height);
+
+ if (srcBuffer.format == HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21
+ copyNV21toRGB32(width, height,
+ srcPixels,
+ tgtPixels, tgtBuffer.stride);
+ } else if (srcBuffer.format == HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12
+ copyYV12toRGB32(width, height,
+ srcPixels,
+ tgtPixels, tgtBuffer.stride);
+ } else if (srcBuffer.format == HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV
+ copyYUYVtoRGB32(width, height,
+ srcPixels, srcBuffer.stride,
+ tgtPixels, tgtBuffer.stride);
+ } else if (srcBuffer.format == tgtBuffer.format) { // 32bit RGBA
+ copyMatchedInterleavedFormats(width, height,
+ srcPixels, srcBuffer.stride,
+ tgtPixels, tgtBuffer.stride,
+ tgtBuffer.pixelSize);
+ }
+
+ mStreamHandler->doneWithFrame(srcBuffer);
+ }
+ }
+ } else {
+ ALOGE("Failed to lock buffer contents for contents transfer");
+ success = false;
+ }
+
+ if (tgtPixels) {
+ tgt->unlock();
+ }
+
+ return success;
+}
diff --git a/evs/app/RenderPixelCopy.h b/evs/app/RenderPixelCopy.h
new file mode 100644
index 0000000..ee6eede
--- /dev/null
+++ b/evs/app/RenderPixelCopy.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef CAR_EVS_APP_RENDERPIXELCOPY_H
+#define CAR_EVS_APP_RENDERPIXELCOPY_H
+
+
+#include "RenderBase.h"
+
+#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
+#include "ConfigManager.h"
+#include "VideoTex.h"
+
+
+using namespace ::android::hardware::automotive::evs::V1_0;
+
+
+/*
+ * Renders the view from a single specified camera directly to the full display.
+ */
+class RenderPixelCopy: public RenderBase {
+public:
+ RenderPixelCopy(sp<IEvsEnumerator> enumerator, const ConfigManager::CameraInfo& cam);
+
+ virtual bool activate() override;
+ virtual void deactivate() override;
+
+ virtual bool drawFrame(const BufferDesc& tgtBuffer);
+
+protected:
+ sp<IEvsEnumerator> mEnumerator;
+ ConfigManager::CameraInfo mCameraInfo;
+
+ sp<StreamHandler> mStreamHandler;
+};
+
+
+#endif //CAR_EVS_APP_RENDERPIXELCOPY_H
diff --git a/evs/app/RenderTopView.cpp b/evs/app/RenderTopView.cpp
new file mode 100644
index 0000000..1579a0a
--- /dev/null
+++ b/evs/app/RenderTopView.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "RenderTopView.h"
+#include "VideoTex.h"
+#include "glError.h"
+#include "shader.h"
+#include "shader_simpleTex.h"
+#include "shader_projectedTex.h"
+
+#include <log/log.h>
+#include <math/mat4.h>
+#include <math/vec3.h>
+
+
+// Simple aliases to make geometric math using vectors more readable
+static const unsigned X = 0;
+static const unsigned Y = 1;
+static const unsigned Z = 2;
+//static const unsigned W = 3;
+
+
+// Since we assume no roll in these views, we can simplify the required math
+static android::vec3 unitVectorFromPitchAndYaw(float pitch, float yaw) {
+ float sinPitch, cosPitch;
+ sincosf(pitch, &sinPitch, &cosPitch);
+ float sinYaw, cosYaw;
+ sincosf(yaw, &sinYaw, &cosYaw);
+ return android::vec3(cosPitch * -sinYaw,
+ cosPitch * cosYaw,
+ sinPitch);
+}
+
+
+// Helper function to set up a perspective matrix with independent horizontal and vertical
+// angles of view.
+static android::mat4 perspective(float hfov, float vfov, float near, float far) {
+ const float tanHalfFovX = tanf(hfov * 0.5f);
+ const float tanHalfFovY = tanf(vfov * 0.5f);
+
+ android::mat4 p(0.0f);
+ p[0][0] = 1.0f / tanHalfFovX;
+ p[1][1] = 1.0f / tanHalfFovY;
+ p[2][2] = - (far + near) / (far - near);
+ p[2][3] = -1.0f;
+ p[3][2] = - (2.0f * far * near) / (far - near);
+ return p;
+}
+
+
+// Helper function to set up a view matrix for a camera given it's yaw & pitch & location
+// Yes, with a bit of work, we could use lookAt, but it does a lot of extra work
+// internally that we can short cut.
+static android::mat4 cameraLookMatrix(const ConfigManager::CameraInfo& cam) {
+ float sinYaw, cosYaw;
+ sincosf(cam.yaw, &sinYaw, &cosYaw);
+
+ // Construct principal unit vectors
+ android::vec3 vAt = unitVectorFromPitchAndYaw(cam.pitch, cam.yaw);
+ android::vec3 vRt = android::vec3(cosYaw, sinYaw, 0.0f);
+ android::vec3 vUp = -cross(vAt, vRt);
+ android::vec3 eye = android::vec3(cam.position[X], cam.position[Y], cam.position[Z]);
+
+ android::mat4 Result(1.0f);
+ Result[0][0] = vRt.x;
+ Result[1][0] = vRt.y;
+ Result[2][0] = vRt.z;
+ Result[0][1] = vUp.x;
+ Result[1][1] = vUp.y;
+ Result[2][1] = vUp.z;
+ Result[0][2] =-vAt.x;
+ Result[1][2] =-vAt.y;
+ Result[2][2] =-vAt.z;
+ Result[3][0] =-dot(vRt, eye);
+ Result[3][1] =-dot(vUp, eye);
+ Result[3][2] = dot(vAt, eye);
+ return Result;
+}
+
+
+RenderTopView::RenderTopView(sp<IEvsEnumerator> enumerator,
+ const std::vector<ConfigManager::CameraInfo>& camList,
+ const ConfigManager& mConfig) :
+ mEnumerator(enumerator),
+ mConfig(mConfig) {
+
+ // Copy the list of cameras we're to employ into our local storage. We'll create and
+ // associate a streaming video texture when we are activated.
+ mActiveCameras.reserve(camList.size());
+ for (unsigned i=0; i<camList.size(); i++) {
+ mActiveCameras.emplace_back(camList[i]);
+ }
+}
+
+
+bool RenderTopView::activate() {
+ // Ensure GL is ready to go...
+ if (!prepareGL()) {
+ ALOGE("Error initializing GL");
+ return false;
+ }
+
+ // Load our shader programs
+ mPgmAssets.simpleTexture = buildShaderProgram(vtxShader_simpleTexture,
+ pixShader_simpleTexture,
+ "simpleTexture");
+ if (!mPgmAssets.simpleTexture) {
+ ALOGE("Failed to build shader program");
+ return false;
+ }
+ mPgmAssets.projectedTexture = buildShaderProgram(vtxShader_projectedTexture,
+ pixShader_projectedTexture,
+ "projectedTexture");
+ if (!mPgmAssets.projectedTexture) {
+ ALOGE("Failed to build shader program");
+ return false;
+ }
+
+
+ // Load the checkerboard text image
+ mTexAssets.checkerBoard.reset(createTextureFromPng(
+ "/system/etc/automotive/evs/LabeledChecker.png"));
+ if (!mTexAssets.checkerBoard->glId()) {
+ ALOGE("Failed to load checkerboard texture");
+ return false;
+ }
+
+ // Load the car image
+ mTexAssets.carTopView.reset(createTextureFromPng(
+ "/system/etc/automotive/evs/CarFromTop.png"));
+ if (!mTexAssets.carTopView->glId()) {
+ ALOGE("Failed to load carTopView texture");
+ return false;
+ }
+
+
+ // Set up streaming video textures for our associated cameras
+ for (auto&& cam: mActiveCameras) {
+ cam.tex.reset(createVideoTexture(mEnumerator, cam.info.cameraId.c_str(), sDisplay));
+ if (!cam.tex) {
+ ALOGE("Failed to set up video texture for %s (%s)",
+ cam.info.cameraId.c_str(), cam.info.function.c_str());
+// TODO: For production use, we may actually want to fail in this case, but not yet...
+// return false;
+ }
+ }
+
+ return true;
+}
+
+
+void RenderTopView::deactivate() {
+ // Release our video textures
+ // We can't hold onto it because some other Render object might need the same camera
+ // TODO: If start/stop costs become a problem, we could share video textures
+ for (auto&& cam: mActiveCameras) {
+ cam.tex = nullptr;
+ }
+}
+
+
+bool RenderTopView::drawFrame(const BufferDesc& tgtBuffer) {
+ // Tell GL to render to the given buffer
+ if (!attachRenderTarget(tgtBuffer)) {
+ ALOGE("Failed to attached render target");
+ return false;
+ }
+
+ // Set up our top down projection matrix from car space (world units, Xfwd, Yright, Zup)
+ // to view space (-1 to 1)
+ const float top = mConfig.getDisplayTopLocation();
+ const float bottom = mConfig.getDisplayBottomLocation();
+ const float right = mConfig.getDisplayRightLocation(sAspectRatio);
+ const float left = mConfig.getDisplayLeftLocation(sAspectRatio);
+
+ const float near = 10.0f; // arbitrary top of view volume
+ const float far = 0.0f; // ground plane is at zero
+
+ // We can use a simple, unrotated ortho view since the screen and car space axis are
+ // naturally aligned in the top down view.
+ // TODO: Not sure if flipping top/bottom here is "correct" or a double reverse...
+// orthoMatrix = android::mat4::ortho(left, right, bottom, top, near, far);
+ orthoMatrix = android::mat4::ortho(left, right, top, bottom, near, far);
+
+
+ // Refresh our video texture contents. We do it all at once in hopes of getting
+ // better coherence among images. This does not guarantee synchronization, of course...
+ for (auto&& cam: mActiveCameras) {
+ if (cam.tex) {
+ cam.tex->refresh();
+ }
+ }
+
+ // Iterate over all the cameras and project their images onto the ground plane
+ for (auto&& cam: mActiveCameras) {
+ renderCameraOntoGroundPlane(cam);
+ }
+
+ // Draw the car image
+ renderCarTopView();
+
+ // Wait for the rendering to finish
+ glFinish();
+
+ return true;
+}
+
+
+//
+// Responsible for drawing the car's self image in the top down view.
+// Draws in car model space (units of meters with origin at center of rear axel)
+// NOTE: We probably want to eventually switch to using a VertexArray based model system.
+//
+void RenderTopView::renderCarTopView() {
+ // Compute the corners of our image footprint in car space
+ const float carLengthInTexels = mConfig.carGraphicRearPixel() - mConfig.carGraphicFrontPixel();
+ const float carSpaceUnitsPerTexel = mConfig.getCarLength() / carLengthInTexels;
+ const float textureHeightInCarSpace = mTexAssets.carTopView->height() * carSpaceUnitsPerTexel;
+ const float textureAspectRatio = (float)mTexAssets.carTopView->width() /
+ mTexAssets.carTopView->height();
+ const float pixelsBehindCarInImage = mTexAssets.carTopView->height() -
+ mConfig.carGraphicRearPixel();
+ const float textureExtentBehindCarInCarSpace = pixelsBehindCarInImage * carSpaceUnitsPerTexel;
+
+ const float btCS = mConfig.getRearLocation() - textureExtentBehindCarInCarSpace;
+ const float tpCS = textureHeightInCarSpace + btCS;
+ const float ltCS = 0.5f * textureHeightInCarSpace * textureAspectRatio;
+ const float rtCS = -ltCS;
+
+ GLfloat vertsCarPos[] = { ltCS, tpCS, 0.0f, // left top in car space
+ rtCS, tpCS, 0.0f, // right top
+ ltCS, btCS, 0.0f, // left bottom
+ rtCS, btCS, 0.0f // right bottom
+ };
+ // NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image
+ GLfloat vertsCarTex[] = { 0.0f, 0.0f, // left top
+ 1.0f, 0.0f, // right top
+ 0.0f, 1.0f, // left bottom
+ 1.0f, 1.0f // right bottom
+ };
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glUseProgram(mPgmAssets.simpleTexture);
+ GLint loc = glGetUniformLocation(mPgmAssets.simpleTexture, "cameraMat");
+ glUniformMatrix4fv(loc, 1, false, orthoMatrix.asArray());
+ glBindTexture(GL_TEXTURE_2D, mTexAssets.carTopView->glId());
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+
+ glDisable(GL_BLEND);
+
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+}
+
+
+// NOTE: Might be worth reviewing the ideas at
+// http://math.stackexchange.com/questions/1691895/inverse-of-perspective-matrix
+// to see if that simplifies the math, although we'll still want to compute the actual ground
+// interception points taking into account the pitchLimit as below.
+void RenderTopView::renderCameraOntoGroundPlane(const ActiveCamera& cam) {
+ // How far is the farthest any camera should even consider projecting it's image?
+ const float visibleSizeV = mConfig.getDisplayTopLocation() - mConfig.getDisplayBottomLocation();
+ const float visibleSizeH = visibleSizeV * sAspectRatio;
+ const float maxRange = (visibleSizeH > visibleSizeV) ? visibleSizeH : visibleSizeV;
+
+ // Construct the projection matrix (View + Projection) associated with this sensor
+ // TODO: Consider just hard coding the far plane distance as it likely doesn't matter
+ const android::mat4 V = cameraLookMatrix(cam.info);
+ const android::mat4 P = perspective(cam.info.hfov, cam.info.vfov, cam.info.position[Z], maxRange);
+ const android::mat4 projectionMatix = P*V;
+
+ // Just draw the whole darn ground plane for now -- we're wasting fill rate, but so what?
+ // A 2x optimization would be to draw only the 1/2 space of the window in the direction
+ // the sensor is facing. A more complex solution would be to construct the intersection
+ // of the sensor volume with the ground plane and render only that geometry.
+ const float top = mConfig.getDisplayTopLocation();
+ const float bottom = mConfig.getDisplayBottomLocation();
+ const float wsHeight = top - bottom;
+ const float wsWidth = wsHeight * sAspectRatio;
+ const float right = wsWidth * 0.5f;
+ const float left = -right;
+
+ const android::vec3 topLeft(left, top, 0.0f);
+ const android::vec3 topRight(right, top, 0.0f);
+ const android::vec3 botLeft(left, bottom, 0.0f);
+ const android::vec3 botRight(right, bottom, 0.0f);
+
+ GLfloat vertsPos[] = { topLeft[X], topLeft[Y], topLeft[Z],
+ topRight[X], topRight[Y], topRight[Z],
+ botLeft[X], botLeft[Y], botLeft[Z],
+ botRight[X], botRight[Y], botRight[Z],
+ };
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsPos);
+ glEnableVertexAttribArray(0);
+
+
+ glDisable(GL_BLEND);
+
+ glUseProgram(mPgmAssets.projectedTexture);
+ GLint locCam = glGetUniformLocation(mPgmAssets.projectedTexture, "cameraMat");
+ glUniformMatrix4fv(locCam, 1, false, orthoMatrix.asArray());
+ GLint locProj = glGetUniformLocation(mPgmAssets.projectedTexture, "projectionMat");
+ glUniformMatrix4fv(locProj, 1, false, projectionMatix.asArray());
+
+ GLuint texId;
+ if (cam.tex) {
+ texId = cam.tex->glId();
+ } else {
+ texId = mTexAssets.checkerBoard->glId();
+ }
+ glBindTexture(GL_TEXTURE_2D, texId);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+
+ glDisableVertexAttribArray(0);
+}
diff --git a/evs/app/RenderTopView.h b/evs/app/RenderTopView.h
new file mode 100644
index 0000000..570718f
--- /dev/null
+++ b/evs/app/RenderTopView.h
@@ -0,0 +1,76 @@
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef CAR_EVS_APP_RENDERTOPVIEW_H
+#define CAR_EVS_APP_RENDERTOPVIEW_H
+
+
+#include "RenderBase.h"
+
+#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
+#include "ConfigManager.h"
+#include "VideoTex.h"
+#include <math/mat4.h>
+
+
+using namespace ::android::hardware::automotive::evs::V1_0;
+
+
+/*
+ * Combines the views from all available cameras into one reprojected top down view.
+ */
+class RenderTopView: public RenderBase {
+public:
+ RenderTopView(sp<IEvsEnumerator> enumerator,
+ const std::vector<ConfigManager::CameraInfo>& camList,
+ const ConfigManager& config);
+
+ virtual bool activate() override;
+ virtual void deactivate() override;
+
+ virtual bool drawFrame(const BufferDesc& tgtBuffer);
+
+protected:
+ struct ActiveCamera {
+ const ConfigManager::CameraInfo& info;
+ std::unique_ptr<VideoTex> tex;
+
+ ActiveCamera(const ConfigManager::CameraInfo& c) : info(c) {};
+ };
+
+ void renderCarTopView();
+ void renderCameraOntoGroundPlane(const ActiveCamera& cam);
+
+ sp<IEvsEnumerator> mEnumerator;
+ const ConfigManager& mConfig;
+ std::vector<ActiveCamera> mActiveCameras;
+
+ struct {
+ std::unique_ptr<TexWrapper> checkerBoard;
+ std::unique_ptr<TexWrapper> carTopView;
+ } mTexAssets;
+
+ struct {
+ GLuint simpleTexture;
+ GLuint projectedTexture;
+ } mPgmAssets;
+
+ android::mat4 orthoMatrix;
+};
+
+
+#endif //CAR_EVS_APP_RENDERTOPVIEW_H
diff --git a/evs/app/StreamHandler.cpp b/evs/app/StreamHandler.cpp
index ee50e96..5477642 100644
--- a/evs/app/StreamHandler.cpp
+++ b/evs/app/StreamHandler.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,40 +14,52 @@
* limitations under the License.
*/
-#define LOG_TAG "EvsTest"
+#define LOG_TAG "EVSAPP"
#include "StreamHandler.h"
#include <stdio.h>
#include <string.h>
-#include <android/log.h>
+#include <log/log.h>
#include <cutils/native_handle.h>
-#include <ui/GraphicBuffer.h>
-
-#include <algorithm> // std::min
-// For the moment, we're assuming that the underlying EVS driver we're working with
-// is providing 4 byte RGBx data. This is fine for loopback testing, although
-// real hardware is expected to provide YUV data -- most likly formatted as YV12
-static const unsigned kBytesPerPixel = 4; // assuming 4 byte RGBx pixels
-
-
-StreamHandler::StreamHandler(android::sp<IEvsCamera> pCamera, android::sp<IEvsDisplay> pDisplay) :
- mCamera(pCamera),
- mDisplay(pDisplay) {
+StreamHandler::StreamHandler(android::sp <IEvsCamera> pCamera) :
+ mCamera(pCamera)
+{
+ // We rely on the camera having at least two buffers available since we'll hold one and
+ // expect the camera to be able to capture a new image in the background.
+ pCamera->setMaxFramesInFlight(2);
}
-void StreamHandler::startStream() {
- // Mark ourselves as running
- mLock.lock();
- mRunning = true;
- mLock.unlock();
+void StreamHandler::shutdown()
+{
+ // Make sure we're not still streaming
+ blockingStopStream();
- // Tell the camera to start streaming
- mCamera->startVideoStream(this);
+ // At this point, the receiver thread is no longer running, so we can safely drop
+ // our remote object references so they can be freed
+ mCamera = nullptr;
+}
+
+
+bool StreamHandler::startStream() {
+ std::unique_lock<std::mutex> lock(mLock);
+
+ if (!mRunning) {
+ // Tell the camera to start streaming
+ Return <EvsResult> result = mCamera->startVideoStream(this);
+ if (result != EvsResult::OK) {
+ return false;
+ }
+
+ // Mark ourselves as running
+ mRunning = true;
+ }
+
+ return true;
}
@@ -64,7 +76,9 @@
// Wait until the stream has actually stopped
std::unique_lock<std::mutex> lock(mLock);
- mSignal.wait(lock, [this](){ return !mRunning; });
+ if (mRunning) {
+ mSignal.wait(lock, [this]() { return !mRunning; });
+ }
}
@@ -74,146 +88,80 @@
}
-unsigned StreamHandler::getFramesReceived() {
+bool StreamHandler::newFrameAvailable() {
std::unique_lock<std::mutex> lock(mLock);
- return mFramesReceived;
-};
-
-
-unsigned StreamHandler::getFramesCompleted() {
- std::unique_lock<std::mutex> lock(mLock);
- return mFramesCompleted;
-};
-
-
-Return<void> StreamHandler::deliverFrame(const BufferDesc& bufferArg) {
- ALOGD("Received a frame from the camera (%p)", bufferArg.memHandle.getNativeHandle());
-
- // Local flag we use to keep track of when the stream is stopping
- bool timeToStop = false;
-
- if (bufferArg.memHandle.getNativeHandle() == nullptr) {
- // Signal that the last frame has been received and that the stream should stop
- timeToStop = true;
- ALOGI("End of stream signaled");
- } else {
- // Get the output buffer we'll use to display the imagery
- BufferDesc tgtBuffer = {};
- mDisplay->getTargetBuffer([&tgtBuffer]
- (const BufferDesc& buff) {
- tgtBuffer = buff;
- ALOGD("Got output buffer (%p) with id %d cloned as (%p)",
- buff.memHandle.getNativeHandle(),
- tgtBuffer.bufferId,
- tgtBuffer.memHandle.getNativeHandle());
- }
- );
-
- if (tgtBuffer.memHandle == nullptr) {
- printf("Didn't get target buffer - frame lost\n");
- ALOGE("Didn't get requested output buffer -- skipping this frame.");
- } else {
- // Copy the contents of the of buffer.memHandle into tgtBuffer
- copyBufferContents(tgtBuffer, bufferArg);
-
- // TODO: Add a bit of overlay graphics?
- // TODO: Use OpenGL to render from texture?
- // NOTE: If we mess with the frame contents, we'll need to update the frame inspection
- // logic in the default (test) display driver.
-
- // Send the target buffer back for display
- ALOGD("Calling returnTargetBufferForDisplay (%p)",
- tgtBuffer.memHandle.getNativeHandle());
- Return<EvsResult> result = mDisplay->returnTargetBufferForDisplay(tgtBuffer);
- if (!result.isOk()) {
- printf("HIDL error on display buffer (%s)- frame lost\n",
- result.description().c_str());
- ALOGE("Error making the remote function call. HIDL said %s",
- result.description().c_str());
- } else if (result != EvsResult::OK) {
- printf("Display reported error - frame lost\n");
- ALOGE("We encountered error %d when returning a buffer to the display!",
- (EvsResult)result);
- } else {
- // Everything looks good! Keep track so tests or watch dogs can monitor progress
- mLock.lock();
- mFramesCompleted++;
- mLock.unlock();
- printf("frame OK\n");
- }
- }
-
- // Send the camera buffer back now that we're done with it
- ALOGD("Calling doneWithFrame");
- // TODO: Why is it that we get a HIDL crash if we pass back the cloned buffer?
- mCamera->doneWithFrame(bufferArg);
-
- ALOGD("Frame handling complete");
- }
-
-
- // Update our received frame count and notify anybody who cares that things have changed
- mLock.lock();
- if (timeToStop) {
- mRunning = false;
- } else {
- mFramesReceived++;
- }
- mLock.unlock();
- mSignal.notify_all();
-
-
- return Void();
+ return (mReadyBuffer >= 0);
}
-bool StreamHandler::copyBufferContents(const BufferDesc& tgtBuffer,
- const BufferDesc& srcBuffer) {
- bool success = true;
+const BufferDesc& StreamHandler::getNewFrame() {
+ std::unique_lock<std::mutex> lock(mLock);
- // Make sure we don't run off the end of either buffer
- const unsigned width = std::min(tgtBuffer.width,
- srcBuffer.width);
- const unsigned height = std::min(tgtBuffer.height,
- srcBuffer.height);
-
- sp<android::GraphicBuffer> tgt = new android::GraphicBuffer(
- tgtBuffer.memHandle, android::GraphicBuffer::CLONE_HANDLE,
- tgtBuffer.width, tgtBuffer.height, tgtBuffer.format, 1,
- tgtBuffer.usage, tgtBuffer.stride);
- sp<android::GraphicBuffer> src = new android::GraphicBuffer(
- srcBuffer.memHandle, android::GraphicBuffer::CLONE_HANDLE,
- srcBuffer.width, srcBuffer.height, srcBuffer.format, 1,
- srcBuffer.usage, srcBuffer.stride);
-
- // Lock our source buffer for reading
- unsigned char* srcPixels = nullptr;
- src->lock(GRALLOC_USAGE_SW_READ_OFTEN, (void **) &srcPixels);
-
- // Lock our target buffer for writing
- unsigned char* tgtPixels = nullptr;
- tgt->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void **) &tgtPixels);
-
- if (srcPixels && tgtPixels) {
- for (unsigned row = 0; row < height; row++) {
- // Copy the entire row of pixel data
- memcpy(tgtPixels, srcPixels, width * kBytesPerPixel);
-
- // Advance to the next row (keeping in mind that stride here is in units of pixels)
- tgtPixels += tgtBuffer.stride * kBytesPerPixel;
- srcPixels += srcBuffer.stride * kBytesPerPixel;
- }
+ if (mHeldBuffer >= 0) {
+ ALOGE("Ignored call for new frame while still holding the old one.");
} else {
- ALOGE("Failed to copy buffer contents");
- success = false;
+ if (mReadyBuffer < 0) {
+ ALOGE("Returning invalid buffer because we don't have any. Call newFrameAvailable first?");
+ mReadyBuffer = 0; // This is a lie!
+ }
+
+ // Move the ready buffer into the held position, and clear the ready position
+ mHeldBuffer = mReadyBuffer;
+ mReadyBuffer = -1;
}
- if (srcPixels) {
- src->unlock();
- }
- if (tgtPixels) {
- tgt->unlock();
+ return mBuffers[mHeldBuffer];
+}
+
+
+void StreamHandler::doneWithFrame(const BufferDesc& buffer) {
+ std::unique_lock<std::mutex> lock(mLock);
+
+ // We better be getting back the buffer we original delivered!
+ if ((mHeldBuffer < 0) || (buffer.bufferId != mBuffers[mHeldBuffer].bufferId)) {
+ ALOGE("StreamHandler::doneWithFrame got an unexpected buffer!");
}
- return success;
+ // Send the buffer back to the underlying camera
+ mCamera->doneWithFrame(mBuffers[mHeldBuffer]);
+
+ // Clear the held position
+ mHeldBuffer = -1;
+}
+
+
+Return<void> StreamHandler::deliverFrame(const BufferDesc& buffer) {
+ ALOGD("Received a frame from the camera (%p)", buffer.memHandle.getNativeHandle());
+
+ // Take the lock to protect our frame slots and running state variable
+ {
+ std::unique_lock <std::mutex> lock(mLock);
+
+ if (buffer.memHandle.getNativeHandle() == nullptr) {
+ // Signal that the last frame has been received and the stream is stopped
+ mRunning = false;
+ } else {
+ // Do we already have a "ready" frame?
+ if (mReadyBuffer >= 0) {
+ // Send the previously saved buffer back to the camera unused
+ mCamera->doneWithFrame(mBuffers[mReadyBuffer]);
+
+ // We'll reuse the same ready buffer index
+ } else if (mHeldBuffer >= 0) {
+ // The client is holding a buffer, so use the other slot for "on deck"
+ mReadyBuffer = 1 - mHeldBuffer;
+ } else {
+ // This is our first buffer, so just pick a slot
+ mReadyBuffer = 0;
+ }
+
+ // Save this frame until our client is interested in it
+ mBuffers[mReadyBuffer] = buffer;
+ }
+ }
+
+ // Notify anybody who cares that things have changed
+ mSignal.notify_all();
+
+ return Void();
}
diff --git a/evs/app/StreamHandler.h b/evs/app/StreamHandler.h
index eb2f6ce..9e1d3b7 100644
--- a/evs/app/StreamHandler.h
+++ b/evs/app/StreamHandler.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-#ifndef CAR_EVS_APP_STREAMHANDLER_H
-#define CAR_EVS_APP_STREAMHANDLER_H
+#ifndef EVS_VTS_STREAMHANDLER_H
+#define EVS_VTS_STREAMHANDLER_H
+
+#include <queue>
+
+#include "ui/GraphicBuffer.h"
#include <android/hardware/automotive/evs/1.0/IEvsCameraStream.h>
#include <android/hardware/automotive/evs/1.0/IEvsCamera.h>
@@ -29,38 +33,49 @@
using ::android::sp;
+/*
+ * StreamHandler:
+ * This class can be used to receive camera imagery from an IEvsCamera implementation. It will
+ * hold onto the most recent image buffer, returning older ones.
+ * Note that the video frames are delivered on a background thread, while the control interface
+ * is actuated from the applications foreground thread.
+ */
class StreamHandler : public IEvsCameraStream {
public:
- StreamHandler(android::sp <IEvsCamera> pCamera,
- android::sp <IEvsDisplay> pDisplay);
+ virtual ~StreamHandler() { shutdown(); };
- void startStream();
+ StreamHandler(android::sp <IEvsCamera> pCamera);
+ void shutdown();
+
+ bool startStream();
void asyncStopStream();
void blockingStopStream();
bool isRunning();
- unsigned getFramesReceived();
- unsigned getFramesCompleted();
+ bool newFrameAvailable();
+ const BufferDesc& getNewFrame();
+ void doneWithFrame(const BufferDesc& buffer);
private:
// Implementation for ::android::hardware::automotive::evs::V1_0::ICarCameraStream
Return<void> deliverFrame(const BufferDesc& buffer) override;
- // Local implementation details
- bool copyBufferContents(const BufferDesc& tgtBuffer, const BufferDesc& srcBuffer);
-
+ // Values initialized as startup
android::sp <IEvsCamera> mCamera;
- android::sp <IEvsDisplay> mDisplay;
+ // Since we get frames delivered to us asnchronously via the ICarCameraStream interface,
+ // we need to protect all member variables that may be modified while we're streaming
+ // (ie: those below)
std::mutex mLock;
std::condition_variable mSignal;
bool mRunning = false;
- unsigned mFramesReceived = 0; // Simple counter -- rolls over eventually!
- unsigned mFramesCompleted = 0; // Simple counter -- rolls over eventually!
+ BufferDesc mBuffers[2];
+ int mHeldBuffer = -1; // Index of the one currently held by the client
+ int mReadyBuffer = -1; // Index of the newest available buffer
};
-#endif //CAR_EVS_APP_STREAMHANDLER_H
+#endif //EVS_VTS_STREAMHANDLER_H
diff --git a/evs/app/TexWrapper.cpp b/evs/app/TexWrapper.cpp
new file mode 100644
index 0000000..7ec2191
--- /dev/null
+++ b/evs/app/TexWrapper.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include "TexWrapper.h"
+#include "glError.h"
+
+#include "log/log.h"
+
+#include <fcntl.h>
+#include <malloc.h>
+#include <png.h>
+
+
+/* Create an new empty GL texture that will be filled later */
+TexWrapper::TexWrapper() {
+ GLuint textureId;
+ glGenTextures(1, &textureId);
+ if (textureId <= 0) {
+ ALOGE("Didn't get a texture handle allocated: %s", getEGLError());
+ } else {
+ // Store the basic texture properties
+ id = textureId;
+ w = 0;
+ h = 0;
+ }
+}
+
+
+/* Wrap a texture that already allocated. The wrapper takes ownership. */
+TexWrapper::TexWrapper(GLuint textureId, unsigned width, unsigned height) {
+ // Store the basic texture properties
+ id = textureId;
+ w = width;
+ h = height;
+}
+
+
+TexWrapper::~TexWrapper() {
+ // Give the texture ID back
+ if (id > 0) {
+ glDeleteTextures(1, &id);
+ }
+ id = -1;
+}
+
+
+/* Factory to build TexWrapper objects from a given PNG file */
+TexWrapper* createTextureFromPng(const char * filename)
+{
+ // Open the PNG file
+ FILE *inputFile = fopen(filename, "rb");
+ if (inputFile == 0)
+ {
+ perror(filename);
+ return nullptr;
+ }
+
+ // Read the file header and validate that it is a PNG
+ static const int kSigSize = 8;
+ png_byte header[kSigSize] = {0};
+ fread(header, 1, kSigSize, inputFile);
+ if (png_sig_cmp(header, 0, kSigSize)) {
+ printf("%s is not a PNG.\n", filename);
+ fclose(inputFile);
+ return nullptr;
+ }
+
+ // Set up our control structure
+ png_structp pngControl = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!pngControl)
+ {
+ printf("png_create_read_struct failed.\n");
+ fclose(inputFile);
+ return nullptr;
+ }
+
+ // Set up our image info structure
+ png_infop pngInfo = png_create_info_struct(pngControl);
+ if (!pngInfo)
+ {
+ printf("error: png_create_info_struct returned 0.\n");
+ png_destroy_read_struct(&pngControl, nullptr, nullptr);
+ fclose(inputFile);
+ return nullptr;
+ }
+
+ // Install an error handler
+ if (setjmp(png_jmpbuf(pngControl))) {
+ printf("libpng reported an error\n");
+ png_destroy_read_struct(&pngControl, &pngInfo, nullptr);
+ fclose(inputFile);
+ return nullptr;
+ }
+
+ // Set up the png reader and fetch the remaining bits of the header
+ png_init_io(pngControl, inputFile);
+ png_set_sig_bytes(pngControl, kSigSize);
+ png_read_info(pngControl, pngInfo);
+
+ // Get basic information about the PNG we're reading
+ int bitDepth;
+ int colorFormat;
+ png_uint_32 width;
+ png_uint_32 height;
+ png_get_IHDR(pngControl, pngInfo,
+ &width, &height,
+ &bitDepth, &colorFormat,
+ NULL, NULL, NULL);
+
+ GLint format;
+ switch(colorFormat)
+ {
+ case PNG_COLOR_TYPE_RGB:
+ format = GL_RGB;
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ format = GL_RGBA;
+ break;
+ default:
+ printf("%s: Unknown libpng color format %d.\n", filename, colorFormat);
+ return nullptr;
+ }
+
+ // Refresh the values in the png info struct in case any transformation shave been applied.
+ png_read_update_info(pngControl, pngInfo);
+ int stride = png_get_rowbytes(pngControl, pngInfo);
+ stride += 3 - ((stride-1) % 4); // glTexImage2d requires rows to be 4-byte aligned
+
+ // Allocate storage for the pixel data
+ png_byte * buffer = (png_byte*)malloc(stride * height);
+ if (buffer == NULL)
+ {
+ printf("error: could not allocate memory for PNG image data\n");
+ png_destroy_read_struct(&pngControl, &pngInfo, nullptr);
+ fclose(inputFile);
+ return nullptr;
+ }
+
+ // libpng needs an array of pointers into the image data for each row
+ png_byte ** rowPointers = (png_byte**)malloc(height * sizeof(png_byte*));
+ if (rowPointers == NULL)
+ {
+ printf("Failed to allocate temporary row pointers\n");
+ png_destroy_read_struct(&pngControl, &pngInfo, nullptr);
+ free(buffer);
+ fclose(inputFile);
+ return nullptr;
+ }
+ for (unsigned int r = 0; r < height; r++)
+ {
+ rowPointers[r] = buffer + r*stride;
+ }
+
+
+ // Read in the actual image bytes
+ png_read_image(pngControl, rowPointers);
+ png_read_end(pngControl, nullptr);
+
+
+ // Set up the OpenGL texture to contain this image
+ GLuint textureId;
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ // Send the image data to GL
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+
+ // Initialize the sampling properties (it seems the sample may not work if this isn't done)
+ // The user of this texture may very well want to set their own filtering, but we're going
+ // to pay the (minor) price of setting this up for them to avoid the dreaded "black image" if
+ // they forget.
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ // clean up
+ png_destroy_read_struct(&pngControl, &pngInfo, nullptr);
+ free(buffer);
+ free(rowPointers);
+ fclose(inputFile);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+
+ // Return the texture
+ return new TexWrapper(textureId, width, height);
+}
diff --git a/evs/app/TexWrapper.h b/evs/app/TexWrapper.h
new file mode 100644
index 0000000..7c92247
--- /dev/null
+++ b/evs/app/TexWrapper.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#ifndef TEXWRAPPER_H
+#define TEXWRAPPER_H
+
+#include <GLES2/gl2.h>
+
+
+class TexWrapper {
+public:
+ TexWrapper(GLuint textureId, unsigned width, unsigned height);
+ virtual ~TexWrapper();
+
+ GLuint glId() { return id; };
+ unsigned width() { return w; };
+ unsigned height() { return h; };
+
+protected:
+ TexWrapper();
+
+ GLuint id;
+ unsigned w;
+ unsigned h;
+};
+
+
+TexWrapper* createTextureFromPng(const char* filename);
+
+#endif // TEXWRAPPER_H
\ No newline at end of file
diff --git a/evs/app/VideoTex.cpp b/evs/app/VideoTex.cpp
new file mode 100644
index 0000000..10d54bd
--- /dev/null
+++ b/evs/app/VideoTex.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include <vector>
+#include <stdio.h>
+#include <fcntl.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <malloc.h>
+#include <png.h>
+
+#include "VideoTex.h"
+#include "glError.h"
+
+#include <ui/GraphicBuffer.h>
+
+// Eventually we shouldn't need this dependency, but for now the
+// graphics allocator interface isn't fully supported on all platforms
+// and this is our work around.
+using ::android::GraphicBuffer;
+
+
+VideoTex::VideoTex(sp<IEvsEnumerator> pEnum,
+ sp<IEvsCamera> pCamera,
+ sp<StreamHandler> pStreamHandler,
+ EGLDisplay glDisplay)
+ : TexWrapper()
+ , mEnumerator(pEnum)
+ , mCamera(pCamera)
+ , mStreamHandler(pStreamHandler)
+ , mDisplay(glDisplay) {
+ // Nothing but initialization here...
+}
+
+VideoTex::~VideoTex() {
+ // Tell the stream to stop flowing
+ mStreamHandler->asyncStopStream();
+
+ // Close the camera
+ mEnumerator->closeCamera(mCamera);
+
+ // Drop our device texture image
+ if (mKHRimage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(mDisplay, mKHRimage);
+ mKHRimage = EGL_NO_IMAGE_KHR;
+ }
+}
+
+
+// Return true if the texture contents are changed
+bool VideoTex::refresh() {
+ if (!mStreamHandler->newFrameAvailable()) {
+ // No new image has been delivered, so there's nothing to do here
+ return false;
+ }
+
+ // If we already have an image backing us, then it's time to return it
+ if (mImageBuffer.memHandle.getNativeHandle() != nullptr) {
+ // Drop our device texture image
+ if (mKHRimage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(mDisplay, mKHRimage);
+ mKHRimage = EGL_NO_IMAGE_KHR;
+ }
+
+ // Return it since we're done with it
+ mStreamHandler->doneWithFrame(mImageBuffer);
+ }
+
+ // Get the new image we want to use as our contents
+ mImageBuffer = mStreamHandler->getNewFrame();
+
+
+ // create a GraphicBuffer from the existing handle
+ sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(mImageBuffer.memHandle,
+ GraphicBuffer::CLONE_HANDLE,
+ mImageBuffer.width, mImageBuffer.height,
+ mImageBuffer.format, 1, // layer count
+ GRALLOC_USAGE_HW_TEXTURE,
+ mImageBuffer.stride);
+ if (pGfxBuffer.get() == nullptr) {
+ ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
+ // Returning "true" in this error condition because we already released the
+ // previous image (if any) and so the texture may change in unpredictable ways now!
+ return true;
+ }
+
+ // Get a GL compatible reference to the graphics buffer we've been given
+ EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+ EGLClientBuffer clientBuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
+ mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, clientBuf,
+ eglImageAttributes);
+ if (mKHRimage == EGL_NO_IMAGE_KHR) {
+ const char *msg = getEGLError();
+ ALOGE("error creating EGLImage: %s", msg);
+ } else {
+ // Update the texture handle we already created to refer to this gralloc buffer
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, glId());
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
+
+ // Initialize the sampling properties (it seems the sample may not work if this isn't done)
+ // The user of this texture may very well want to set their own filtering, but we're going
+ // to pay the (minor) price of setting this up for them to avoid the dreaded "black image"
+ // if they forget.
+ // TODO: Can we do this once for the texture ID rather than ever refresh?
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ return true;
+}
+
+
+VideoTex* createVideoTexture(sp<IEvsEnumerator> pEnum,
+ const char* evsCameraId,
+ EGLDisplay glDisplay) {
+ // Set up the camera to feed this texture
+ sp<IEvsCamera> pCamera = pEnum->openCamera(evsCameraId);
+ if (pCamera.get() == nullptr) {
+ ALOGE("Failed to allocate new EVS Camera interface for %s", evsCameraId);
+ return nullptr;
+ }
+
+ // Initialize the stream that will help us update this texture's contents
+ sp<StreamHandler> pStreamHandler = new StreamHandler(pCamera);
+ if (pStreamHandler.get() == nullptr) {
+ ALOGE("failed to allocate FrameHandler");
+ return nullptr;
+ }
+
+ // Start the video stream
+ if (!pStreamHandler->startStream()) {
+ printf("Couldn't start the camera stream (%s)\n", evsCameraId);
+ ALOGE("start stream failed for %s", evsCameraId);
+ return nullptr;
+ }
+
+ return new VideoTex(pEnum, pCamera, pStreamHandler, glDisplay);
+}
diff --git a/evs/app/VideoTex.h b/evs/app/VideoTex.h
new file mode 100644
index 0000000..0b95c1d
--- /dev/null
+++ b/evs/app/VideoTex.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#ifndef VIDEOTEX_H
+#define VIDEOTEX_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+
+#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
+
+#include "TexWrapper.h"
+#include "StreamHandler.h"
+
+
+using namespace ::android::hardware::automotive::evs::V1_0;
+
+
+class VideoTex: public TexWrapper {
+ friend VideoTex* createVideoTexture(sp<IEvsEnumerator> pEnum,
+ const char * evsCameraId,
+ EGLDisplay glDisplay);
+
+public:
+ VideoTex() = delete;
+ virtual ~VideoTex();
+
+ bool refresh(); // returns true if the texture contents were updated
+
+private:
+ VideoTex(sp<IEvsEnumerator> pEnum,
+ sp<IEvsCamera> pCamera,
+ sp<StreamHandler> pStreamHandler,
+ EGLDisplay glDisplay);
+
+ sp<IEvsEnumerator> mEnumerator;
+ sp<IEvsCamera> mCamera;
+ sp<StreamHandler> mStreamHandler;
+ BufferDesc mImageBuffer;
+
+ EGLDisplay mDisplay;
+ EGLImageKHR mKHRimage = EGL_NO_IMAGE_KHR;
+};
+
+
+VideoTex* createVideoTexture(sp<IEvsEnumerator> pEnum,
+ const char * deviceName,
+ EGLDisplay glDisplay);
+
+#endif // VIDEOTEX_H
\ No newline at end of file
diff --git a/evs/app/WindowSurface.cpp b/evs/app/WindowSurface.cpp
new file mode 100644
index 0000000..a3f56bc
--- /dev/null
+++ b/evs/app/WindowSurface.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#include "WindowSurface.h"
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <ui/DisplayInfo.h>
+
+using namespace android;
+
+WindowSurface::WindowSurface() {
+ status_t err;
+
+ sp<SurfaceComposerClient> surfaceComposerClient = new SurfaceComposerClient;
+ err = surfaceComposerClient->initCheck();
+ if (err != NO_ERROR) {
+ fprintf(stderr, "SurfaceComposerClient::initCheck error: %#x\n", err);
+ return;
+ }
+
+ // Get main display parameters.
+ sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain);
+ DisplayInfo mainDpyInfo;
+ err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: unable to get display characteristics\n");
+ return;
+ }
+
+ uint32_t width, height;
+ if (mainDpyInfo.orientation != DISPLAY_ORIENTATION_0 &&
+ mainDpyInfo.orientation != DISPLAY_ORIENTATION_180) {
+ // rotated
+ width = mainDpyInfo.h;
+ height = mainDpyInfo.w;
+ } else {
+ width = mainDpyInfo.w;
+ height = mainDpyInfo.h;
+ }
+
+ sp<SurfaceControl> sc = surfaceComposerClient->createSurface(
+ String8("Benchmark"), width, height,
+ PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
+ if (sc == NULL || !sc->isValid()) {
+ fprintf(stderr, "Failed to create SurfaceControl\n");
+ return;
+ }
+
+ SurfaceComposerClient::openGlobalTransaction();
+ err = sc->setLayer(0x7FFFFFFF); // always on top
+ if (err != NO_ERROR) {
+ fprintf(stderr, "SurfaceComposer::setLayer error: %#x\n", err);
+ return;
+ }
+
+ err = sc->show();
+ if (err != NO_ERROR) {
+ fprintf(stderr, "SurfaceComposer::show error: %#x\n", err);
+ return;
+ }
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ mSurfaceControl = sc;
+}
+
+EGLNativeWindowType WindowSurface::getSurface() const {
+ sp<ANativeWindow> anw = mSurfaceControl->getSurface();
+ return (EGLNativeWindowType) anw.get();
+}
+
diff --git a/evs/app/WindowSurface.h b/evs/app/WindowSurface.h
new file mode 100644
index 0000000..966ea11
--- /dev/null
+++ b/evs/app/WindowSurface.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef OPENGL_TESTS_WINDOWSURFACE_H
+#define OPENGL_TESTS_WINDOWSURFACE_H
+
+#include <gui/SurfaceControl.h>
+
+#include <EGL/egl.h>
+
+
+/*
+ * A window that covers the entire display surface.
+ *
+ * The window is destroyed when this object is destroyed, so don't try
+ * to use the surface after that point.
+ */
+class WindowSurface {
+public:
+ // Creates the window.
+ WindowSurface();
+
+ // Retrieves a handle to the window.
+ EGLNativeWindowType getSurface() const;
+
+private:
+ WindowSurface(const WindowSurface&) = delete;
+ WindowSurface& operator=(const WindowSurface&) = delete;
+
+ android::sp<android::SurfaceControl> mSurfaceControl;
+};
+
+#endif /* OPENGL_TESTS_WINDOWSURFACE_H */
diff --git a/evs/app/config.json b/evs/app/config.json
index 791dc4e..5de8bb5 100644
--- a/evs/app/config.json
+++ b/evs/app/config.json
@@ -6,8 +6,6 @@
"rearExtent" : 40
},
"display" : {
- "width" : 640,
- "height" : 480,
"frontRange" : 100,
"rearRange" : 100
},
@@ -17,57 +15,37 @@
},
"cameras" : [
{
- "name" : "rightFront",
- "x" : 36.0,
- "y" : 90.0,
- "z" : 36,
- "yaw" : -45,
- "pitch" : -25,
- "hfov" : 60,
- "vfov" : 40
+ "cameraId" : "/dev/video32",
+ "function" : "reverse,park",
+ "x" : 0.0,
+ "y" : -40.0,
+ "z" : 48,
+ "yaw" : 180,
+ "pitch" : -30,
+ "hfov" : 125,
+ "vfov" :103
},
{
- "name" : "rightRear",
- "function" : "right",
- "x" : 36.0,
- "y" : -10,
- "z" : 36,
- "yaw" : -135,
- "pitch" : -25,
- "hfov" : 60,
- "vfov" : 40
- },
- {
- "name" : "left",
- "function" : "left",
- "x" : -36.0,
- "y" : 80,
- "z" : 30,
- "yaw" : 90,
- "pitch" : -45,
- "hfov" : 90,
- "vfov" : 90
- },
- {
- "name" : "front",
+ "cameraId" : "/dev/video45",
+ "function" : "front,park",
"x" : 0.0,
"y" : 100.0,
"z" : 48,
"yaw" : 0,
"pitch" : -10,
- "hfov" : 60,
- "vfov" : 42
+ "hfov" : 70,
+ "vfov" : 43
},
{
- "name" : "rear",
- "function" : "rear",
- "x" : 0.0,
- "y" : -40,
- "z" : 30,
- "yaw" : 180,
- "pitch" : -45,
- "hfov" : 90,
- "vfov" : 60
+ "cameraId" : "/dev/video0",
+ "function" : "right,park",
+ "x" : 36.0,
+ "y" : 60.0,
+ "z" : 32,
+ "yaw" : -90,
+ "pitch" : -30,
+ "hfov" : 60,
+ "vfov" : 42
}
]
-}
\ No newline at end of file
+}
diff --git a/evs/app/evs_app.cpp b/evs/app/evs_app.cpp
index d17119e..9d33e05 100644
--- a/evs/app/evs_app.cpp
+++ b/evs/app/evs_app.cpp
@@ -40,18 +40,56 @@
using android::hardware::joinRpcThreadpool;
-// TODO: Should this somehow be a shared definition with the module itself?
-const static char kEvsServiceName[] = "EvsSharedEnumerator";
+// Helper to subscribe to VHal notifications
+static bool subscribeToVHal(sp<IVehicle> pVnet,
+ sp<IVehicleCallback> listener,
+ VehicleProperty propertyId) {
+ assert(pVnet != nullptr);
+ assert(listener != nullptr);
+
+ // Register for vehicle state change callbacks we care about
+ // Changes in these values are what will trigger a reconfiguration of the EVS pipeline
+ SubscribeOptions optionsData[] = {
+ {
+ .propId = static_cast<int32_t>(propertyId),
+ .flags = SubscribeFlags::DEFAULT
+ },
+ };
+ hidl_vec <SubscribeOptions> options;
+ options.setToExternal(optionsData, arraysize(optionsData));
+ StatusCode status = pVnet->subscribe(listener, options);
+ if (status != StatusCode::OK) {
+ ALOGW("VHAL subscription for property 0x%08X failed with code %d.", propertyId, status);
+ return false;
+ }
+
+ return true;
+}
// Main entry point
-int main(int /* argc */, char** /* argv */)
+int main(int argc, char** argv)
{
- printf("EVS app starting\n");
+ ALOGI("EVS app starting\n");
+
+ // Set up default behavior, then check for command line options
+ bool useVehicleHal = true;
+ const char* evsServiceName = "default";
+ for (int i=1; i< argc; i++) {
+ if (strcmp(argv[i], "--test") == 0) {
+ useVehicleHal = false;
+ } else if (strcmp(argv[i], "--hw") == 0) {
+ evsServiceName = "EvsEnumeratorHw";
+ } else if (strcmp(argv[i], "--mock") == 0) {
+ evsServiceName = "EvsEnumeratorHw-Mock";
+ } else {
+ printf("Ignoring unrecognized command line arg '%s'\n", argv[i]);
+ }
+ }
// Load our configuration information
ConfigManager config;
- config.initialize("config.json");
+ config.initialize("/system/etc/automotive/evs/config.json");
// Set thread pool size to one to avoid concurrent events from the HAL.
// This pool will handle the EvsCameraStream callbacks.
@@ -64,9 +102,9 @@
// Get the EVS manager service
ALOGI("Acquiring EVS Enumerator");
- android::sp<IEvsEnumerator> pEvs = IEvsEnumerator::getService(kEvsServiceName);
+ android::sp<IEvsEnumerator> pEvs = IEvsEnumerator::getService(evsServiceName);
if (pEvs.get() == nullptr) {
- ALOGE("getService returned NULL. Exiting.");
+ ALOGE("getService(%s) returned NULL. Exiting.", evsServiceName);
return 1;
}
@@ -80,52 +118,43 @@
}
// Connect to the Vehicle HAL so we can monitor state
- ALOGI("Connecting to Vehicle HAL");
- android::sp <IVehicle> pVnet = IVehicle::getService();
- if (pVnet.get() == nullptr) {
-#if 0
- ALOGE("Vehicle HAL getService returned NULL. Exiting.");
- return 1;
-#else
- // While testing, at least, we want to be able to run without a vehicle
- ALOGE("getService returned NULL, but we're in test, so we'll pretend to be in reverse");
-#endif
- } else {
- // Register for vehicle state change callbacks we care about
- // Changes in these values are what will trigger a reconfiguration of the EVS pipeline
- SubscribeOptions optionsData[2] = {
- {
- .propId = static_cast<int32_t>(VehicleProperty::GEAR_SELECTION),
- .flags = SubscribeFlags::DEFAULT
- },
- {
- .propId = static_cast<int32_t>(VehicleProperty::TURN_SIGNAL_STATE),
- .flags = SubscribeFlags::DEFAULT
- },
- };
- hidl_vec<SubscribeOptions> options;
- options.setToExternal(optionsData, arraysize(optionsData));
- StatusCode status = pVnet->subscribe(pEvsListener, options);
- if (status != StatusCode::OK) {
- ALOGE("Subscription to vehicle notifications failed with code %d. Exiting.", status);
+ sp<IVehicle> pVnet;
+ if (useVehicleHal) {
+ ALOGI("Connecting to Vehicle HAL");
+ pVnet = IVehicle::getService();
+ if (pVnet.get() == nullptr) {
+ ALOGE("Vehicle HAL getService returned NULL. Exiting.");
return 1;
+ } else {
+ // Register for vehicle state change callbacks we care about
+ // Changes in these values are what will trigger a reconfiguration of the EVS pipeline
+ if (!subscribeToVHal(pVnet, pEvsListener, VehicleProperty::GEAR_SELECTION)) {
+ ALOGE("Without gear notification, we can't support EVS. Exiting.");
+ return 1;
+ }
+ if (!subscribeToVHal(pVnet, pEvsListener, VehicleProperty::TURN_SIGNAL_STATE)) {
+ ALOGW("Didn't get turn signal notificaitons, so we'll ignore those.");
+ }
}
+ } else {
+ ALOGW("Test mode selected, so not talking to Vehicle HAL");
}
// Configure ourselves for the current vehicle state at startup
ALOGI("Constructing state controller");
EvsStateControl *pStateController = new EvsStateControl(pVnet, pEvs, pDisplay, config);
- if (!pStateController->configureForVehicleState()) {
+ if (!pStateController->startUpdateLoop()) {
ALOGE("Initial configuration failed. Exiting.");
return 1;
- } else {
- // Run forever, reacting to events as necessary
- ALOGI("Entering running state");
- pEvsListener->run(pStateController);
}
+ // Run forever, reacting to events as necessary
+ ALOGI("Entering running state");
+ pEvsListener->run(pStateController);
+
// In normal operation, we expect to run forever, but in some error conditions we'll quit.
// One known example is if another process preempts our registration for our service name.
- printf("EVS Listener stopped. Exiting.\n");
+ ALOGE("EVS Listener stopped. Exiting.");
+
return 0;
}
diff --git a/evs/app/evs_app.rc b/evs/app/evs_app.rc
new file mode 100644
index 0000000..a61edfe
--- /dev/null
+++ b/evs/app/evs_app.rc
@@ -0,0 +1,5 @@
+service evs_app /system/bin/evs_app
+ class hal
+ priority -20
+ user automotive_evs
+ group automotive_evs
diff --git a/evs/app/glError.cpp b/evs/app/glError.cpp
new file mode 100644
index 0000000..53188d3
--- /dev/null
+++ b/evs/app/glError.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <stdio.h>
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+
+
+const char *getEGLError(void) {
+ switch (eglGetError()) {
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "Unknown error";
+ }
+}
+
+
+const char *getGLFramebufferError(void) {
+ switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
+ case GL_FRAMEBUFFER_COMPLETE:
+ return "GL_FRAMEBUFFER_COMPLETE";
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ return "GL_FRAMEBUFFER_UNSUPPORTED";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ default:
+ return "Unknown error";
+ }
+}
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java b/evs/app/glError.h
similarity index 65%
copy from tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
copy to evs/app/glError.h
index 94abeb1..52c5d5a 100644
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
+++ b/evs/app/glError.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package com.android.support.car.apitest;
+#ifndef GLERROR_H
+#define GLERROR_H
-import android.support.car.app.CarProxyActivity;
+const char *getEGLError(void);
-public class TestCarProxyActivity extends CarProxyActivity {
+const char *getGLFramebufferError(void);
- public TestCarProxyActivity() {
- super(TestCarActivity.class);
- }
-}
+#endif // GLERROR_H
\ No newline at end of file
diff --git a/evs/app/shader.cpp b/evs/app/shader.cpp
new file mode 100644
index 0000000..6922fbe
--- /dev/null
+++ b/evs/app/shader.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include "shader.h"
+
+#include <stdio.h>
+#include <memory>
+
+
+// Given shader source, load and compile it
+static GLuint loadShader(GLenum type, const char *shaderSrc, const char *name) {
+ // Create the shader object
+ GLuint shader = glCreateShader (type);
+ if (shader == 0) {
+ return 0;
+ }
+
+ // Load and compile the shader
+ glShaderSource(shader, 1, &shaderSrc, nullptr);
+ glCompileShader(shader);
+
+ // Verify the compilation worked as expected
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ printf("Error compiling %s shader for %s\n", (type==GL_VERTEX_SHADER) ? "vtx":"pxl", name);
+
+ GLint size = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
+ if (size > 0)
+ {
+ // Get and report the error message
+ std::unique_ptr<char> infoLog(new char[size]);
+ glGetShaderInfoLog(shader, size, NULL, infoLog.get());
+ printf(" msg:\n%s\n", infoLog.get());
+ }
+
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+}
+
+
+// Create a program object given vertex and pixels shader source
+GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc, const char* name) {
+ GLuint program = glCreateProgram();
+ if (program == 0) {
+ printf("Failed to allocate program object\n");
+ return 0;
+ }
+
+ // Compile the shaders and bind them to this program
+ GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc, name);
+ if (vertexShader == 0) {
+ printf("Failed to load vertex shader\n");
+ glDeleteProgram(program);
+ return 0;
+ }
+ GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc, name);
+ if (pixelShader == 0) {
+ printf("Failed to load pixel shader\n");
+ glDeleteProgram(program);
+ glDeleteShader(vertexShader);
+ return 0;
+ }
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, pixelShader);
+
+ // Link the program
+ glLinkProgram(program);
+ GLint linked = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ if (!linked)
+ {
+ printf("Error linking program.\n");
+ GLint size = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
+ if (size > 0)
+ {
+ // Get and report the error message
+ std::unique_ptr<char> infoLog(new char[size]);
+ glGetProgramInfoLog(program, size, NULL, infoLog.get());
+ printf(" msg: %s\n", infoLog.get());
+ }
+
+ glDeleteProgram(program);
+ glDeleteShader(vertexShader);
+ glDeleteShader(pixelShader);
+ return 0;
+ }
+
+
+#if 0 // Debug output to diagnose shader parameters
+ GLint numShaderParams;
+ GLchar paramName[128];
+ GLint paramSize;
+ GLenum paramType;
+ const char *typeName = "?";
+ printf("Shader parameters for %s:\n", name);
+ glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numShaderParams);
+ for (GLint i=0; i<numShaderParams; i++) {
+ glGetActiveUniform(program,
+ i,
+ sizeof(paramName),
+ nullptr,
+ ¶mSize,
+ ¶mType,
+ paramName);
+ switch (paramType) {
+ case GL_FLOAT: typeName = "GL_FLOAT"; break;
+ case GL_FLOAT_VEC4: typeName = "GL_FLOAT_VEC4"; break;
+ case GL_FLOAT_MAT4: typeName = "GL_FLOAT_MAT4"; break;
+ case GL_SAMPLER_2D: typeName = "GL_SAMPLER_2D"; break;
+ }
+
+ printf(" %2d: %s\t (%d) of type %s(%d)\n", i, paramName, paramSize, typeName, paramType);
+ }
+#endif
+
+
+ return program;
+}
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java b/evs/app/shader.h
similarity index 65%
copy from tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
copy to evs/app/shader.h
index 94abeb1..476a2f0 100644
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
+++ b/evs/app/shader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.support.car.apitest;
+#ifndef SHADER_H
+#define SHADER_H
-import android.support.car.app.CarProxyActivity;
+#include <GLES2/gl2.h>
-public class TestCarProxyActivity extends CarProxyActivity {
- public TestCarProxyActivity() {
- super(TestCarActivity.class);
- }
-}
+// Create a program object given vertex and pixels shader source
+GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc, const char* name);
+
+#endif // SHADER_H
\ No newline at end of file
diff --git a/evs/app/shader_projectedTex.h b/evs/app/shader_projectedTex.h
new file mode 100644
index 0000000..65e9109
--- /dev/null
+++ b/evs/app/shader_projectedTex.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef SHADER_PROJECTED_TEX_H
+#define SHADER_PROJECTED_TEX_H
+
+// This shader is used to project a sensors image onto wold space geometry
+// as if it were projected from the original sensor's point of view in the world.
+
+const char vtxShader_projectedTexture[] = ""
+ "#version 300 es \n"
+ "layout(location = 0) in vec4 pos; \n"
+ "uniform mat4 cameraMat; \n"
+ "uniform mat4 projectionMat; \n"
+ "out vec4 projectionSpace; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = cameraMat * pos; \n"
+ " projectionSpace = projectionMat * pos; \n"
+ "} \n";
+
+const char pixShader_projectedTexture[] =
+ "#version 300 es \n"
+ "precision mediump float; \n"
+ "uniform sampler2D tex; \n"
+ "in vec4 projectionSpace; \n"
+ "out vec4 color; \n"
+ "void main() \n"
+ "{ \n"
+ " const vec2 zero = vec2(0.0f, 0.0f); \n"
+ " const vec2 one = vec2(1.0f, 1.0f); \n"
+ " \n"
+ " // Compute perspective correct texture coordinates \n"
+ " // in the sensor map \n"
+ " vec2 cs = projectionSpace.xy / projectionSpace.w; \n"
+ " \n"
+ " // flip the texture! \n"
+ " cs.y = -cs.y; \n"
+ " \n"
+ " // scale from -1/1 clip space to 0/1 uv space \n"
+ " vec2 uv = (cs + 1.0f) * 0.5f; \n"
+ " \n"
+ " // Bail if we don't have a valid projection \n"
+ " if ((projectionSpace.w <= 0.0f) || \n"
+ " any(greaterThan(uv, one)) || \n"
+ " any(lessThan(uv, zero))) { \n"
+ " discard; \n"
+ " } \n"
+ " color = texture(tex, uv); \n"
+ "} \n";
+
+#endif // SHADER_PROJECTED_TEX_H
\ No newline at end of file
diff --git a/evs/app/shader_simpleTex.h b/evs/app/shader_simpleTex.h
new file mode 100644
index 0000000..0e962bd
--- /dev/null
+++ b/evs/app/shader_simpleTex.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef SHADER_SIMPLE_TEX_H
+#define SHADER_SIMPLE_TEX_H
+
+const char vtxShader_simpleTexture[] = ""
+ "#version 300 es \n"
+ "layout(location = 0) in vec4 pos; \n"
+ "layout(location = 1) in vec2 tex; \n"
+ "uniform mat4 cameraMat; \n"
+ "out vec2 uv; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = cameraMat * pos; \n"
+ " uv = tex; \n"
+ "} \n";
+
+const char pixShader_simpleTexture[] =
+ "#version 300 es \n"
+ "precision mediump float; \n"
+ "uniform sampler2D tex; \n"
+ "in vec2 uv; \n"
+ "out vec4 color; \n"
+ "void main() \n"
+ "{ \n"
+ " vec4 texel = texture(tex, uv); \n"
+ " color = texel; \n"
+ "} \n";
+
+#endif // SHADER_SIMPLE_TEX_H
\ No newline at end of file
diff --git a/evs/manager/Android.mk b/evs/manager/Android.mk
index 9522f81..fe384d9 100644
--- a/evs/manager/Android.mk
+++ b/evs/manager/Android.mk
@@ -9,26 +9,24 @@
HalCamera.cpp \
VirtualCamera.cpp \
-LOCAL_C_INCLUDES += \
- frameworks/base/include \
- packages/services/Car/evs/manager \
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
libutils \
libui \
- libhwbinder \
libhidlbase \
libhidltransport \
- libtinyalsa \
libhardware \
android.hardware.automotive.evs@1.0 \
-LOCAL_STRIP_MODULE := keep_symbols
+
+LOCAL_INIT_RC := android.automotive.evs.manager@1.0.rc
LOCAL_MODULE := android.automotive.evs.manager@1.0
+
LOCAL_MODULE_TAGS := optional
+LOCAL_STRIP_MODULE := keep_symbols
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
diff --git a/evs/manager/ServiceNames.h b/evs/manager/ServiceNames.h
index 3d85001..46705a9 100644
--- a/evs/manager/ServiceNames.h
+++ b/evs/manager/ServiceNames.h
@@ -16,7 +16,7 @@
// This is the name as which we'll register ourselves
-const static char kManagedEnumeratorName[] = "EvsSharedEnumerator";
+const static char kManagedEnumeratorName[] = "default";
// This is the name of the hardware provider to which we'll bind
// TODO: How should we configure these values to target appropriate hardware?
diff --git a/evs/manager/android.automotive.evs.manager@1.0.rc b/evs/manager/android.automotive.evs.manager@1.0.rc
new file mode 100644
index 0000000..8a53ba7
--- /dev/null
+++ b/evs/manager/android.automotive.evs.manager@1.0.rc
@@ -0,0 +1,6 @@
+service evs_manager /system/bin/android.automotive.evs.manager@1.0
+ class hal
+ priority -20
+ user automotive_evs
+ group automotive_evs
+ onrestart restart evs_app
diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
new file mode 100644
index 0000000..231ab6e
--- /dev/null
+++ b/evs/sampleDriver/Android.mk
@@ -0,0 +1,44 @@
+LOCAL_PATH:= $(call my-dir)
+
+##################################
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ service.cpp \
+ EvsEnumerator.cpp \
+ EvsV4lCamera.cpp \
+ EvsGlDisplay.cpp \
+ GlWrapper.cpp \
+ VideoCapture.cpp \
+ bufferCopy.cpp \
+
+
+LOCAL_SHARED_LIBRARIES := \
+ android.hardware.automotive.evs@1.0 \
+ libui \
+ libgui \
+ libEGL \
+ libGLESv2 \
+ libbase \
+ libbinder \
+ libcutils \
+ libhardware \
+ libhidlbase \
+ libhidltransport \
+ liblog \
+ libutils \
+
+LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc
+
+LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_STRIP_MODULE := keep_symbols
+
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+
+# NOTE: It can be helpful, while debugging, to disable optimizations
+#LOCAL_CFLAGS += -O0 -g
+
+include $(BUILD_EXECUTABLE)
diff --git a/evs/sampleDriver/EvsEnumerator.cpp b/evs/sampleDriver/EvsEnumerator.cpp
new file mode 100644
index 0000000..25b8133
--- /dev/null
+++ b/evs/sampleDriver/EvsEnumerator.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
+
+#include "EvsEnumerator.h"
+#include "EvsV4lCamera.h"
+#include "EvsGlDisplay.h"
+
+#include <dirent.h>
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_0 {
+namespace implementation {
+
+
+// NOTE: All members values are static so that all clients operate on the same state
+// That is to say, this is effectively a singleton despite the fact that HIDL
+// constructs a new instance for each client.
+std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
+wp<EvsGlDisplay> EvsEnumerator::sActiveDisplay;
+
+
+EvsEnumerator::EvsEnumerator() {
+ ALOGD("EvsEnumerator created");
+
+ unsigned videoCount = 0;
+ unsigned captureCount = 0;
+
+ // For every video* entry in the dev folder, see if it reports suitable capabilities
+ // WARNING: Depending on the driver implementations this could be slow, especially if
+ // there are timeouts or round trips to hardware required to collect the needed
+ // information. Platform implementers should consider hard coding this list of
+ // known good devices to speed up the startup time of their EVS implementation.
+ // For example, this code might be replaced with nothing more than:
+ // sCameraList.emplace_back("/dev/video0");
+ // sCameraList.emplace_back("/dev/video1");
+ ALOGI("Starting dev/video* enumeration");
+ DIR* dir = opendir("/dev");
+ if (!dir) {
+ LOG_FATAL("Failed to open /dev folder\n");
+ }
+ struct dirent* entry;
+ while ((entry = readdir(dir)) != nullptr) {
+ // We're only looking for entries starting with 'video'
+ if (strncmp(entry->d_name, "video", 5) == 0) {
+ std::string deviceName("/dev/");
+ deviceName += entry->d_name;
+ videoCount++;
+ if (qualifyCaptureDevice(deviceName.c_str())) {
+ sCameraList.emplace_back(deviceName.c_str());
+ captureCount++;
+ }
+ }
+ }
+
+ ALOGI("Found %d qualified video capture devices of %d checked\n", captureCount, videoCount);
+}
+
+
+// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
+Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
+ ALOGD("getCameraList");
+
+ const unsigned numCameras = sCameraList.size();
+
+ // Build up a packed array of CameraDesc for return
+ hidl_vec<CameraDesc> hidlCameras;
+ hidlCameras.resize(numCameras);
+ unsigned i = 0;
+ for (const auto& cam : sCameraList) {
+ hidlCameras[i++] = cam.desc;
+ }
+
+ // Send back the results
+ ALOGD("reporting %zu cameras available", hidlCameras.size());
+ _hidl_cb(hidlCameras);
+
+ // HIDL convention says we return Void if we sent our result back via callback
+ return Void();
+}
+
+
+Return<sp<IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
+ ALOGD("openCamera");
+
+ // Is this a recognized camera id?
+ CameraRecord *pRecord = findCameraById(cameraId);
+ if (!pRecord) {
+ ALOGE("Requested camera %s not found", cameraId.c_str());
+ return nullptr;
+ }
+
+ // Has this camera already been instantiated by another caller?
+ sp<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.promote();
+ if (pActiveCamera != nullptr) {
+ ALOGW("Killing previous camera because of new caller");
+ closeCamera(pActiveCamera);
+ }
+
+ // Construct a camera instance for the caller
+ pActiveCamera = new EvsV4lCamera(cameraId.c_str());
+ pRecord->activeInstance = pActiveCamera;
+ if (pActiveCamera == nullptr) {
+ ALOGE("Failed to allocate new EvsV4lCamera object for %s\n", cameraId.c_str());
+ }
+
+ return pActiveCamera;
+}
+
+
+Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& pCamera) {
+ ALOGD("closeCamera");
+
+ if (pCamera == nullptr) {
+ ALOGE("Ignoring call to closeCamera with null camera ptr");
+ return Void();
+ }
+
+ // Get the camera id so we can find it in our list
+ std::string cameraId;
+ pCamera->getCameraInfo([&cameraId](CameraDesc desc) {
+ cameraId = desc.cameraId;
+ }
+ );
+
+ // Find the named camera
+ CameraRecord *pRecord = findCameraById(cameraId);
+
+ // Is the display being destroyed actually the one we think is active?
+ if (!pRecord) {
+ ALOGE("Asked to close a camera whose name isn't recognized");
+ } else {
+ sp<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.promote();
+
+ if (pActiveCamera == nullptr) {
+ ALOGE("Somehow a camera is being destroyed when the enumerator didn't know one existed");
+ } else if (pActiveCamera != pCamera) {
+ // This can happen if the camera was aggressively reopened, orphaning this previous instance
+ ALOGW("Ignoring close of previously orphaned camera - why did a client steal?");
+ } else {
+ // Drop the active camera
+ pActiveCamera->shutdown();
+ pRecord->activeInstance = nullptr;
+ }
+ }
+
+ return Void();
+}
+
+
+Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
+ ALOGD("openDisplay");
+
+ // If we already have a display active, then we need to shut it down so we can
+ // give exclusive access to the new caller.
+ sp<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
+ if (pActiveDisplay != nullptr) {
+ ALOGW("Killing previous display because of new caller");
+ closeDisplay(pActiveDisplay);
+ }
+
+ // Create a new display interface and return it
+ pActiveDisplay = new EvsGlDisplay();
+ sActiveDisplay = pActiveDisplay;
+
+ ALOGD("Returning new EvsGlDisplay object %p", pActiveDisplay.get());
+ return pActiveDisplay;
+}
+
+
+Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) {
+ ALOGD("closeDisplay");
+
+ // Do we still have a display object we think should be active?
+ sp<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
+ if (pActiveDisplay == nullptr) {
+ ALOGE("Somehow a display is being destroyed when the enumerator didn't know one existed");
+ } else if (sActiveDisplay != pDisplay) {
+ ALOGW("Ignoring close of previously orphaned display - why did a client steal?");
+ } else {
+ // Drop the active display
+ pActiveDisplay->forceShutdown();
+ sActiveDisplay = nullptr;
+ }
+
+ return Void();
+}
+
+
+Return<DisplayState> EvsEnumerator::getDisplayState() {
+ ALOGD("getDisplayState");
+
+ // Do we still have a display object we think should be active?
+ sp<IEvsDisplay> pActiveDisplay = sActiveDisplay.promote();
+ if (pActiveDisplay != nullptr) {
+ return pActiveDisplay->getDisplayState();
+ } else {
+ return DisplayState::NOT_OPEN;
+ }
+}
+
+
+bool EvsEnumerator::qualifyCaptureDevice(const char* deviceName) {
+ class FileHandleWrapper {
+ public:
+ FileHandleWrapper(int fd) { mFd = fd; }
+ ~FileHandleWrapper() { if (mFd > 0) close(mFd); }
+ operator int() const { return mFd; }
+ private:
+ int mFd = -1;
+ };
+
+
+ FileHandleWrapper fd = open(deviceName, O_RDWR, 0);
+ if (fd < 0) {
+ return false;
+ }
+
+ v4l2_capability caps;
+ int result = ioctl(fd, VIDIOC_QUERYCAP, &caps);
+ if (result < 0) {
+ return false;
+ }
+ if (((caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) ||
+ ((caps.capabilities & V4L2_CAP_STREAMING) == 0)) {
+ return false;
+ }
+
+ // Enumerate the available capture formats (if any)
+ v4l2_fmtdesc formatDescription;
+ formatDescription.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ for (int i=0; true; i++) {
+ formatDescription.index = i;
+ if (ioctl(fd, VIDIOC_ENUM_FMT, &formatDescription) == 0) {
+ switch (formatDescription.pixelformat)
+ {
+ case V4L2_PIX_FMT_YUYV: return true;
+ case V4L2_PIX_FMT_NV21: return true;
+ case V4L2_PIX_FMT_NV16: return true;
+ case V4L2_PIX_FMT_YVU420: return true;
+ case V4L2_PIX_FMT_RGB32: return true;
+#ifdef V4L2_PIX_FMT_ARGB32 // introduced with kernel v3.17
+ case V4L2_PIX_FMT_ARGB32: return true;
+ case V4L2_PIX_FMT_XRGB32: return true;
+#endif // V4L2_PIX_FMT_ARGB32
+ default: break;
+ }
+ } else {
+ // No more formats available
+ break;
+ }
+ }
+
+ // If we get here, we didn't find a usable output format
+ return false;
+}
+
+
+EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
+ // Find the named camera
+ for (auto &&cam : sCameraList) {
+ if (cam.desc.cameraId == cameraId) {
+ // Found a match!
+ return &cam;
+ }
+ }
+
+ // We didn't find a match
+ return nullptr;
+}
+
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/evs/sampleDriver/EvsEnumerator.h b/evs/sampleDriver/EvsEnumerator.h
new file mode 100644
index 0000000..d8d7b36
--- /dev/null
+++ b/evs/sampleDriver/EvsEnumerator.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSCAMERAENUMERATOR_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSCAMERAENUMERATOR_H
+
+#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
+#include <android/hardware/automotive/evs/1.0/IEvsCamera.h>
+
+#include <list>
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_0 {
+namespace implementation {
+
+
+class EvsV4lCamera; // from EvsCamera.h
+class EvsGlDisplay; // from EvsGlDisplay.h
+
+
+class EvsEnumerator : public IEvsEnumerator {
+public:
+ // Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
+ Return<void> getCameraList(getCameraList_cb _hidl_cb) override;
+ Return<sp<IEvsCamera>> openCamera(const hidl_string& cameraId) override;
+ Return<void> closeCamera(const ::android::sp<IEvsCamera>& carCamera) override;
+ Return<sp<IEvsDisplay>> openDisplay() override;
+ Return<void> closeDisplay(const ::android::sp<IEvsDisplay>& display) override;
+ Return<DisplayState> getDisplayState() override;
+
+ // Implementation details
+ EvsEnumerator();
+
+private:
+ struct CameraRecord {
+ CameraDesc desc;
+ wp<EvsV4lCamera> activeInstance;
+
+ CameraRecord(const char *cameraId) : desc() { desc.cameraId = cameraId; }
+ };
+
+
+ static bool qualifyCaptureDevice(const char* deviceName);
+ static CameraRecord* findCameraById(const std::string& cameraId);
+
+
+ // NOTE: All members values are static so that all clients operate on the same state
+ // That is to say, this is effectively a singleton despite the fact that HIDL
+ // constructs a new instance for each client.
+ // Because our server has a single thread in the thread pool, these values are
+ // never accessed concurrently despite potentially having multiple instance objects
+ // using them.
+ static std::list<CameraRecord> sCameraList;
+
+ static wp<EvsGlDisplay> sActiveDisplay; // Weak pointer. Object destructs if client dies.
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSCAMERAENUMERATOR_H
diff --git a/evs/sampleDriver/EvsGlDisplay.cpp b/evs/sampleDriver/EvsGlDisplay.cpp
new file mode 100644
index 0000000..0f62e64
--- /dev/null
+++ b/evs/sampleDriver/EvsGlDisplay.cpp
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
+
+#include "EvsGlDisplay.h"
+
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_0 {
+namespace implementation {
+
+
+EvsGlDisplay::EvsGlDisplay() {
+ ALOGD("EvsGlDisplay instantiated");
+
+ // Set up our self description
+ // NOTE: These are arbitrary values chosen for testing
+ mInfo.displayId = "Mock Display";
+ mInfo.vendorFlags = 3870;
+}
+
+
+EvsGlDisplay::~EvsGlDisplay() {
+ ALOGD("EvsGlDisplay being destroyed");
+ forceShutdown();
+}
+
+
+/**
+ * This gets called if another caller "steals" ownership of the display
+ */
+void EvsGlDisplay::forceShutdown()
+{
+ ALOGD("EvsGlDisplay forceShutdown");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // If the buffer isn't being held by a remote client, release it now as an
+ // optimization to release the resources more quickly than the destructor might
+ // get called.
+ if (mBuffer.memHandle) {
+ // Report if we're going away while a buffer is outstanding
+ if (mFrameBusy) {
+ ALOGE("EvsGlDisplay going down while client is holding a buffer");
+ }
+
+ // Drop the graphics buffer we've been using
+ GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
+ alloc.free(mBuffer.memHandle);
+ mBuffer.memHandle = nullptr;
+
+ mGlWrapper.shutdown();
+ }
+
+ // Put this object into an unrecoverable error state since somebody else
+ // is going to own the display now.
+ mRequestedState = DisplayState::DEAD;
+}
+
+
+/**
+ * Returns basic information about the EVS display provided by the system.
+ * See the description of the DisplayDesc structure for details.
+ */
+Return<void> EvsGlDisplay::getDisplayInfo(getDisplayInfo_cb _hidl_cb) {
+ ALOGD("getDisplayInfo");
+
+ // Send back our self description
+ _hidl_cb(mInfo);
+ return Void();
+}
+
+
+/**
+ * Clients may set the display state to express their desired state.
+ * The HAL implementation must gracefully accept a request for any state
+ * while in any other state, although the response may be to ignore the request.
+ * The display is defined to start in the NOT_VISIBLE state upon initialization.
+ * The client is then expected to request the VISIBLE_ON_NEXT_FRAME state, and
+ * then begin providing video. When the display is no longer required, the client
+ * is expected to request the NOT_VISIBLE state after passing the last video frame.
+ */
+Return<EvsResult> EvsGlDisplay::setDisplayState(DisplayState state) {
+ ALOGD("setDisplayState");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mRequestedState == DisplayState::DEAD) {
+ // This object no longer owns the display -- it's been superceeded!
+ return EvsResult::OWNERSHIP_LOST;
+ }
+
+ // Ensure we recognize the requested state so we don't go off the rails
+ if (state >= DisplayState::NUM_STATES) {
+ return EvsResult::INVALID_ARG;
+ }
+
+ switch (state) {
+ case DisplayState::NOT_VISIBLE:
+ mGlWrapper.hideWindow();
+ break;
+ case DisplayState::VISIBLE:
+ mGlWrapper.showWindow();
+ break;
+ default:
+ break;
+ }
+
+ // Record the requested state
+ mRequestedState = state;
+
+ return EvsResult::OK;
+}
+
+
+/**
+ * The HAL implementation should report the actual current state, which might
+ * transiently differ from the most recently requested state. Note, however, that
+ * the logic responsible for changing display states should generally live above
+ * the device layer, making it undesirable for the HAL implementation to
+ * spontaneously change display states.
+ */
+Return<DisplayState> EvsGlDisplay::getDisplayState() {
+ ALOGD("getDisplayState");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ return mRequestedState;
+}
+
+
+/**
+ * This call returns a handle to a frame buffer associated with the display.
+ * This buffer may be locked and written to by software and/or GL. This buffer
+ * must be returned via a call to returnTargetBufferForDisplay() even if the
+ * display is no longer visible.
+ */
+Return<void> EvsGlDisplay::getTargetBuffer(getTargetBuffer_cb _hidl_cb) {
+ ALOGV("getTargetBuffer");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mRequestedState == DisplayState::DEAD) {
+ ALOGE("Rejecting buffer request from object that lost ownership of the display.");
+ BufferDesc nullBuff = {};
+ _hidl_cb(nullBuff);
+ return Void();
+ }
+
+ // If we don't already have a buffer, allocate one now
+ if (!mBuffer.memHandle) {
+ // Initialize our display window
+ // NOTE: This will cause the display to become "VISIBLE" before a frame is actually
+ // returned, which is contrary to the spec and will likely result in a black frame being
+ // (briefly) shown.
+ if (!mGlWrapper.initialize()) {
+ // Report the failure
+ ALOGE("Failed to initialize GL display");
+ BufferDesc nullBuff = {};
+ _hidl_cb(nullBuff);
+ return Void();
+ }
+
+ // Assemble the buffer description we'll use for our render target
+ mBuffer.width = mGlWrapper.getWidth();
+ mBuffer.height = mGlWrapper.getHeight();
+ mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
+ mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
+ mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition
+ mBuffer.pixelSize = 4;
+
+ // Allocate the buffer that will hold our displayable image
+ buffer_handle_t handle = nullptr;
+ GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
+ status_t result = alloc.allocate(mBuffer.width, mBuffer.height,
+ mBuffer.format, 1,
+ mBuffer.usage, &handle,
+ &mBuffer.stride,
+ 0, "EvsGlDisplay");
+ if (result != NO_ERROR) {
+ ALOGE("Error %d allocating %d x %d graphics buffer",
+ result, mBuffer.width, mBuffer.height);
+ BufferDesc nullBuff = {};
+ _hidl_cb(nullBuff);
+ mGlWrapper.shutdown();
+ return Void();
+ }
+ if (!handle) {
+ ALOGE("We didn't get a buffer handle back from the allocator");
+ BufferDesc nullBuff = {};
+ _hidl_cb(nullBuff);
+ mGlWrapper.shutdown();
+ return Void();
+ }
+
+ mBuffer.memHandle = handle;
+ ALOGD("Allocated new buffer %p with stride %u",
+ mBuffer.memHandle.getNativeHandle(), mBuffer.stride);
+ mFrameBusy = false;
+ }
+
+ // Do we have a frame available?
+ if (mFrameBusy) {
+ // This means either we have a 2nd client trying to compete for buffers
+ // (an unsupported mode of operation) or else the client hasn't returned
+ // a previously issued buffer yet (they're behaving badly).
+ // NOTE: We have to make the callback even if we have nothing to provide
+ ALOGE("getTargetBuffer called while no buffers available.");
+ BufferDesc nullBuff = {};
+ _hidl_cb(nullBuff);
+ return Void();
+ } else {
+ // Mark our buffer as busy
+ mFrameBusy = true;
+
+ // Send the buffer to the client
+ ALOGV("Providing display buffer handle %p as id %d",
+ mBuffer.memHandle.getNativeHandle(), mBuffer.bufferId);
+ _hidl_cb(mBuffer);
+ return Void();
+ }
+}
+
+
+/**
+ * This call tells the display that the buffer is ready for display.
+ * The buffer is no longer valid for use by the client after this call.
+ */
+Return<EvsResult> EvsGlDisplay::returnTargetBufferForDisplay(const BufferDesc& buffer) {
+ ALOGV("returnTargetBufferForDisplay %p", buffer.memHandle.getNativeHandle());
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // Nobody should call us with a null handle
+ if (!buffer.memHandle.getNativeHandle()) {
+ ALOGE ("returnTargetBufferForDisplay called without a valid buffer handle.\n");
+ return EvsResult::INVALID_ARG;
+ }
+ if (buffer.bufferId != mBuffer.bufferId) {
+ ALOGE ("Got an unrecognized frame returned.\n");
+ return EvsResult::INVALID_ARG;
+ }
+ if (!mFrameBusy) {
+ ALOGE ("A frame was returned with no outstanding frames.\n");
+ return EvsResult::BUFFER_NOT_AVAILABLE;
+ }
+
+ mFrameBusy = false;
+
+ // If we've been displaced by another owner of the display, then we can't do anything else
+ if (mRequestedState == DisplayState::DEAD) {
+ return EvsResult::OWNERSHIP_LOST;
+ }
+
+ // If we were waiting for a new frame, this is it!
+ if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) {
+ mRequestedState = DisplayState::VISIBLE;
+ mGlWrapper.showWindow();
+ }
+
+ // Validate we're in an expected state
+ if (mRequestedState != DisplayState::VISIBLE) {
+ // Not sure why a client would send frames back when we're not visible.
+ ALOGW ("Got a frame returned while not visible - ignoring.\n");
+ } else {
+ // Update the texture contents with the provided data
+// TODO: Why doesn't it work to pass in the buffer handle we got from HIDL?
+// if (!mGlWrapper.updateImageTexture(buffer)) {
+ if (!mGlWrapper.updateImageTexture(mBuffer)) {
+ return EvsResult::UNDERLYING_SERVICE_ERROR;
+ }
+
+ // Put the image on the screen
+ mGlWrapper.renderImageToScreen();
+ }
+
+ return EvsResult::OK;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/evs/sampleDriver/EvsGlDisplay.h b/evs/sampleDriver/EvsGlDisplay.h
new file mode 100644
index 0000000..7adbac9
--- /dev/null
+++ b/evs/sampleDriver/EvsGlDisplay.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSGLDISPLAY_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSGLDISPLAY_H
+
+#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
+#include <ui/GraphicBuffer.h>
+
+#include "GlWrapper.h"
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_0 {
+namespace implementation {
+
+
+class EvsGlDisplay : public IEvsDisplay {
+public:
+ // Methods from ::android::hardware::automotive::evs::V1_0::IEvsDisplay follow.
+ Return<void> getDisplayInfo(getDisplayInfo_cb _hidl_cb) override;
+ Return<EvsResult> setDisplayState(DisplayState state) override;
+ Return<DisplayState> getDisplayState() override;
+ Return<void> getTargetBuffer(getTargetBuffer_cb _hidl_cb) override;
+ Return<EvsResult> returnTargetBufferForDisplay(const BufferDesc& buffer) override;
+
+ // Implementation details
+ EvsGlDisplay();
+ virtual ~EvsGlDisplay() override;
+
+ void forceShutdown(); // This gets called if another caller "steals" ownership of the display
+
+private:
+ DisplayDesc mInfo = {};
+ BufferDesc mBuffer = {}; // A graphics buffer into which we'll store images
+
+ bool mFrameBusy = false; // A flag telling us our buffer is in use
+ DisplayState mRequestedState = DisplayState::NOT_VISIBLE;
+
+ GlWrapper mGlWrapper;
+
+ std::mutex mAccessLock;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSGLDISPLAY_H
diff --git a/evs/sampleDriver/EvsV4lCamera.cpp b/evs/sampleDriver/EvsV4lCamera.cpp
new file mode 100644
index 0000000..045d7ab
--- /dev/null
+++ b/evs/sampleDriver/EvsV4lCamera.cpp
@@ -0,0 +1,536 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
+
+#include "EvsV4lCamera.h"
+#include "EvsEnumerator.h"
+#include "bufferCopy.h"
+
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_0 {
+namespace implementation {
+
+
+// Arbitrary limit on number of graphics buffers allowed to be allocated
+// Safeguards against unreasonable resource consumption and provides a testable limit
+static const unsigned MAX_BUFFERS_IN_FLIGHT = 100;
+
+
+EvsV4lCamera::EvsV4lCamera(const char *deviceName) :
+ mFramesAllowed(0),
+ mFramesInUse(0) {
+ ALOGD("EvsV4lCamera instantiated");
+
+ mDescription.cameraId = deviceName;
+
+ // Initialize the video device
+ if (!mVideo.open(deviceName)) {
+ ALOGE("Failed to open v4l device %s\n", deviceName);
+ }
+
+ // NOTE: Our current spec says only support NV21 -- can we stick to that with software
+ // conversion? Will this work with the hardware texture units?
+ // TODO: Settle on the one official format that works on all platforms
+ // TODO: Get NV21 working? It is scrambled somewhere along the way right now.
+// mFormat = HAL_PIXEL_FORMAT_YCRCB_420_SP; // 420SP == NV21
+// mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+ mFormat = HAL_PIXEL_FORMAT_YCBCR_422_I;
+
+ // How we expect to use the gralloc buffers we'll exchange with our client
+ mUsage = GRALLOC_USAGE_HW_TEXTURE |
+ GRALLOC_USAGE_SW_READ_RARELY |
+ GRALLOC_USAGE_SW_WRITE_OFTEN;
+}
+
+
+EvsV4lCamera::~EvsV4lCamera() {
+ ALOGD("EvsV4lCamera being destroyed");
+ shutdown();
+}
+
+
+//
+// This gets called if another caller "steals" ownership of the camera
+//
+void EvsV4lCamera::shutdown()
+{
+ ALOGD("EvsV4lCamera shutdown");
+
+ // Make sure our output stream is cleaned up
+ // (It really should be already)
+ stopVideoStream();
+
+ // Note: Since stopVideoStream is blocking, no other threads can now be running
+
+ // Close our video capture device
+ mVideo.close();
+
+ // Drop all the graphics buffers we've been using
+ if (mBuffers.size() > 0) {
+ GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
+ for (auto&& rec : mBuffers) {
+ if (rec.inUse) {
+ ALOGW("Error - releasing buffer despite remote ownership");
+ }
+ alloc.free(rec.handle);
+ rec.handle = nullptr;
+ }
+ mBuffers.clear();
+ }
+}
+
+
+// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
+Return<void> EvsV4lCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) {
+ ALOGD("getCameraInfo");
+
+ // Send back our self description
+ _hidl_cb(mDescription);
+ return Void();
+}
+
+
+Return<EvsResult> EvsV4lCamera::setMaxFramesInFlight(uint32_t bufferCount) {
+ ALOGD("setMaxFramesInFlight");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (!mVideo.isOpen()) {
+ ALOGW("ignoring setMaxFramesInFlight call when camera has been lost.");
+ return EvsResult::OWNERSHIP_LOST;
+ }
+
+ // We cannot function without at least one video buffer to send data
+ if (bufferCount < 1) {
+ ALOGE("Ignoring setMaxFramesInFlight with less than one buffer requested");
+ return EvsResult::INVALID_ARG;
+ }
+
+ // Update our internal state
+ if (setAvailableFrames_Locked(bufferCount)) {
+ return EvsResult::OK;
+ } else {
+ return EvsResult::BUFFER_NOT_AVAILABLE;
+ }
+}
+
+
+Return<EvsResult> EvsV4lCamera::startVideoStream(const ::android::sp<IEvsCameraStream>& stream) {
+ ALOGD("startVideoStream");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (!mVideo.isOpen()) {
+ ALOGW("ignoring startVideoStream call when camera has been lost.");
+ return EvsResult::OWNERSHIP_LOST;
+ }
+ if (mStream.get() != nullptr) {
+ ALOGE("ignoring startVideoStream call when a stream is already running.");
+ return EvsResult::STREAM_ALREADY_RUNNING;
+ }
+
+ // If the client never indicated otherwise, configure ourselves for a single streaming buffer
+ if (mFramesAllowed < 1) {
+ if (!setAvailableFrames_Locked(1)) {
+ ALOGE("Failed to start stream because we couldn't get a graphics buffer");
+ return EvsResult::BUFFER_NOT_AVAILABLE;
+ }
+ }
+
+ // Choose which image transfer function we need
+ // Map from V4L2 to Android graphic buffer format
+ const uint32_t videoSrcFormat = mVideo.getV4LFormat();
+ ALOGI("Configuring to accept %4.4s camera data and convert to %4.4s",
+ (char*)&videoSrcFormat, (char*)&mFormat);
+
+ // TODO: Simplify this by supporting only ONE fixed output format
+ switch (mFormat) {
+ case HAL_PIXEL_FORMAT_YCRCB_420_SP:
+ switch (videoSrcFormat) {
+ case V4L2_PIX_FMT_NV21: mFillBufferFromVideo = fillNV21FromNV21; break;
+ // case V4L2_PIX_FMT_YV12: mFillBufferFromVideo = fillNV21FromYV12; break;
+ case V4L2_PIX_FMT_YUYV: mFillBufferFromVideo = fillNV21FromYUYV; break;
+ // case V4L2_PIX_FORMAT_NV16: mFillBufferFromVideo = fillNV21FromNV16; break;
+ default:
+ // TODO: Are there other V4L2 formats we must support?
+ ALOGE("Unhandled camera output format %c%c%c%c (0x%8X)\n",
+ ((char*)&videoSrcFormat)[0],
+ ((char*)&videoSrcFormat)[1],
+ ((char*)&videoSrcFormat)[2],
+ ((char*)&videoSrcFormat)[3],
+ videoSrcFormat);
+ }
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ switch (videoSrcFormat) {
+ case V4L2_PIX_FMT_YUYV: mFillBufferFromVideo = fillRGBAFromYUYV; break;
+ default:
+ // TODO: Are there other V4L2 formats we must support?
+ ALOGE("Unhandled camera format %4.4s", (char*)&videoSrcFormat);
+ }
+ break;
+ case HAL_PIXEL_FORMAT_YCBCR_422_I:
+ switch (videoSrcFormat) {
+ case V4L2_PIX_FMT_YUYV: mFillBufferFromVideo = fillYUYVFromYUYV; break;
+ case V4L2_PIX_FMT_UYVY: mFillBufferFromVideo = fillYUYVFromUYVY; break;
+ default:
+ // TODO: Are there other V4L2 formats we must support?
+ ALOGE("Unhandled camera format %4.4s", (char*)&videoSrcFormat);
+ }
+ break;
+ default:
+ // TODO: Why have we told ourselves to output something we don't understand!?
+ ALOGE("Unhandled output format %4.4s", (char*)&mFormat);
+ }
+
+
+ // Record the user's callback for use when we have a frame ready
+ mStream = stream;
+
+ // Set up the video stream with a callback to our member function forwardFrame()
+ if (!mVideo.startStream([this](VideoCapture*, imageBuffer* tgt, void* data) {
+ this->forwardFrame(tgt, data);
+ })
+ ) {
+ mStream = nullptr; // No need to hold onto this if we failed to start
+ ALOGE("underlying camera start stream failed");
+ return EvsResult::UNDERLYING_SERVICE_ERROR;
+ }
+
+ return EvsResult::OK;
+}
+
+
+Return<void> EvsV4lCamera::doneWithFrame(const BufferDesc& buffer) {
+ ALOGD("doneWithFrame");
+ std::lock_guard <std::mutex> lock(mAccessLock);
+
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (!mVideo.isOpen()) {
+ ALOGW("ignoring doneWithFrame call when camera has been lost.");
+ } else {
+ if (buffer.memHandle == nullptr) {
+ ALOGE("ignoring doneWithFrame called with null handle");
+ } else if (buffer.bufferId >= mBuffers.size()) {
+ ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)",
+ buffer.bufferId, mBuffers.size()-1);
+ } else if (!mBuffers[buffer.bufferId].inUse) {
+ ALOGE("ignoring doneWithFrame called on frame %d which is already free",
+ buffer.bufferId);
+ } else {
+ // Mark the frame as available
+ mBuffers[buffer.bufferId].inUse = false;
+ mFramesInUse--;
+
+ // If this frame's index is high in the array, try to move it down
+ // to improve locality after mFramesAllowed has been reduced.
+ if (buffer.bufferId >= mFramesAllowed) {
+ // Find an empty slot lower in the array (which should always exist in this case)
+ for (auto&& rec : mBuffers) {
+ if (rec.handle == nullptr) {
+ rec.handle = mBuffers[buffer.bufferId].handle;
+ mBuffers[buffer.bufferId].handle = nullptr;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return Void();
+}
+
+
+Return<void> EvsV4lCamera::stopVideoStream() {
+ ALOGD("stopVideoStream");
+
+ // Tell the capture device to stop (and block until it does)
+ mVideo.stopStream();
+
+ if (mStream != nullptr) {
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ // Send one last NULL frame to signal the actual end of stream
+ BufferDesc nullBuff = {};
+ auto result = mStream->deliverFrame(nullBuff);
+ if (!result.isOk()) {
+ ALOGE("Error delivering end of stream marker");
+ }
+
+ // Drop our reference to the client's stream receiver
+ mStream = nullptr;
+ }
+
+ return Void();
+}
+
+
+Return<int32_t> EvsV4lCamera::getExtendedInfo(uint32_t /*opaqueIdentifier*/) {
+ ALOGD("getExtendedInfo");
+ // Return zero by default as required by the spec
+ return 0;
+}
+
+
+Return<EvsResult> EvsV4lCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/,
+ int32_t /*opaqueValue*/) {
+ ALOGD("setExtendedInfo");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (!mVideo.isOpen()) {
+ ALOGW("ignoring setExtendedInfo call when camera has been lost.");
+ return EvsResult::OWNERSHIP_LOST;
+ }
+
+ // We don't store any device specific information in this implementation
+ return EvsResult::INVALID_ARG;
+}
+
+
+bool EvsV4lCamera::setAvailableFrames_Locked(unsigned bufferCount) {
+ if (bufferCount < 1) {
+ ALOGE("Ignoring request to set buffer count to zero");
+ return false;
+ }
+ if (bufferCount > MAX_BUFFERS_IN_FLIGHT) {
+ ALOGE("Rejecting buffer request in excess of internal limit");
+ return false;
+ }
+
+ // Is an increase required?
+ if (mFramesAllowed < bufferCount) {
+ // An increase is required
+ unsigned needed = bufferCount - mFramesAllowed;
+ ALOGI("Allocating %d buffers for camera frames", needed);
+
+ unsigned added = increaseAvailableFrames_Locked(needed);
+ if (added != needed) {
+ // If we didn't add all the frames we needed, then roll back to the previous state
+ ALOGE("Rolling back to previous frame queue size");
+ decreaseAvailableFrames_Locked(added);
+ return false;
+ }
+ } else if (mFramesAllowed > bufferCount) {
+ // A decrease is required
+ unsigned framesToRelease = mFramesAllowed - bufferCount;
+ ALOGI("Returning %d camera frame buffers", framesToRelease);
+
+ unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
+ if (released != framesToRelease) {
+ // This shouldn't happen with a properly behaving client because the client
+ // should only make this call after returning sufficient outstanding buffers
+ // to allow a clean resize.
+ ALOGE("Buffer queue shrink failed -- too many buffers currently in use?");
+ }
+ }
+
+ return true;
+}
+
+
+unsigned EvsV4lCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
+ // Acquire the graphics buffer allocator
+ GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
+
+ unsigned added = 0;
+
+
+ while (added < numToAdd) {
+ unsigned pixelsPerLine;
+ buffer_handle_t memHandle = nullptr;
+ status_t result = alloc.allocate(mVideo.getWidth(), mVideo.getHeight(),
+ mFormat, 1,
+ mUsage,
+ &memHandle, &pixelsPerLine, 0, "EvsV4lCamera");
+ if (result != NO_ERROR) {
+ ALOGE("Error %d allocating %d x %d graphics buffer",
+ result,
+ mVideo.getWidth(),
+ mVideo.getHeight());
+ break;
+ }
+ if (!memHandle) {
+ ALOGE("We didn't get a buffer handle back from the allocator");
+ break;
+ }
+ if (mStride) {
+ if (mStride != pixelsPerLine) {
+ ALOGE("We did not expect to get buffers with different strides!");
+ }
+ } else {
+ // Gralloc defines stride in terms of pixels per line
+ mStride = pixelsPerLine;
+ }
+
+ // Find a place to store the new buffer
+ bool stored = false;
+ for (auto&& rec : mBuffers) {
+ if (rec.handle == nullptr) {
+ // Use this existing entry
+ rec.handle = memHandle;
+ rec.inUse = false;
+ stored = true;
+ break;
+ }
+ }
+ if (!stored) {
+ // Add a BufferRecord wrapping this handle to our set of available buffers
+ mBuffers.emplace_back(memHandle);
+ }
+
+ mFramesAllowed++;
+ added++;
+ }
+
+ return added;
+}
+
+
+unsigned EvsV4lCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
+ // Acquire the graphics buffer allocator
+ GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
+
+ unsigned removed = 0;
+
+ for (auto&& rec : mBuffers) {
+ // Is this record not in use, but holding a buffer that we can free?
+ if ((rec.inUse == false) && (rec.handle != nullptr)) {
+ // Release buffer and update the record so we can recognize it as "empty"
+ alloc.free(rec.handle);
+ rec.handle = nullptr;
+
+ mFramesAllowed--;
+ removed++;
+
+ if (removed == numToRemove) {
+ break;
+ }
+ }
+ }
+
+ return removed;
+}
+
+
+// This is the async callback from the video camera that tells us a frame is ready
+void EvsV4lCamera::forwardFrame(imageBuffer* /*pV4lBuff*/, void* pData) {
+ bool readyForFrame = false;
+ size_t idx = 0;
+
+ // Lock scope for updating shared state
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // Are we allowed to issue another buffer?
+ if (mFramesInUse >= mFramesAllowed) {
+ // Can't do anything right now -- skip this frame
+ ALOGW("Skipped a frame because too many are in flight\n");
+ } else {
+ // Identify an available buffer to fill
+ for (idx = 0; idx < mBuffers.size(); idx++) {
+ if (!mBuffers[idx].inUse) {
+ if (mBuffers[idx].handle != nullptr) {
+ // Found an available record, so stop looking
+ break;
+ }
+ }
+ }
+ if (idx >= mBuffers.size()) {
+ // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
+ ALOGE("Failed to find an available buffer slot\n");
+ } else {
+ // We're going to make the frame busy
+ mBuffers[idx].inUse = true;
+ mFramesInUse++;
+ readyForFrame = true;
+ }
+ }
+ }
+
+ if (!readyForFrame) {
+ // We need to return the vide buffer so it can capture a new frame
+ mVideo.markFrameConsumed();
+ } else {
+ // Assemble the buffer description we'll transmit below
+ BufferDesc buff = {};
+ buff.width = mVideo.getWidth();
+ buff.height = mVideo.getHeight();
+ buff.stride = mStride;
+ buff.format = mFormat;
+ buff.usage = mUsage;
+ buff.bufferId = idx;
+ buff.memHandle = mBuffers[idx].handle;
+
+ // Lock our output buffer for writing
+ void *targetPixels = nullptr;
+ GraphicBufferMapper &mapper = GraphicBufferMapper::get();
+ mapper.lock(buff.memHandle,
+ GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
+ android::Rect(buff.width, buff.height),
+ (void **) &targetPixels);
+
+ // If we failed to lock the pixel buffer, we're about to crash, but log it first
+ if (!targetPixels) {
+ ALOGE("Camera failed to gain access to image buffer for writing");
+ }
+
+ // Transfer the video image into the output buffer, making any needed
+ // format conversion along the way
+ mFillBufferFromVideo(buff, (uint8_t*)targetPixels, pData, mVideo.getStride());
+
+ // Unlock the output buffer
+ mapper.unlock(buff.memHandle);
+
+
+ // Give the video frame back to the underlying device for reuse
+ // Note that we do this before making the client callback to give the underlying
+ // camera more time to capture the next frame.
+ mVideo.markFrameConsumed();
+
+ // Issue the (asynchronous) callback to the client -- can't be holding the lock
+ auto result = mStream->deliverFrame(buff);
+ if (result.isOk()) {
+ ALOGD("Delivered %p as id %d", buff.memHandle.getNativeHandle(), buff.bufferId);
+ } else {
+ // This can happen if the client dies and is likely unrecoverable.
+ // To avoid consuming resources generating failing calls, we stop sending
+ // frames. Note, however, that the stream remains in the "STREAMING" state
+ // until cleaned up on the main thread.
+ ALOGE("Frame delivery call failed in the transport layer.");
+
+ // Since we didn't actually deliver it, mark the frame as available
+ std::lock_guard<std::mutex> lock(mAccessLock);
+ mBuffers[idx].inUse = false;
+ mFramesInUse--;
+ }
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/evs/sampleDriver/EvsV4lCamera.h b/evs/sampleDriver/EvsV4lCamera.h
new file mode 100644
index 0000000..3d351b9
--- /dev/null
+++ b/evs/sampleDriver/EvsV4lCamera.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSV4LCAMERA_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSV4LCAMERA_H
+
+#include <android/hardware/automotive/evs/1.0/types.h>
+#include <android/hardware/automotive/evs/1.0/IEvsCamera.h>
+#include <ui/GraphicBuffer.h>
+
+#include <thread>
+#include <functional>
+
+#include "VideoCapture.h"
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_0 {
+namespace implementation {
+
+
+// From EvsEnumerator.h
+class EvsEnumerator;
+
+
+class EvsV4lCamera : public IEvsCamera {
+public:
+ // Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
+ Return<void> getCameraInfo(getCameraInfo_cb _hidl_cb) override;
+ Return <EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
+ Return <EvsResult> startVideoStream(const ::android::sp<IEvsCameraStream>& stream) override;
+ Return<void> doneWithFrame(const BufferDesc& buffer) override;
+ Return<void> stopVideoStream() override;
+ Return <int32_t> getExtendedInfo(uint32_t opaqueIdentifier) override;
+ Return <EvsResult> setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) override;
+
+ // Implementation details
+ EvsV4lCamera(const char *deviceName);
+ virtual ~EvsV4lCamera() override;
+ void shutdown();
+
+ const CameraDesc& getDesc() { return mDescription; };
+
+private:
+ // These three functions are expected to be called while mAccessLock is held
+ bool setAvailableFrames_Locked(unsigned bufferCount);
+ unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
+ unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
+
+ void forwardFrame(imageBuffer* tgt, void* data);
+
+ sp <IEvsCameraStream> mStream = nullptr; // The callback used to deliver each frame
+
+ VideoCapture mVideo; // Interface to the v4l device
+
+ CameraDesc mDescription = {}; // The properties of this camera
+ uint32_t mFormat = 0; // Values from android_pixel_format_t
+ uint32_t mUsage = 0; // Values from from Gralloc.h
+ uint32_t mStride = 0; // Pixels per row (may be greater than image width)
+
+ struct BufferRecord {
+ buffer_handle_t handle;
+ bool inUse;
+
+ explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false) {};
+ };
+
+ std::vector <BufferRecord> mBuffers; // Graphics buffers to transfer images
+ unsigned mFramesAllowed; // How many buffers are we currently using
+ unsigned mFramesInUse; // How many buffers are currently outstanding
+
+ // Which format specific function we need to use to move camera imagery into our output buffers
+ void(*mFillBufferFromVideo)(const BufferDesc& tgtBuff, uint8_t* tgt,
+ void* imgData, unsigned imgStride);
+
+ // Synchronization necessary to deconflict the capture thread from the main service thread
+ // Note that the service interface remains single threaded (ie: not reentrant)
+ std::mutex mAccessLock;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_EVSV4LCAMERA_H
diff --git a/evs/sampleDriver/GlWrapper.cpp b/evs/sampleDriver/GlWrapper.cpp
new file mode 100644
index 0000000..a49eb20
--- /dev/null
+++ b/evs/sampleDriver/GlWrapper.cpp
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
+
+#include "GlWrapper.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <ui/DisplayInfo.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+
+using namespace android;
+
+
+// TODO: Consider dropping direct use of GraphicsBufferAllocator and Mapper?
+using android::GraphicBuffer;
+using android::GraphicBufferAllocator;
+using android::GraphicBufferMapper;
+using android::sp;
+
+
+const char vertexShaderSource[] = ""
+ "#version 300 es \n"
+ "layout(location = 0) in vec4 pos; \n"
+ "layout(location = 1) in vec2 tex; \n"
+ "out vec2 uv; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = pos; \n"
+ " uv = tex; \n"
+ "} \n";
+
+const char pixelShaderSource[] =
+ "#version 300 es \n"
+ "precision mediump float; \n"
+ "uniform sampler2D tex; \n"
+ "in vec2 uv; \n"
+ "out vec4 color; \n"
+ "void main() \n"
+ "{ \n"
+ " vec4 texel = texture(tex, uv); \n"
+ " color = texel; \n"
+ "} \n";
+
+
+static const char *getEGLError(void) {
+ switch (eglGetError()) {
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "Unknown error";
+ }
+}
+
+
+// Given shader source, load and compile it
+static GLuint loadShader(GLenum type, const char *shaderSrc) {
+ // Create the shader object
+ GLuint shader = glCreateShader (type);
+ if (shader == 0) {
+ return 0;
+ }
+
+ // Load and compile the shader
+ glShaderSource(shader, 1, &shaderSrc, nullptr);
+ glCompileShader(shader);
+
+ // Verify the compilation worked as expected
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ ALOGE("Error compiling shader\n");
+
+ GLint size = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
+ if (size > 0)
+ {
+ // Get and report the error message
+ char *infoLog = (char*)malloc(size);
+ glGetShaderInfoLog(shader, size, nullptr, infoLog);
+ ALOGE(" msg:\n%s\n", infoLog);
+ free(infoLog);
+ }
+
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+}
+
+
+// Create a program object given vertex and pixels shader source
+static GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
+ GLuint program = glCreateProgram();
+ if (program == 0) {
+ ALOGE("Failed to allocate program object\n");
+ return 0;
+ }
+
+ // Compile the shaders and bind them to this program
+ GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
+ if (vertexShader == 0) {
+ ALOGE("Failed to load vertex shader\n");
+ glDeleteProgram(program);
+ return 0;
+ }
+ GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
+ if (pixelShader == 0) {
+ ALOGE("Failed to load pixel shader\n");
+ glDeleteProgram(program);
+ glDeleteShader(vertexShader);
+ return 0;
+ }
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, pixelShader);
+
+ // Link the program
+ glLinkProgram(program);
+ GLint linked = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ if (!linked)
+ {
+ ALOGE("Error linking program.\n");
+ GLint size = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
+ if (size > 0)
+ {
+ // Get and report the error message
+ char *infoLog = (char*)malloc(size);
+ glGetProgramInfoLog(program, size, nullptr, infoLog);
+ ALOGE(" msg: %s\n", infoLog);
+ free(infoLog);
+ }
+
+ glDeleteProgram(program);
+ glDeleteShader(vertexShader);
+ glDeleteShader(pixelShader);
+ return 0;
+ }
+
+ return program;
+}
+
+
+// Main entry point
+bool GlWrapper::initialize() {
+ //
+ // Create the native full screen window and get a suitable configuration to match it
+ //
+ status_t err;
+
+ mFlinger = new SurfaceComposerClient();
+ if (mFlinger == nullptr) {
+ ALOGE("SurfaceComposerClient couldn't be allocated");
+ return false;
+ }
+ err = mFlinger->initCheck();
+ if (err != NO_ERROR) {
+ ALOGE("SurfaceComposerClient::initCheck error: %#x", err);
+ return false;
+ }
+
+ // Get main display parameters.
+ sp <IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain);
+ DisplayInfo mainDpyInfo;
+ err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+ if (err != NO_ERROR) {
+ ALOGE("ERROR: unable to get display characteristics");
+ return false;
+ }
+
+ if (mainDpyInfo.orientation != DISPLAY_ORIENTATION_0 &&
+ mainDpyInfo.orientation != DISPLAY_ORIENTATION_180) {
+ // rotated
+ mWidth = mainDpyInfo.h;
+ mHeight = mainDpyInfo.w;
+ } else {
+ mWidth = mainDpyInfo.w;
+ mHeight = mainDpyInfo.h;
+ }
+
+ mFlingerSurfaceControl = mFlinger->createSurface(
+ String8("Evs Display"), mWidth, mHeight,
+ PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
+ if (mFlingerSurfaceControl == nullptr || !mFlingerSurfaceControl->isValid()) {
+ ALOGE("Failed to create SurfaceControl");
+ return false;
+ }
+ mFlingerSurface = mFlingerSurfaceControl->getSurface();
+
+
+ // Set up our OpenGL ES context associated with the default display
+ mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mDisplay == EGL_NO_DISPLAY) {
+ ALOGE("Failed to get egl display");
+ return false;
+ }
+
+ EGLint major = 3;
+ EGLint minor = 0;
+ if (!eglInitialize(mDisplay, &major, &minor)) {
+ ALOGE("Failed to initialize EGL: %s", getEGLError());
+ return false;
+ }
+
+
+ const EGLint config_attribs[] = {
+ // Tag Value
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_NONE
+ };
+
+ // Pick the default configuration without constraints (is this good enough?)
+ EGLConfig egl_config = {0};
+ EGLint numConfigs = -1;
+ eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
+ if (numConfigs != 1) {
+ ALOGE("Didn't find a suitable format for our display window");
+ return false;
+ }
+
+ // Create the EGL render target surface
+ mSurface = eglCreateWindowSurface(mDisplay, egl_config, mFlingerSurface.get(), nullptr);
+ if (mSurface == EGL_NO_SURFACE) {
+ ALOGE("gelCreateWindowSurface failed.");
+ return false;
+ }
+
+ // Create the EGL context
+ // NOTE: Our shader is (currently at least) written to require version 3, so this
+ // is required.
+ const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+ mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
+ if (mContext == EGL_NO_CONTEXT) {
+ ALOGE("Failed to create OpenGL ES Context: %s", getEGLError());
+ return false;
+ }
+
+
+ // Activate our render target for drawing
+ if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
+ ALOGE("Failed to make the OpenGL ES Context current: %s", getEGLError());
+ return false;
+ }
+
+
+ // Create the shader program for our simple pipeline
+ mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
+ if (!mShaderProgram) {
+ ALOGE("Failed to build shader program: %s", getEGLError());
+ return false;
+ }
+
+ // Create a GL texture that will eventually wrap our externally created texture surface(s)
+ glGenTextures(1, &mTextureMap);
+ if (mTextureMap <= 0) {
+ ALOGE("Didn't get a texture handle allocated: %s", getEGLError());
+ return false;
+ }
+
+
+ return true;
+}
+
+
+void GlWrapper::shutdown() {
+
+ // Drop our device textures
+ if (mKHRimage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(mDisplay, mKHRimage);
+ mKHRimage = EGL_NO_IMAGE_KHR;
+ }
+
+ // Release all GL resources
+ eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroySurface(mDisplay, mSurface);
+ eglDestroyContext(mDisplay, mContext);
+ eglTerminate(mDisplay);
+ mSurface = EGL_NO_SURFACE;
+ mContext = EGL_NO_CONTEXT;
+ mDisplay = EGL_NO_DISPLAY;
+
+ // Let go of our SurfaceComposer resources
+ mFlingerSurface.clear();
+ mFlingerSurfaceControl.clear();
+ mFlinger.clear();
+}
+
+
+void GlWrapper::showWindow() {
+ if (mFlingerSurfaceControl != nullptr) {
+ SurfaceComposerClient::openGlobalTransaction();
+ mFlingerSurfaceControl->setLayer(0x7FFFFFFF); // always on top
+ mFlingerSurfaceControl->show();
+ SurfaceComposerClient::closeGlobalTransaction();
+ }
+}
+
+
+void GlWrapper::hideWindow() {
+ if (mFlingerSurfaceControl != nullptr) {
+ SurfaceComposerClient::openGlobalTransaction();
+ mFlingerSurfaceControl->hide();
+ SurfaceComposerClient::closeGlobalTransaction();
+ }
+}
+
+
+bool GlWrapper::updateImageTexture(const BufferDesc& buffer) {
+
+ // If we haven't done it yet, create an "image" object to wrap the gralloc buffer
+ if (mKHRimage == EGL_NO_IMAGE_KHR) {
+ // create a temporary GraphicBuffer to wrap the provided handle
+ sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(
+ buffer.width,
+ buffer.height,
+ buffer.format,
+ 1, /* layer count */
+ buffer.usage,
+ buffer.stride,
+ const_cast<native_handle_t*>(buffer.memHandle.getNativeHandle()),
+ false /* keep ownership */
+ );
+ if (pGfxBuffer.get() == nullptr) {
+ ALOGE("Failed to allocate GraphicsBuffer to wrap our native handle");
+ return false;
+ }
+
+
+ // Get a GL compatible reference to the graphics buffer we've been given
+ EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+ EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
+// TODO: If we pass in a context, we get "bad context" back
+#if 0
+ mKHRimage = eglCreateImageKHR(mDisplay, mContext,
+ EGL_NATIVE_BUFFER_ANDROID, cbuf,
+ eglImageAttributes);
+#else
+ mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, cbuf,
+ eglImageAttributes);
+#endif
+ if (mKHRimage == EGL_NO_IMAGE_KHR) {
+ ALOGE("error creating EGLImage: %s", getEGLError());
+ return false;
+ }
+
+
+ // Update the texture handle we already created to refer to this gralloc buffer
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTextureMap);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
+
+ }
+
+ return true;
+}
+
+
+void GlWrapper::renderImageToScreen() {
+ // Set the viewport
+ glViewport(0, 0, mWidth, mHeight);
+
+ // Clear the color buffer
+ glClearColor(0.1f, 0.5f, 0.1f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Select our screen space simple texture shader
+ glUseProgram(mShaderProgram);
+
+ // Bind the texture and assign it to the shader's sampler
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mTextureMap);
+ GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
+ glUniform1i(sampler, 0);
+
+ // We want our image to show up opaque regardless of alpha values
+ glDisable(GL_BLEND);
+
+
+ // Draw a rectangle on the screen
+ // TODO: We pulled in from the edges for now for diagnostic purposes...
+#if 0
+ GLfloat vertsCarPos[] = { -1.0, 1.0, 0.0f, // left top in window space
+ 1.0, 1.0, 0.0f, // right top
+ -1.0, -1.0, 0.0f, // left bottom
+ 1.0, -1.0, 0.0f // right bottom
+ };
+#else
+ GLfloat vertsCarPos[] = { -0.8, 0.8, 0.0f, // left top in window space
+ 0.8, 0.8, 0.0f, // right top
+ -0.8, -0.8, 0.0f, // left bottom
+ 0.8, -0.8, 0.0f // right bottom
+ };
+#endif
+ // NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image
+ GLfloat vertsCarTex[] = { 0.0f, 0.0f, // left top
+ 1.0f, 0.0f, // right top
+ 0.0f, 1.0f, // left bottom
+ 1.0f, 1.0f // right bottom
+ };
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+
+ // Clean up and flip the rendered result to the front so it is visible
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+
+ glFinish();
+
+ eglSwapBuffers(mDisplay, mSurface);
+}
+
diff --git a/evs/sampleDriver/GlWrapper.h b/evs/sampleDriver/GlWrapper.h
new file mode 100644
index 0000000..07b5525
--- /dev/null
+++ b/evs/sampleDriver/GlWrapper.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_DISPLAY_GLWRAPPER_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_DISPLAY_GLWRAPPER_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <android/hardware/automotive/evs/1.0/types.h>
+
+
+using ::android::sp;
+using ::android::SurfaceComposerClient;
+using ::android::SurfaceControl;
+using ::android::Surface;
+using ::android::hardware::automotive::evs::V1_0::BufferDesc;
+
+
+class GlWrapper {
+public:
+ bool initialize();
+ void shutdown();
+
+ bool updateImageTexture(const BufferDesc& buffer);
+ void renderImageToScreen();
+
+ void showWindow();
+ void hideWindow();
+
+ unsigned getWidth() { return mWidth; };
+ unsigned getHeight() { return mHeight; };
+
+private:
+ sp<SurfaceComposerClient> mFlinger;
+ sp<SurfaceControl> mFlingerSurfaceControl;
+ sp<Surface> mFlingerSurface;
+ EGLDisplay mDisplay;
+ EGLSurface mSurface;
+ EGLContext mContext;
+
+ unsigned mWidth = 0;
+ unsigned mHeight = 0;
+
+ EGLImageKHR mKHRimage = EGL_NO_IMAGE_KHR;
+
+ GLuint mTextureMap = 0;
+ GLuint mShaderProgram = 0;
+};
+
+#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_DISPLAY_GLWRAPPER_H
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java b/evs/sampleDriver/ServiceNames.h
similarity index 65%
rename from tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
rename to evs/sampleDriver/ServiceNames.h
index 94abeb1..1178da5 100644
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarProxyActivity.java
+++ b/evs/sampleDriver/ServiceNames.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,13 +14,4 @@
* limitations under the License.
*/
-package com.android.support.car.apitest;
-
-import android.support.car.app.CarProxyActivity;
-
-public class TestCarProxyActivity extends CarProxyActivity {
-
- public TestCarProxyActivity() {
- super(TestCarActivity.class);
- }
-}
+const static char kEnumeratorServiceName[] = "EvsEnumeratorHw";
diff --git a/evs/sampleDriver/VideoCapture.cpp b/evs/sampleDriver/VideoCapture.cpp
new file mode 100644
index 0000000..2122a2c
--- /dev/null
+++ b/evs/sampleDriver/VideoCapture.cpp
@@ -0,0 +1,305 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <errno.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <cutils/log.h>
+
+#include "assert.h"
+
+#include "VideoCapture.h"
+
+
+// NOTE: This developmental code does not properly clean up resources in case of failure
+// during the resource setup phase. Of particular note is the potential to leak
+// the file descriptor. This must be fixed before using this code for anything but
+// experimentation.
+bool VideoCapture::open(const char* deviceName) {
+ // If we want a polling interface for getting frames, we would use O_NONBLOCK
+// int mDeviceFd = open(deviceName, O_RDWR | O_NONBLOCK, 0);
+ mDeviceFd = ::open(deviceName, O_RDWR, 0);
+ if (mDeviceFd < 0) {
+ ALOGE("failed to open device %s (%d = %s)", deviceName, errno, strerror(errno));
+ return false;
+ }
+
+ v4l2_capability caps;
+ {
+ int result = ioctl(mDeviceFd, VIDIOC_QUERYCAP, &caps);
+ if (result < 0) {
+ ALOGE("failed to get device caps for %s (%d = %s)", deviceName, errno, strerror(errno));
+ return false;
+ }
+ }
+
+ // Report device properties
+ ALOGI("Open Device: %s (fd=%d)", deviceName, mDeviceFd);
+ ALOGI(" Driver: %s", caps.driver);
+ ALOGI(" Card: %s", caps.card);
+ ALOGI(" Version: %u.%u.%u",
+ (caps.version >> 16) & 0xFF,
+ (caps.version >> 8) & 0xFF,
+ (caps.version) & 0xFF);
+ ALOGI(" All Caps: %08X", caps.capabilities);
+ ALOGI(" Dev Caps: %08X", caps.device_caps);
+
+ // Enumerate the available capture formats (if any)
+ ALOGI("Supported capture formats:");
+ v4l2_fmtdesc formatDescriptions;
+ formatDescriptions.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ for (int i=0; true; i++) {
+ formatDescriptions.index = i;
+ if (ioctl(mDeviceFd, VIDIOC_ENUM_FMT, &formatDescriptions) == 0) {
+ ALOGI(" %2d: %s 0x%08X 0x%X",
+ i,
+ formatDescriptions.description,
+ formatDescriptions.pixelformat,
+ formatDescriptions.flags
+ );
+ } else {
+ // No more formats available
+ break;
+ }
+ }
+
+ // Verify we can use this device for video capture
+ if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) ||
+ !(caps.capabilities & V4L2_CAP_STREAMING)) {
+ // Can't do streaming capture.
+ ALOGE("Streaming capture not supported by %s.", deviceName);
+ return false;
+ }
+
+ // Set our desired output format
+ v4l2_format format;
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; // Could/should we request V4L2_PIX_FMT_NV21?
+ format.fmt.pix.width = 720; // TODO: Can we avoid hard coding dimensions?
+ format.fmt.pix.height = 240; // For now, this works with available hardware
+ format.fmt.pix.field = V4L2_FIELD_ALTERNATE; // TODO: Do we need to specify this?
+ ALOGI("Requesting format %c%c%c%c (0x%08X)",
+ ((char*)&format.fmt.pix.pixelformat)[0],
+ ((char*)&format.fmt.pix.pixelformat)[1],
+ ((char*)&format.fmt.pix.pixelformat)[2],
+ ((char*)&format.fmt.pix.pixelformat)[3],
+ format.fmt.pix.pixelformat);
+ if (ioctl(mDeviceFd, VIDIOC_S_FMT, &format) < 0) {
+ ALOGE("VIDIOC_S_FMT: %s", strerror(errno));
+ }
+
+ // Report the current output format
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (ioctl(mDeviceFd, VIDIOC_G_FMT, &format) == 0) {
+
+ mFormat = format.fmt.pix.pixelformat;
+ mWidth = format.fmt.pix.width;
+ mHeight = format.fmt.pix.height;
+ mStride = format.fmt.pix.bytesperline;
+
+ ALOGI("Current output format: fmt=0x%X, %dx%d, pitch=%d",
+ format.fmt.pix.pixelformat,
+ format.fmt.pix.width,
+ format.fmt.pix.height,
+ format.fmt.pix.bytesperline
+ );
+ } else {
+ ALOGE("VIDIOC_G_FMT: %s", strerror(errno));
+ return false;
+ }
+
+ // Make sure we're initialized to the STOPPED state
+ mRunMode = STOPPED;
+ mFrameReady = false;
+
+ // Ready to go!
+ return true;
+}
+
+
+void VideoCapture::close() {
+ ALOGD("VideoCapture::close");
+ // Stream should be stopped first!
+ assert(mRunMode == STOPPED);
+
+ if (isOpen()) {
+ ALOGD("closing video device file handled %d", mDeviceFd);
+ ::close(mDeviceFd);
+ mDeviceFd = -1;
+ }
+}
+
+
+bool VideoCapture::startStream(std::function<void(VideoCapture*, imageBuffer*, void*)> callback) {
+ // Set the state of our background thread
+ int prevRunMode = mRunMode.fetch_or(RUN);
+ if (prevRunMode & RUN) {
+ // The background thread is already running, so we can't start a new stream
+ ALOGE("Already in RUN state, so we can't start a new streaming thread");
+ return false;
+ }
+
+ // Tell the L4V2 driver to prepare our streaming buffers
+ v4l2_requestbuffers bufrequest;
+ bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ bufrequest.memory = V4L2_MEMORY_MMAP;
+ bufrequest.count = 1;
+ if (ioctl(mDeviceFd, VIDIOC_REQBUFS, &bufrequest) < 0) {
+ ALOGE("VIDIOC_REQBUFS: %s", strerror(errno));
+ return false;
+ }
+
+ // Get the information on the buffer that was created for us
+ memset(&mBufferInfo, 0, sizeof(mBufferInfo));
+ mBufferInfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ mBufferInfo.memory = V4L2_MEMORY_MMAP;
+ mBufferInfo.index = 0;
+ if (ioctl(mDeviceFd, VIDIOC_QUERYBUF, &mBufferInfo) < 0) {
+ ALOGE("VIDIOC_QUERYBUF: %s", strerror(errno));
+ return false;
+ }
+
+ ALOGI("Buffer description:");
+ ALOGI(" offset: %d", mBufferInfo.m.offset);
+ ALOGI(" length: %d", mBufferInfo.length);
+
+ // Get a pointer to the buffer contents by mapping into our address space
+ mPixelBuffer = mmap(
+ NULL,
+ mBufferInfo.length,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ mDeviceFd,
+ mBufferInfo.m.offset
+ );
+ if( mPixelBuffer == MAP_FAILED) {
+ ALOGE("mmap: %s", strerror(errno));
+ return false;
+ }
+ memset(mPixelBuffer, 0, mBufferInfo.length);
+ ALOGI("Buffer mapped at %p", mPixelBuffer);
+
+ // Queue the first capture buffer
+ if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
+ ALOGE("VIDIOC_QBUF: %s", strerror(errno));
+ return false;
+ }
+
+ // Start the video stream
+ int type = mBufferInfo.type;
+ if (ioctl(mDeviceFd, VIDIOC_STREAMON, &type) < 0) {
+ ALOGE("VIDIOC_STREAMON: %s", strerror(errno));
+ return false;
+ }
+
+ // Remember who to tell about new frames as they arrive
+ mCallback = callback;
+
+ // Fire up a thread to receive and dispatch the video frames
+ mCaptureThread = std::thread([this](){ collectFrames(); });
+
+ ALOGD("Stream started.");
+ return true;
+}
+
+
+void VideoCapture::stopStream() {
+ // Tell the background thread to stop
+ int prevRunMode = mRunMode.fetch_or(STOPPING);
+ if (prevRunMode == STOPPED) {
+ // The background thread wasn't running, so set the flag back to STOPPED
+ mRunMode = STOPPED;
+ } else if (prevRunMode & STOPPING) {
+ ALOGE("stopStream called while stream is already stopping. Reentrancy is not supported!");
+ return;
+ } else {
+ // Block until the background thread is stopped
+ if (mCaptureThread.joinable()) {
+ mCaptureThread.join();
+ }
+
+ // Stop the underlying video stream (automatically empties the buffer queue)
+ int type = mBufferInfo.type;
+ if (ioctl(mDeviceFd, VIDIOC_STREAMOFF, &type) < 0) {
+ ALOGE("VIDIOC_STREAMOFF: %s", strerror(errno));
+ }
+
+ ALOGD("Capture thread stopped.");
+ }
+
+ // Unmap the buffers we allocated
+ munmap(mPixelBuffer, mBufferInfo.length);
+
+ // Tell the L4V2 driver to release our streaming buffers
+ v4l2_requestbuffers bufrequest;
+ bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ bufrequest.memory = V4L2_MEMORY_MMAP;
+ bufrequest.count = 0;
+ ioctl(mDeviceFd, VIDIOC_REQBUFS, &bufrequest);
+
+ // Drop our reference to the frame delivery callback interface
+ mCallback = nullptr;
+}
+
+
+void VideoCapture::markFrameReady() {
+ mFrameReady = true;
+};
+
+
+bool VideoCapture::returnFrame() {
+ // We're giving the frame back to the system, so clear the "ready" flag
+ mFrameReady = false;
+
+ // Requeue the buffer to capture the next available frame
+ if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
+ ALOGE("VIDIOC_QBUF: %s", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+
+// This runs on a background thread to receive and dispatch video frames
+void VideoCapture::collectFrames() {
+ // Run until our atomic signal is cleared
+ while (mRunMode == RUN) {
+ // Wait for a buffer to be ready
+ if (ioctl(mDeviceFd, VIDIOC_DQBUF, &mBufferInfo) < 0) {
+ ALOGE("VIDIOC_DQBUF: %s", strerror(errno));
+ break;
+ }
+
+ markFrameReady();
+
+ // If a callback was requested per frame, do that now
+ if (mCallback) {
+ mCallback(this, &mBufferInfo, mPixelBuffer);
+ }
+ }
+
+ // Mark ourselves stopped
+ ALOGD("VideoCapture thread ending");
+ mRunMode = STOPPED;
+}
diff --git a/evs/sampleDriver/VideoCapture.h b/evs/sampleDriver/VideoCapture.h
new file mode 100644
index 0000000..f2d1175
--- /dev/null
+++ b/evs/sampleDriver/VideoCapture.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+#include <atomic>
+#include <thread>
+#include <functional>
+#include <linux/videodev2.h>
+
+
+typedef v4l2_buffer imageBuffer;
+
+
+class VideoCapture {
+public:
+ bool open(const char* deviceName);
+ void close();
+
+ bool startStream(std::function<void(VideoCapture*, imageBuffer*, void*)> callback = nullptr);
+ void stopStream();
+
+ // Valid only after open()
+ __u32 getWidth() { return mWidth; };
+ __u32 getHeight() { return mHeight; };
+ __u32 getStride() { return mStride; };
+ __u32 getV4LFormat() { return mFormat; };
+
+ // NULL until stream is started
+ void* getLatestData() { return mPixelBuffer; };
+
+ bool isFrameReady() { return mFrameReady; };
+ void markFrameConsumed() { returnFrame(); };
+
+ bool isOpen() { return mDeviceFd >= 0; };
+
+private:
+ void collectFrames();
+ void markFrameReady();
+ bool returnFrame();
+
+ int mDeviceFd = -1;
+
+ v4l2_buffer mBufferInfo = {};
+ void* mPixelBuffer = nullptr;
+
+ __u32 mFormat = 0;
+ __u32 mWidth = 0;
+ __u32 mHeight = 0;
+ __u32 mStride = 0;
+
+ std::function<void(VideoCapture*, imageBuffer*, void*)> mCallback;
+
+ std::thread mCaptureThread; // The thread we'll use to dispatch frames
+ std::atomic<int> mRunMode; // Used to signal the frame loop (see RunModes below)
+ std::atomic<bool> mFrameReady; // Set when a frame has been delivered
+
+ // Careful changing these -- we're using bit-wise ops to manipulate these
+ enum RunModes {
+ STOPPED = 0,
+ RUN = 1,
+ STOPPING = 2,
+ };
+};
+
diff --git a/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc b/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
new file mode 100644
index 0000000..81fe33a
--- /dev/null
+++ b/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
@@ -0,0 +1,6 @@
+service evs_driver /system/bin/android.hardware.automotive.evs@1.0-sample
+ class hal
+ priority -20
+ user graphics
+ group automotive_evs
+ onrestart restart evs_manager
diff --git a/evs/sampleDriver/bufferCopy.cpp b/evs/sampleDriver/bufferCopy.cpp
new file mode 100644
index 0000000..a585040
--- /dev/null
+++ b/evs/sampleDriver/bufferCopy.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
+
+#include "bufferCopy.h"
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_0 {
+namespace implementation {
+
+
+// Round up to the nearest multiple of the given alignment value
+template<unsigned alignment>
+int align(int value) {
+ static_assert((alignment && !(alignment & (alignment - 1))),
+ "alignment must be a power of 2");
+
+ unsigned mask = alignment - 1;
+ return (value + mask) & ~mask;
+}
+
+
+// Limit the given value to the provided range. :)
+static inline float clamp(float v, float min, float max) {
+ if (v < min) return min;
+ if (v > max) return max;
+ return v;
+}
+
+
+static uint32_t yuvToRgbx(const unsigned char Y, const unsigned char Uin, const unsigned char Vin) {
+ // Don't use this if you want to see the best performance. :)
+ // Better to do this in a pixel shader if we really have to, but on actual
+ // embedded hardware we expect to be able to texture directly from the YUV data
+ float U = Uin - 128.0f;
+ float V = Vin - 128.0f;
+
+ float Rf = Y + 1.140f*V;
+ float Gf = Y - 0.395f*U - 0.581f*V;
+ float Bf = Y + 2.032f*U;
+ unsigned char R = (unsigned char)clamp(Rf, 0.0f, 255.0f);
+ unsigned char G = (unsigned char)clamp(Gf, 0.0f, 255.0f);
+ unsigned char B = (unsigned char)clamp(Bf, 0.0f, 255.0f);
+
+ return ((R & 0xFF)) |
+ ((G & 0xFF) << 8) |
+ ((B & 0xFF) << 16) |
+ 0xFF000000; // Fill the alpha channel with ones
+}
+
+
+void fillNV21FromNV21(const BufferDesc& tgtBuff, uint8_t* tgt, void* imgData, unsigned) {
+ // The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleave U/V array.
+ // It assumes an even width and height for the overall image, and a horizontal stride that is
+ // an even multiple of 16 bytes for both the Y and UV arrays.
+
+ // Target and source image layout properties (They match since the formats match!)
+ const unsigned strideLum = align<16>(tgtBuff.width);
+ const unsigned sizeY = strideLum * tgtBuff.height;
+ const unsigned strideColor = strideLum; // 1/2 the samples, but two interleaved channels
+ const unsigned sizeColor = strideColor * tgtBuff.height/2;
+ const unsigned totalBytes = sizeY + sizeColor;
+
+ // Simply copy the data byte for byte
+ memcpy(tgt, imgData, totalBytes);
+}
+
+
+void fillNV21FromYUYV(const BufferDesc& tgtBuff, uint8_t* tgt, void* imgData, unsigned imgStride) {
+ // The YUYV format provides an interleaved array of pixel values with U and V subsampled in
+ // the horizontal direction only. Also known as interleaved 422 format. A 4 byte
+ // "macro pixel" provides the Y value for two adjacent pixels and the U and V values shared
+ // between those two pixels. The width of the image must be an even number.
+ // We need to down sample the UV values and collect them together after all the packed Y values
+ // to construct the NV21 format.
+ // NV21 requires even width and height, so we assume that is the case for the incomming image
+ // as well.
+ uint32_t *srcDataYUYV = (uint32_t*)imgData;
+ struct YUYVpixel {
+ uint8_t Y1;
+ uint8_t U;
+ uint8_t Y2;
+ uint8_t V;
+ };
+
+ // Target image layout properties
+ const unsigned strideLum = align<16>(tgtBuff.width);
+ const unsigned sizeY = strideLum * tgtBuff.height;
+ const unsigned strideColor = strideLum; // 1/2 the samples, but two interleaved channels
+
+ // Source image layout properties
+ const unsigned srcRowPixels = imgStride/4; // imgStride is in units of bytes
+ const unsigned srcRowDoubleStep = srcRowPixels * 2;
+ uint32_t* topSrcRow = srcDataYUYV;
+ uint32_t* botSrcRow = srcDataYUYV + srcRowPixels;
+
+ // We're going to work on one 2x2 cell in the output image at at time
+ for (unsigned cellRow = 0; cellRow < tgtBuff.height/2; cellRow++) {
+
+ // Set up the output pointers
+ uint8_t* yTopRow = tgt + (cellRow*2) * strideLum;
+ uint8_t* yBotRow = yTopRow + strideLum;
+ uint8_t* uvRow = (tgt + sizeY) + cellRow * strideColor;
+
+ for (unsigned cellCol = 0; cellCol < tgtBuff.width/2; cellCol++) {
+ // Collect the values from the YUYV interleaved data
+ const YUYVpixel* pTopMacroPixel = (YUYVpixel*)&topSrcRow[cellCol];
+ const YUYVpixel* pBotMacroPixel = (YUYVpixel*)&botSrcRow[cellCol];
+
+ // Down sample the U/V values by linear average between rows
+ const uint8_t uValue = (pTopMacroPixel->U + pBotMacroPixel->U) >> 1;
+ const uint8_t vValue = (pTopMacroPixel->V + pBotMacroPixel->V) >> 1;
+
+ // Store the values into the NV21 layout
+ yTopRow[cellCol*2] = pTopMacroPixel->Y1;
+ yTopRow[cellCol*2+1] = pTopMacroPixel->Y2;
+ yBotRow[cellCol*2] = pBotMacroPixel->Y1;
+ yBotRow[cellCol*2+1] = pBotMacroPixel->Y2;
+ uvRow[cellCol*2] = uValue;
+ uvRow[cellCol*2+1] = vValue;
+ }
+
+ // Skipping two rows to get to the next set of two source rows
+ topSrcRow += srcRowDoubleStep;
+ botSrcRow += srcRowDoubleStep;
+ }
+}
+
+
+void fillRGBAFromYUYV(const BufferDesc& tgtBuff, uint8_t* tgt, void* imgData, unsigned imgStride) {
+ unsigned width = tgtBuff.width;
+ unsigned height = tgtBuff.height;
+ uint32_t* src = (uint32_t*)imgData;
+ uint32_t* dst = (uint32_t*)tgt;
+ unsigned srcStridePixels = imgStride / 2;
+ unsigned dstStridePixels = tgtBuff.stride;
+
+ const int srcRowPadding32 = srcStridePixels/2 - width/2; // 2 bytes per pixel, 4 bytes per word
+ const int dstRowPadding32 = dstStridePixels - width; // 4 bytes per pixel, 4 bytes per word
+
+ for (unsigned r=0; r<height; r++) {
+ for (unsigned c=0; c<width/2; c++) {
+ // Note: we're walking two pixels at a time here (even/odd)
+ uint32_t srcPixel = *src++;
+
+ uint8_t Y1 = (srcPixel) & 0xFF;
+ uint8_t U = (srcPixel >> 8) & 0xFF;
+ uint8_t Y2 = (srcPixel >> 16) & 0xFF;
+ uint8_t V = (srcPixel >> 24) & 0xFF;
+
+ // On the RGB output, we're writing one pixel at a time
+ *(dst+0) = yuvToRgbx(Y1, U, V);
+ *(dst+1) = yuvToRgbx(Y2, U, V);
+ dst += 2;
+ }
+
+ // Skip over any extra data or end of row alignment padding
+ src += srcRowPadding32;
+ dst += dstRowPadding32;
+ }
+}
+
+
+void fillYUYVFromYUYV(const BufferDesc& tgtBuff, uint8_t* tgt, void* imgData, unsigned imgStride) {
+ unsigned width = tgtBuff.width;
+ unsigned height = tgtBuff.height;
+ uint8_t* src = (uint8_t*)imgData;
+ uint8_t* dst = (uint8_t*)tgt;
+ unsigned srcStrideBytes = imgStride;
+ unsigned dstStrideBytes = tgtBuff.stride * 2;
+
+ for (unsigned r=0; r<height; r++) {
+ // Copy a pixel row at a time (2 bytes per pixel, averaged over a YUYV macro pixel)
+ memcpy(dst+r*dstStrideBytes, src+r*srcStrideBytes, width*2);
+ }
+}
+
+
+void fillYUYVFromUYVY(const BufferDesc& tgtBuff, uint8_t* tgt, void* imgData, unsigned imgStride) {
+ unsigned width = tgtBuff.width;
+ unsigned height = tgtBuff.height;
+ uint32_t* src = (uint32_t*)imgData;
+ uint32_t* dst = (uint32_t*)tgt;
+ unsigned srcStridePixels = imgStride / 2;
+ unsigned dstStridePixels = tgtBuff.stride;
+
+ const int srcRowPadding32 = srcStridePixels/2 - width/2; // 2 bytes per pixel, 4 bytes per word
+ const int dstRowPadding32 = dstStridePixels/2 - width/2; // 2 bytes per pixel, 4 bytes per word
+
+ for (unsigned r=0; r<height; r++) {
+ for (unsigned c=0; c<width/2; c++) {
+ // Note: we're walking two pixels at a time here (even/odd)
+ uint32_t srcPixel = *src++;
+
+ uint8_t Y1 = (srcPixel) & 0xFF;
+ uint8_t U = (srcPixel >> 8) & 0xFF;
+ uint8_t Y2 = (srcPixel >> 16) & 0xFF;
+ uint8_t V = (srcPixel >> 24) & 0xFF;
+
+ // Now we write back the pair of pixels with the components swizzled
+ *dst++ = (U) |
+ (Y1 << 8) |
+ (V << 16) |
+ (Y2 << 24);
+ }
+
+ // Skip over any extra data or end of row alignment padding
+ src += srcRowPadding32;
+ dst += dstRowPadding32;
+ }
+}
+
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/evs/sampleDriver/bufferCopy.h b/evs/sampleDriver/bufferCopy.h
new file mode 100644
index 0000000..9f68f2b
--- /dev/null
+++ b/evs/sampleDriver/bufferCopy.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_BUFFERCOPY_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_BUFFERCOPY_H
+
+#include <android/hardware/automotive/evs/1.0/types.h>
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_0 {
+namespace implementation {
+
+
+void fillNV21FromNV21(const BufferDesc& tgtBuff, uint8_t* tgt,
+ void* imgData, unsigned imgStride);
+
+void fillNV21FromYUYV(const BufferDesc& tgtBuff, uint8_t* tgt,
+ void* imgData, unsigned imgStride);
+
+void fillRGBAFromYUYV(const BufferDesc& tgtBuff, uint8_t* tgt,
+ void* imgData, unsigned imgStride);
+
+void fillYUYVFromYUYV(const BufferDesc& tgtBuff, uint8_t* tgt,
+ void* imgData, unsigned imgStride);
+
+void fillYUYVFromUYVY(const BufferDesc& tgtBuff, uint8_t* tgt,
+ void* imgData, unsigned imgStride);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_0_BUFFERCOPY_H
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
new file mode 100644
index 0000000..d73f758
--- /dev/null
+++ b/evs/sampleDriver/service.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
+
+#include <unistd.h>
+
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Log.h>
+
+#include "ServiceNames.h"
+#include "EvsEnumerator.h"
+#include "EvsGlDisplay.h"
+
+
+// libhidl:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::hardware::automotive::evs::V1_0::IEvsEnumerator;
+using android::hardware::automotive::evs::V1_0::IEvsDisplay;
+
+// The namespace in which all our implementation code lives
+using namespace android::hardware::automotive::evs::V1_0::implementation;
+using namespace android;
+
+
+int main() {
+ ALOGI("EVS Hardware Enumerator service is starting");
+ android::sp<IEvsEnumerator> service = new EvsEnumerator();
+
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ // Register our service -- if somebody is already registered by our name,
+ // they will be killed (their thread pool will throw an exception).
+ status_t status = service->registerAsService(kEnumeratorServiceName);
+ if (status == OK) {
+ ALOGD("%s is ready.", kEnumeratorServiceName);
+ joinRpcThreadpool();
+ } else {
+ ALOGE("Could not register service %s (%d).", kEnumeratorServiceName, status);
+ }
+
+ // In normal operation, we don't expect the thread pool to exit
+ ALOGE("EVS Hardware Enumerator is shutting down");
+ return 1;
+}
diff --git a/obd2-lib/src/com/android/car/obd2/IntegerArrayStream.java b/obd2-lib/src/com/android/car/obd2/IntegerArrayStream.java
index 56f64f7..4768ceb 100644
--- a/obd2-lib/src/com/android/car/obd2/IntegerArrayStream.java
+++ b/obd2-lib/src/com/android/car/obd2/IntegerArrayStream.java
@@ -43,6 +43,10 @@
return mData.length - mIndex;
}
+ public boolean isEmpty() {
+ return residualLength() == 0;
+ }
+
public boolean hasAtLeast(int n) {
return residualLength() >= n;
}
diff --git a/obd2-lib/src/com/android/car/obd2/Obd2Command.java b/obd2-lib/src/com/android/car/obd2/Obd2Command.java
index e3366cf..30fca0c 100644
--- a/obd2-lib/src/com/android/car/obd2/Obd2Command.java
+++ b/obd2-lib/src/com/android/car/obd2/Obd2Command.java
@@ -172,7 +172,7 @@
* @param <ValueType> The Java type that represents the command's result type.
*/
public static class FreezeFrameCommand<ValueType> extends Obd2Command<ValueType> {
- private static final int RESPONSE_MARKER = 0x2;
+ private static final int RESPONSE_MARKER = 0x42;
private int mFrameId;
diff --git a/obd2-lib/src/com/android/car/obd2/Obd2Connection.java b/obd2-lib/src/com/android/car/obd2/Obd2Connection.java
index bfdb9c0..f7a2d36 100644
--- a/obd2-lib/src/com/android/car/obd2/Obd2Connection.java
+++ b/obd2-lib/src/com/android/car/obd2/Obd2Connection.java
@@ -20,12 +20,16 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
/** This class represents a connection between Java code and a "vehicle" that talks OBD2. */
public class Obd2Connection {
+ private static final String TAG = Obd2Connection.class.getSimpleName();
+ private static final boolean DBG = false;
/**
* The transport layer that moves OBD2 requests from us to the remote entity and viceversa. It
@@ -72,6 +76,10 @@
return true;
}
+ public boolean isConnected() {
+ return mConnection.isConnected();
+ }
+
static int toDigitValue(char c) {
if ((c >= '0') && (c <= '9')) return c - '0';
switch (c) {
@@ -112,6 +120,10 @@
InputStream in = Objects.requireNonNull(mConnection.getInputStream());
OutputStream out = Objects.requireNonNull(mConnection.getOutputStream());
+ if (DBG) {
+ Log.i(TAG, "runImpl(" + command + ")");
+ }
+
out.write((command + "\r").getBytes());
out.flush();
@@ -127,6 +139,11 @@
}
String responseValue = response.toString();
+
+ if (DBG) {
+ Log.i(TAG, "runImpl() returned " + responseValue);
+ }
+
return responseValue;
}
@@ -137,11 +154,30 @@
return response;
}
+ String unpackLongFrame(String response) {
+ // long frames come back to us containing colon separated portions
+ if (response.indexOf(':') < 0) return response;
+
+ // remove everything until the first colon
+ response = response.substring(response.indexOf(':') + 1);
+
+ // then remove the <digit>: portions (sequential frame parts)
+ //TODO(egranata): maybe validate the sequence of digits is progressive
+ return response.replaceAll("[0-9]:", "");
+ }
+
public int[] run(String command) throws IOException, InterruptedException {
String responseValue = runImpl(command);
String originalResponseValue = responseValue;
- if (responseValue.startsWith(command))
- responseValue = responseValue.substring(command.length());
+ String unspacedCommand = command.replaceAll(" ", "");
+ if (responseValue.startsWith(unspacedCommand))
+ responseValue = responseValue.substring(unspacedCommand.length());
+ responseValue = unpackLongFrame(responseValue);
+
+ if (DBG) {
+ Log.i(TAG, "post-processed response " + responseValue);
+ }
+
//TODO(egranata): should probably handle these intelligently
responseValue =
removeSideData(
@@ -157,11 +193,12 @@
if (responseValue.equals("?")) return new int[] {0};
if (responseValue.equals("NODATA")) return new int[] {};
if (responseValue.equals("UNABLETOCONNECT")) throw new IOException("connection failure");
+ if (responseValue.equals("CANERROR")) throw new IOException("CAN bus error");
try {
return toHexValues(responseValue);
} catch (IllegalArgumentException e) {
Log.e(
- "OBD2",
+ TAG,
String.format(
"conversion error: command: '%s', original response: '%s'"
+ ", processed response: '%s'",
@@ -224,7 +261,7 @@
public Set<Integer> getSupportedPIDs() throws IOException, InterruptedException {
Set<Integer> result = new HashSet<>();
String[] pids = new String[] {"0100", "0120", "0140", "0160"};
- int basePid = 0;
+ int basePid = 1;
for (String pid : pids) {
int[] responseData = run(pid);
if (responseData.length >= 6) {
@@ -232,11 +269,19 @@
byte byte1 = (byte) (responseData[3] & 0xFF);
byte byte2 = (byte) (responseData[4] & 0xFF);
byte byte3 = (byte) (responseData[5] & 0xFF);
+ if (DBG) {
+ Log.i(TAG, String.format("supported PID at base %d payload %02X%02X%02X%02X",
+ basePid, byte0, byte1, byte2, byte3));
+ }
FourByteBitSet fourByteBitSet = new FourByteBitSet(byte0, byte1, byte2, byte3);
for (int byteIndex = 0; byteIndex < 4; ++byteIndex) {
for (int bitIndex = 7; bitIndex >= 0; --bitIndex) {
if (fourByteBitSet.getBit(byteIndex, bitIndex)) {
- result.add(basePid + 8 * byteIndex + 7 - bitIndex);
+ int command = basePid + 8 * byteIndex + 7 - bitIndex;
+ if (DBG) {
+ Log.i(TAG, "command " + command + " found supported");
+ }
+ result.add(command);
}
}
}
@@ -246,4 +291,46 @@
return result;
}
+
+ String getDiagnosticTroubleCode(IntegerArrayStream source) {
+ final char[] components = new char[] {'P', 'C', 'B', 'U'};
+ final char[] firstDigits = new char[] {'0', '1', '2', '3'};
+ final char[] otherDigits =
+ new char[] {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ StringBuilder builder = new StringBuilder(5);
+
+ int byte0 = source.consume();
+ int byte1 = source.consume();
+
+ int componentMask = (byte0 & 0xC0) >> 6;
+ int firstDigitMask = (byte0 & 0x30) >> 4;
+ int secondDigitMask = (byte0 & 0x0F);
+ int thirdDigitMask = (byte1 & 0xF0) >> 4;
+ int fourthDigitMask = (byte1 & 0x0F);
+
+ builder.append(components[componentMask]);
+ builder.append(firstDigits[firstDigitMask]);
+ builder.append(otherDigits[secondDigitMask]);
+ builder.append(otherDigits[thirdDigitMask]);
+ builder.append(otherDigits[fourthDigitMask]);
+
+ return builder.toString();
+ }
+
+ public List<String> getDiagnosticTroubleCodes() throws IOException, InterruptedException {
+ List<String> result = new ArrayList<>();
+ int[] response = run("03");
+ IntegerArrayStream stream = new IntegerArrayStream(response);
+ if (stream.isEmpty()) return result;
+ if (!stream.expect(0x43))
+ throw new IllegalArgumentException("data from remote end not a mode 3 response");
+ int count = stream.consume();
+ for (int i = 0; i < count; ++i) {
+ result.add(getDiagnosticTroubleCode(stream));
+ }
+ return result;
+ }
}
diff --git a/obd2-lib/src/com/android/car/obd2/Obd2FreezeFrameGenerator.java b/obd2-lib/src/com/android/car/obd2/Obd2FreezeFrameGenerator.java
new file mode 100644
index 0000000..3de4863
--- /dev/null
+++ b/obd2-lib/src/com/android/car/obd2/Obd2FreezeFrameGenerator.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 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.android.car.obd2;
+
+import android.os.SystemClock;
+import android.util.JsonWriter;
+import android.util.Log;
+import com.android.car.obd2.Obd2Command.FreezeFrameCommand;
+import com.android.car.obd2.Obd2Command.OutputSemanticHandler;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public class Obd2FreezeFrameGenerator {
+ public static final String FRAME_TYPE_FREEZE = "freeze";
+ public static final String TAG = Obd2FreezeFrameGenerator.class.getSimpleName();
+
+ private final Obd2Connection mConnection;
+ private final List<OutputSemanticHandler<Integer>> mIntegerCommands = new ArrayList<>();
+ private final List<OutputSemanticHandler<Float>> mFloatCommands = new ArrayList<>();
+
+ private List<String> mPreviousDtcs = new ArrayList<>();
+
+ public Obd2FreezeFrameGenerator(Obd2Connection connection)
+ throws IOException, InterruptedException {
+ mConnection = connection;
+ Set<Integer> connectionPids = connection.getSupportedPIDs();
+ Set<Integer> apiIntegerPids = Obd2Command.getSupportedIntegerCommands();
+ Set<Integer> apiFloatPids = Obd2Command.getSupportedFloatCommands();
+ apiIntegerPids
+ .stream()
+ .filter(connectionPids::contains)
+ .forEach((Integer pid) -> mIntegerCommands.add(Obd2Command.getIntegerCommand(pid)));
+ apiFloatPids
+ .stream()
+ .filter(connectionPids::contains)
+ .forEach((Integer pid) -> mFloatCommands.add(Obd2Command.getFloatCommand(pid)));
+ Log.i(
+ TAG,
+ String.format(
+ "connectionPids = %s\napiIntegerPids=%s\napiFloatPids = %s\n"
+ + "mIntegerCommands = %s\nmFloatCommands = %s\n",
+ connectionPids,
+ apiIntegerPids,
+ apiFloatPids,
+ mIntegerCommands,
+ mFloatCommands));
+ }
+
+ public JsonWriter generate(JsonWriter jsonWriter) throws IOException, InterruptedException {
+ return generate(jsonWriter, SystemClock.elapsedRealtimeNanos());
+ }
+
+ // OBD2 does not have a notion of timestamping the fault codes
+ // As such, we need to perform additional magic in order to figure out
+ // whether a fault code we retrieved is the same as a fault code we already
+ // saw in a past iteration. The logic goes as follows:
+ // for every position i in currentDtcs, if mPreviousDtcs[i] is the same
+ // fault code, then assume they are identical. If they are not the same fault code,
+ // then everything in currentDtcs[i...size()) is assumed to be a new fault code as
+ // something in the list must have moved around; if currentDtcs is shorter than
+ // mPreviousDtcs then obviously exit at the end of currentDtcs; if currentDtcs
+ // is longer, however, anything in currentDtcs past the end of mPreviousDtcs is a new
+ // fault code and will be included
+ private final class FreezeFrameIdentity {
+ public final String dtc;
+ public final int id;
+
+ FreezeFrameIdentity(String dtc, int id) {
+ this.dtc = dtc;
+ this.id = id;
+ }
+ }
+
+ private List<FreezeFrameIdentity> discoverNewDtcs(List<String> currentDtcs) {
+ List<FreezeFrameIdentity> newDtcs = new ArrayList<>();
+ int currentIndex = 0;
+ boolean inCopyAllMode = false;
+
+ for (; currentIndex < currentDtcs.size(); ++currentIndex) {
+ if (currentIndex == mPreviousDtcs.size()) {
+ // we have more current DTCs than previous DTCs, copy everything
+ inCopyAllMode = true;
+ break;
+ }
+ if (!currentDtcs.get(currentIndex).equals(mPreviousDtcs.get(currentIndex))) {
+ // we found a different DTC, copy everything
+ inCopyAllMode = true;
+ break;
+ }
+ // same DTC, not at end of either list yet, keep looping
+ }
+
+ if (inCopyAllMode) {
+ for (; currentIndex < currentDtcs.size(); ++currentIndex) {
+ newDtcs.add(new FreezeFrameIdentity(currentDtcs.get(currentIndex), currentIndex));
+ }
+ }
+
+ return newDtcs;
+ }
+
+ public JsonWriter generate(JsonWriter jsonWriter, long timestamp)
+ throws IOException, InterruptedException {
+ List<String> currentDtcs = mConnection.getDiagnosticTroubleCodes();
+ List<FreezeFrameIdentity> newDtcs = discoverNewDtcs(currentDtcs);
+ mPreviousDtcs = currentDtcs;
+ for (FreezeFrameIdentity freezeFrame : newDtcs) {
+ jsonWriter.beginObject();
+ jsonWriter.name("type").value(FRAME_TYPE_FREEZE);
+ jsonWriter.name("timestamp").value(timestamp);
+ jsonWriter.name("stringValue").value(freezeFrame.dtc);
+ jsonWriter.name("intValues").beginArray();
+ for (OutputSemanticHandler<Integer> handler : mIntegerCommands) {
+ FreezeFrameCommand<Integer> command =
+ Obd2Command.getFreezeFrameCommand(handler, freezeFrame.id);
+ try {
+ Optional<Integer> result = command.run(mConnection);
+ if (result.isPresent()) {
+ jsonWriter.beginObject();
+ jsonWriter.name("id").value(command.getPid());
+ jsonWriter.name("value").value(result.get());
+ jsonWriter.endObject();
+ }
+ } catch (IOException | InterruptedException e) {
+ Log.w(
+ TAG,
+ String.format(
+ "unable to retrieve OBD2 pid %d due to exception: %s",
+ command.getPid(), e));
+ // skip this entry
+ }
+ }
+ jsonWriter.endArray();
+ jsonWriter.name("floatValues").beginArray();
+ for (OutputSemanticHandler<Float> handler : mFloatCommands) {
+ FreezeFrameCommand<Float> command =
+ Obd2Command.getFreezeFrameCommand(handler, freezeFrame.id);
+ try {
+ Optional<Float> result = command.run(mConnection);
+ if (result.isPresent()) {
+ jsonWriter.beginObject();
+ jsonWriter.name("id").value(command.getPid());
+ jsonWriter.name("value").value(result.get());
+ jsonWriter.endObject();
+ }
+ } catch (IOException | InterruptedException e) {
+ Log.w(
+ TAG,
+ String.format(
+ "unable to retrieve OBD2 pid %d due to exception: %s",
+ command.getPid(), e));
+ // skip this entry
+ }
+ }
+ jsonWriter.endArray();
+ jsonWriter.endObject();
+ }
+ return jsonWriter;
+ }
+}
diff --git a/obd2-lib/src/com/android/car/obd2/Obd2LiveFrameGenerator.java b/obd2-lib/src/com/android/car/obd2/Obd2LiveFrameGenerator.java
index 121b54a..26f408f 100644
--- a/obd2-lib/src/com/android/car/obd2/Obd2LiveFrameGenerator.java
+++ b/obd2-lib/src/com/android/car/obd2/Obd2LiveFrameGenerator.java
@@ -27,7 +27,7 @@
import java.util.Set;
public class Obd2LiveFrameGenerator {
- public static final int FRAME_TYPE_LIVE = 1;
+ public static final String FRAME_TYPE_LIVE = "live";
public static final String TAG = Obd2LiveFrameGenerator.class.getSimpleName();
private final Obd2Connection mConnection;
@@ -56,6 +56,16 @@
mFloatCommands.add(
Obd2Command.getLiveFrameCommand(
Obd2Command.getFloatCommand(pid))));
+ Log.i(
+ TAG,
+ String.format(
+ "connectionPids = %s\napiIntegerPids=%s\napiFloatPids = %s\n"
+ + "mIntegerCommands = %s\nmFloatCommands = %s\n",
+ connectionPids,
+ apiIntegerPids,
+ apiFloatPids,
+ mIntegerCommands,
+ mFloatCommands));
}
public JsonWriter generate(JsonWriter jsonWriter) throws IOException {
diff --git a/obd2-lib/src/com/android/car/obd2/commands/RPM.java b/obd2-lib/src/com/android/car/obd2/commands/RPM.java
index b277abf..a062e97 100644
--- a/obd2-lib/src/com/android/car/obd2/commands/RPM.java
+++ b/obd2-lib/src/com/android/car/obd2/commands/RPM.java
@@ -30,7 +30,7 @@
public Optional<Integer> consume(IntegerArrayStream data) {
return data.hasAtLeast(
2,
- theData -> Optional.of(theData.consume() * 256 + theData.consume() / 4),
+ theData -> Optional.of((theData.consume() * 256 + theData.consume()) / 4),
theData -> Optional.<Integer>empty());
}
}
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 5d474cd..263c83a 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -92,7 +92,7 @@
android:description="@string/car_permission_desc_diag_read" />
<permission
android:name="android.car.permission.DIAGNOSTIC_CLEAR"
- android:protectionLevel="dangerous"
+ android:protectionLevel="system|signature"
android:label="@string/car_permission_label_diag_clear"
android:description="@string/car_permission_desc_diag_clear" />
<permission
@@ -139,6 +139,7 @@
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 088b4ed..fd4a841 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -76,6 +76,9 @@
<string name="defaultHomeActivity">com.android.car.overview/com.android.car.overview.StreamOverviewActivity</string>
<!-- The com.android.car.VmsPublisherService will bind to this list of clients -->
<string-array translatable="false" name="vmsPublisherClients">
- <item>"com.google.android.car.vms.publisher/.VmsPublisherClientSampleService"</item>
+ </string-array>
+ <!-- Permissions that the com.android.car.VmsPublisherService is allowed to grant to publishers -->
+ <string-array translatable="false" name="vmsSafePermissions">
+ <item>"android.permission.ACCESS_FINE_LOCATION"</item>
</string-array>
</resources>
diff --git a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
index 4cdb640..e80b843 100644
--- a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
+++ b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
@@ -24,6 +24,7 @@
import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothPbapClient;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.CarSensorEvent;
import android.car.hardware.CarSensorManager;
@@ -38,6 +39,8 @@
import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES;
import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.Settings;
@@ -237,7 +240,7 @@
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (DBG) {
if (device != null) {
- Log.d(TAG, "Received Intent for device: " + device.getName() + action);
+ Log.d(TAG, "Received Intent for device: " + device + " " + action);
} else {
Log.d(TAG, "Received Intent no device: " + action);
}
@@ -287,6 +290,91 @@
writeDeviceInfoToSettings();
resetBluetoothDevicesConnectionInfo();
}
+ } else if (BluetoothDevice.ACTION_UUID.equals(action)) {
+ // Received during pairing with the UUIDs of the Bluetooth profiles supported by
+ // the remote device.
+ if (DBG) {
+ Log.d(TAG, "Received UUID intent for device " + device);
+ }
+ Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
+ if (uuids != null) {
+ ParcelUuid[] uuidsToSend = new ParcelUuid[uuids.length];
+ for (int i = 0; i < uuidsToSend.length; i++) {
+ uuidsToSend[i] = (ParcelUuid)uuids[i];
+ }
+ setProfilePriorities(device, uuidsToSend, BluetoothProfile.PRIORITY_ON);
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Set priority for the Bluetooth profiles.
+ *
+ * The Bluetooth service stores the priority of a Bluetooth profile per device as a key value
+ * pair - BluetoothProfile_device:<Priority>.
+ * When we pair a device from the Settings App, the expected behavior is for the app to connect
+ * on all appropriate profiles after successful pairing automatically, without the user having
+ * to explicitly issue a connect. The settings app checks for the priority of the device from
+ * the above key-value pair and if the priority is set to PRIORITY_OFF or PRIORITY_UNDEFINED,
+ * the settings app will stop with just pairing and not connect.
+ * This scenario will happen when we pair a device, then unpair it and then pair it again. When
+ * the device is unpaired, the BT stack sets the priority for that device to PRIORITY_UNDEFINED
+ * ( as a way of resetting). So, the next time the same device is paired, the Settings app will
+ * stop with just pairing and not connect as explained above. Here, we register to receive the
+ * ACTION_UUID intent, which will broadcast the UUIDs corresponding to the profiles supported by
+ * the remote device which is successfully paired and we turn on the priority so when the
+ * Settings app tries to check before connecting, the priority is set to the expected value.
+ *
+ * @param device - Remote Bluetooth device
+ * @param uuids - UUIDs of the Bluetooth Profiles supported by the remote device
+ * @param priority - priority to set
+ */
+ private void setProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids, int priority) {
+ // need the BluetoothProfile proxy to be able to call the setPriority API
+ if (mCarBluetoothUserService == null) {
+ mCarBluetoothUserService = setupBluetoothUserService();
+ }
+ if (mCarBluetoothUserService != null) {
+ for (Integer profile : mProfilesToConnect) {
+ setBluetoothProfilePriorityIfUuidFound(uuids, profile, device, priority);
+ }
+ }
+ }
+
+ private void setBluetoothProfilePriorityIfUuidFound(ParcelUuid[] uuids, int profile,
+ BluetoothDevice device, int priority) {
+ if (mCarBluetoothUserService == null || device == null) {
+ return;
+ }
+ // Build a list of UUIDs that represent a profile.
+ List<ParcelUuid> uuidsToCheck = new ArrayList<>();
+ switch (profile) {
+ case BluetoothProfile.A2DP_SINK:
+ uuidsToCheck.add(BluetoothUuid.AudioSource);
+ break;
+ case BluetoothProfile.HEADSET_CLIENT:
+ uuidsToCheck.add(BluetoothUuid.Handsfree_AG);
+ uuidsToCheck.add(BluetoothUuid.HSP_AG);
+ break;
+ case BluetoothProfile.PBAP_CLIENT:
+ uuidsToCheck.add(BluetoothUuid.PBAP_PSE);
+ break;
+ case BluetoothProfile.MAP_CLIENT:
+ uuidsToCheck.add(BluetoothUuid.MAS);
+ break;
+ }
+
+ for (ParcelUuid uuid : uuidsToCheck) {
+ if (BluetoothUuid.isUuidPresent(uuids, uuid)) {
+ try {
+ mCarBluetoothUserService.setProfilePriority(profile, device, priority);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling setProfilePriority");
+ }
+ // if any one of the uuid in uuidsTocheck is present, set the priority and break
+ break;
}
}
}
@@ -309,6 +397,9 @@
if (DBG) {
Log.d(TAG, "Connected to PerUserCarService");
}
+ // Get the BluetoothUserService and also setup the Bluetooth Connection Proxy for
+ // all profiles.
+ mCarBluetoothUserService = setupBluetoothUserService();
// re-initialize for current user.
initializeUserSpecificInfo();
}
@@ -415,9 +506,6 @@
setupBluetoothEventsIntentFilterLocked();
mConnectionInFlight = new ConnectionParams();
- // Get the BluetoothUserService and also setup the Bluetooth Connection Proxy for
- // all profiles.
- mCarBluetoothUserService = setupBluetoothUserService();
mUserSpecificInfoInitialized = true;
}
}
@@ -438,8 +526,11 @@
profileFilter.addAction(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
profileFilter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
profileFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- mContext.registerReceiverAsUser(mBluetoothBroadcastReceiver, UserHandle.CURRENT,
- profileFilter, null, null);
+ profileFilter.addAction(BluetoothDevice.ACTION_UUID);
+ if (mContext != null) {
+ mContext.registerReceiverAsUser(mBluetoothBroadcastReceiver, UserHandle.CURRENT,
+ profileFilter, null, null);
+ }
}
/**
@@ -646,7 +737,7 @@
return;
}
if (DBG) {
- Log.d(TAG, "BondState :" + bondState + " Device: " + device.getName());
+ Log.d(TAG, "BondState :" + bondState + " Device: " + device);
}
// Bonded devices are added to a profile's device list after the device CONNECTS on the
// profile. When unpaired, we remove the device from all of the profiles' device list.
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index 29012da..d161883 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -83,6 +83,8 @@
*/
private static final long NO_FOCUS_PLAY_WAIT_TIME_MS = 100;
+ private static final String RADIO_ROUTING_SOURCE_PREFIX = "RADIO_";
+
private final AudioHalService mAudioHal;
private final Context mContext;
private final HandlerThread mFocusHandlerThread;
@@ -102,10 +104,10 @@
@GuardedBy("mLock")
private LinkedList<AudioFocusInfo> mPendingFocusChanges = new LinkedList<>();
@GuardedBy("mLock")
- private AudioFocusInfo mTopFocusInfo = null;
+ private AudioFocusInfo mPrimaryFocusInfo = null;
/** previous top which may be in ducking state */
@GuardedBy("mLock")
- private AudioFocusInfo mSecondFocusInfo = null;
+ private AudioFocusInfo mSecondaryFocusInfo = null;
private AudioRoutingPolicy mAudioRoutingPolicy;
private final AudioManager mAudioManager;
@@ -118,12 +120,8 @@
new MediaMuteAudioFocusListener();
@GuardedBy("mLock")
- private int mBottomFocusState;
- @GuardedBy("mLock")
private boolean mRadioOrExtSourceActive = false;
@GuardedBy("mLock")
- private boolean mCallActive = false;
- @GuardedBy("mLock")
private int mCurrentAudioContexts = 0;
@GuardedBy("mLock")
private int mCurrentPrimaryAudioContext = 0;
@@ -209,11 +207,6 @@
int r = mAudioManager.requestAudioFocus(mBottomAudioFocusListener, mAttributeBottom,
AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_FLAG_DELAY_OK);
synchronized (mLock) {
- if (r == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mBottomFocusState = AudioManager.AUDIOFOCUS_GAIN;
- } else {
- mBottomFocusState = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
- }
mCurrentFocusState = currentState;
mCurrentAudioContexts = 0;
}
@@ -237,7 +230,7 @@
mAudioHal.getExternalAudioRoutingTypes();
if (externalRoutingTypes != null) {
for (String routingType : externalRoutingTypes.keySet()) {
- if (routingType.startsWith("RADIO_")) {
+ if (routingType.startsWith(RADIO_ROUTING_SOURCE_PREFIX)) {
externalRadioRoutingTypes.add(routingType);
} else {
externalNonRadioRoutingTypes.add(routingType);
@@ -270,7 +263,7 @@
mAudioPolicy = audioPolicy;
}
mRadioPhysicalStream = audioRoutingPolicy.getPhysicalStreamForLogicalStream(
- CarAudioManager.CAR_AUDIO_USAGE_RADIO);;
+ CarAudioManager.CAR_AUDIO_USAGE_RADIO);
mSystemSoundPhysicalStream = audioRoutingPolicy.getPhysicalStreamForLogicalStream(
CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND);
mSystemSoundPhysicalStreamActive = false;
@@ -418,7 +411,7 @@
mAudioParamKeys = null;
mCurrentFocusState = FocusState.STATE_LOSS;
mLastFocusRequestToCar = null;
- mTopFocusInfo = null;
+ mPrimaryFocusInfo = null;
mPendingFocusChanges.clear();
mRadioOrExtSourceActive = false;
if (mCarAudioContextChangeHandler != null) {
@@ -458,7 +451,7 @@
" mLastFocusRequestToCar:" + mLastFocusRequestToCar);
writer.println(" mCurrentAudioContexts:0x" +
Integer.toHexString(mCurrentAudioContexts));
- writer.println(" mCallActive:" + mCallActive + " mRadioOrExtSourceActive:" +
+ writer.println(" mRadioOrExtSourceActive:" +
mRadioOrExtSourceActive);
writer.println(" mCurrentPrimaryAudioContext:" + mCurrentPrimaryAudioContext +
" mCurrentPrimaryPhysicalStream:" + mCurrentPrimaryPhysicalStream);
@@ -731,7 +724,7 @@
Log.d(TAG_FOCUS, "focus change from car:" + mFocusReceived);
}
systemSoundActive = mSystemSoundPhysicalStreamActive;
- topInfo = mTopFocusInfo;
+ topInfo = mPrimaryFocusInfo;
if (!mFocusReceived.equals(mCurrentFocusState.focusState)) {
newFocusState = mFocusReceived.focusState;
}
@@ -766,7 +759,7 @@
newFocusState ==
AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE) {
// clear second one as there can be no such item in these LOSS.
- mSecondFocusInfo = null;
+ mSecondaryFocusInfo = null;
}
}
switch (newFocusState) {
@@ -878,50 +871,50 @@
doHandleAndroidFocusChange(true /*triggeredByStreamChange*/);
}
- private boolean isFocusFromCarServiceBottom(AudioFocusInfo info) {
+ private boolean checkFocusUsage(AudioFocusInfo info, int expectedUsage) {
if (info == null) {
return false;
}
- AudioAttributes attrib = info.getAttributes();
- if (info.getPackageName().equals(mContext.getOpPackageName()) &&
- CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib) ==
- CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_BOTTOM) {
- return true;
+
+ AudioAttributes attributes = info.getAttributes();
+ if (attributes == null) {
+ return false;
+ }
+
+ int actualUsage = CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attributes);
+ if (actualUsage == expectedUsage) {
+ return info.getPackageName().equals(mContext.getOpPackageName());
}
return false;
}
+ private boolean isFocusFromCarServiceBottom(AudioFocusInfo info) {
+ return checkFocusUsage(info, CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_BOTTOM);
+ }
+
private boolean isFocusFromCarProxy(AudioFocusInfo info) {
- if (info == null) {
- return false;
- }
- AudioAttributes attrib = info.getAttributes();
- if (info.getPackageName().equals(mContext.getOpPackageName()) &&
- attrib != null &&
- CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib) ==
- CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY) {
- return true;
- }
- return false;
+ return checkFocusUsage(info, CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY);
}
private boolean isFocusFromExternalRadioOrExternalSource(AudioFocusInfo info) {
if (info == null) {
return false;
}
- AudioAttributes attrib = info.getAttributes();
- if (attrib == null) {
+
+ AudioAttributes attributes = info.getAttributes();
+ if (attributes == null) {
return false;
}
- // if radio is not external, no special handling of radio is necessary.
- if (CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib) ==
- CarAudioManager.CAR_AUDIO_USAGE_RADIO && mIsRadioExternal) {
- return true;
- } else if (CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib) ==
- CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE) {
- return true;
+
+ int focusUsage = CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attributes);
+ switch (focusUsage) {
+ case CarAudioManager.CAR_AUDIO_USAGE_RADIO:
+ return mIsRadioExternal;
+ case CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE:
+ return true;
+ default:
+ return false;
}
- return false;
}
/**
@@ -929,7 +922,7 @@
* @return true if focus change was requested to car.
*/
private boolean reevaluateCarAudioFocusAndSendFocusLocked() {
- if (mTopFocusInfo == null) {
+ if (mPrimaryFocusInfo == null) {
if (mSystemSoundPhysicalStreamActive) {
return requestFocusForSystemSoundOnlyCaseLocked();
} else {
@@ -937,14 +930,14 @@
return false;
}
}
- if (mTopFocusInfo.getLossReceived() != 0) {
+ if (mPrimaryFocusInfo.getLossReceived() != 0) {
// top one got loss. This should not happen.
- Log.e(TAG_FOCUS, "Top focus holder got loss " + dumpAudioFocusInfo(mTopFocusInfo));
+ Log.e(TAG_FOCUS, "Top focus holder got loss " + dumpAudioFocusInfo(mPrimaryFocusInfo));
return false;
}
- if (isFocusFromCarServiceBottom(mTopFocusInfo) || isFocusFromCarProxy(mTopFocusInfo)) {
+ if (isFocusFromCarServiceBottom(mPrimaryFocusInfo) || isFocusFromCarProxy(mPrimaryFocusInfo)) {
// allow system sound only when car is not holding focus.
- if (mSystemSoundPhysicalStreamActive && isFocusFromCarServiceBottom(mTopFocusInfo)) {
+ if (mSystemSoundPhysicalStreamActive && isFocusFromCarServiceBottom(mPrimaryFocusInfo)) {
return requestFocusForSystemSoundOnlyCaseLocked();
}
switch (mCurrentFocusState.focusState) {
@@ -954,7 +947,7 @@
mFocusHandler.handleFocusReleaseRequest();
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS:
- doHandleFocusLossFromCar(mCurrentFocusState, mTopFocusInfo);
+ doHandleFocusLossFromCar(mCurrentFocusState, mPrimaryFocusInfo);
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT:
doHandleFocusLossTransientFromCar(mCurrentFocusState);
@@ -970,7 +963,7 @@
return false;
}
mFocusHandler.cancelFocusReleaseRequest();
- AudioAttributes attrib = mTopFocusInfo.getAttributes();
+ AudioAttributes attrib = mPrimaryFocusInfo.getAttributes();
int logicalStreamTypeForTop = CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib);
int physicalStreamTypeForTop = mAudioRoutingPolicy.getPhysicalStreamForLogicalStream(
(logicalStreamTypeForTop < CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_BOTTOM)
@@ -985,20 +978,14 @@
CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_MEDIA_MUTE) {
muteMedia = true;
}
- if (logicalStreamTypeForTop == CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL) {
- mCallActive = true;
- } else {
- mCallActive = false;
- }
// other apps having focus
int focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE;
int extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG;
int streamsToRequest = 0x1 << physicalStreamTypeForTop;
- boolean primaryIsExternal = false;
- if (isFocusFromExternalRadioOrExternalSource(mTopFocusInfo)) {
+ boolean primaryIsExternal = isFocusFromExternalRadioOrExternalSource(mPrimaryFocusInfo);
+ if (primaryIsExternal) {
streamsToRequest = 0;
mRadioOrExtSourceActive = true;
- primaryIsExternal = true;
if (fixExtSourceAndContext(
mExtSourceInfoScratch.set(primaryExtSource, primaryContext))) {
primaryExtSource = mExtSourceInfoScratch.source;
@@ -1018,7 +1005,7 @@
boolean secondaryIsExternal = false;
int secondaryContext = 0;
String secondaryExtSource = null;
- switch (mTopFocusInfo.getGainRequest()) {
+ switch (mPrimaryFocusInfo.getGainRequest()) {
case AudioManager.AUDIOFOCUS_GAIN:
focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN;
break;
@@ -1029,10 +1016,10 @@
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
focusToRequest =
AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK;
- if (mSecondFocusInfo == null) {
+ if (mSecondaryFocusInfo == null) {
break;
}
- AudioAttributes secondAttrib = mSecondFocusInfo.getAttributes();
+ AudioAttributes secondAttrib = mSecondaryFocusInfo.getAttributes();
if (secondAttrib == null) {
break;
}
@@ -1043,8 +1030,8 @@
muteMedia = true;
break;
}
- if (isFocusFromExternalRadioOrExternalSource(mSecondFocusInfo)) {
- secondaryIsExternal = true;
+ secondaryIsExternal = isFocusFromExternalRadioOrExternalSource(mSecondaryFocusInfo);
+ if (secondaryIsExternal) {
secondaryExtSource = CarAudioAttributesUtil.getExtRouting(secondAttrib);
secondaryContext = AudioHalService.logicalStreamWithExtTypeToHalContextType(
logicalStreamTypeForSecond, secondaryExtSource);
@@ -1089,7 +1076,7 @@
streamsToRequest = 0;
break;
}
- int audioContexts = 0;
+ int audioContexts = primaryContext | secondaryContext;
if (muteMedia) {
boolean addMute = true;
if (primaryIsExternal) {
@@ -1106,7 +1093,6 @@
} else {
mRadioOrExtSourceActive = false;
}
- audioContexts = primaryContext | secondaryContext;
if (addMute) {
audioContexts &= ~(AudioHalService.AUDIO_CONTEXT_RADIO_FLAG |
AudioHalService.AUDIO_CONTEXT_MUSIC_FLAG |
@@ -1116,7 +1102,7 @@
streamsToRequest &= ~(0x1 << mRadioPhysicalStream);
}
} else if (mRadioOrExtSourceActive) {
- boolean addExtFocusFlag = true;
+ boolean shouldDropSecondaryContext = false;
if (primaryIsExternal) {
int primaryExtPhysicalStreamFlag =
getPhysicalStreamFlagForExtSourceLocked(primaryExtSource);
@@ -1125,23 +1111,23 @@
getPhysicalStreamFlagForExtSourceLocked(secondaryExtSource);
if (primaryExtPhysicalStreamFlag == secondaryPhysicalStreamFlag) {
// overlap, drop secondary
- audioContexts &= ~secondaryContext;
- secondaryContext = 0;
+ shouldDropSecondaryContext = true;
secondaryExtSource = null;
}
streamsToRequest = 0;
} else { // primary only
if (streamsToRequest == primaryExtPhysicalStreamFlag) {
// cannot keep secondary
- secondaryContext = 0;
+ shouldDropSecondaryContext = true;
}
streamsToRequest &= ~primaryExtPhysicalStreamFlag;
}
}
- if (addExtFocusFlag) {
- extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG;
+ extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG;
+ if (shouldDropSecondaryContext) {
+ audioContexts &= ~secondaryContext;
+ secondaryContext = 0;
}
- audioContexts = primaryContext | secondaryContext;
} else if (streamsToRequest == 0) {
if (mSystemSoundPhysicalStreamActive) {
return requestFocusForSystemSoundOnlyCaseLocked();
@@ -1150,8 +1136,6 @@
mFocusHandler.handleFocusReleaseRequest();
return false;
}
- } else {
- audioContexts = primaryContext | secondaryContext;
}
if (mSystemSoundPhysicalStreamActive) {
boolean addSystemStream = true;
@@ -1198,7 +1182,7 @@
return true;
}
if (extSourceInfo.context == AudioHalService.AUDIO_CONTEXT_RADIO_FLAG &&
- !extSourceInfo.source.startsWith("RADIO_")) {
+ !extSourceInfo.source.startsWith(RADIO_ROUTING_SOURCE_PREFIX)) {
Log.w(CarLog.TAG_AUDIO, "Expecting Radio source:" + extSourceInfo.source);
extSourceInfo.source = mDefaultRadioRoutingType;
return true;
@@ -1207,13 +1191,7 @@
}
private int getPhysicalStreamFlagForExtSourceLocked(String extSource) {
- AudioHalService.ExtRoutingSourceInfo info = mExternalRoutingTypes.get(
- extSource);
- if (info != null) {
- return 0x1 << info.physicalStreamNumber;
- } else {
- return 0x1 << mRadioPhysicalStream;
- }
+ return 0x1 << getPhysicalStreamNumberForExtSourceLocked(extSource);
}
private int getPhysicalStreamNumberForExtSourceLocked(String extSource) {
@@ -1293,6 +1271,29 @@
}
}
+ private void doSendFocusRequestToCarLocked(int focusToRequest,
+ int streamsToRequest, int extFocus, int audioContexts) {
+ if (DBG) {
+ Log.d(TAG_FOCUS, String.format("audio focus request. focusToRequest = %d, " +
+ "streamsToRequest = 0x%x, extFocus = 0x%x, audioContexts = 0x%x",
+ focusToRequest, streamsToRequest, extFocus, audioContexts));
+ }
+ try {
+ mAudioHal.requestAudioFocusChange(
+ focusToRequest,
+ streamsToRequest,
+ extFocus,
+ audioContexts);
+ } catch (IllegalArgumentException e) {
+ // can happen when mocking ends. ignore. timeout will handle it properly.
+ }
+ try {
+ mLock.wait(mFocusResponseWaitTimeoutMs);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
private boolean sendFocusRequestToCarIfNecessaryLocked(int focusToRequest,
int streamsToRequest, int extFocus, int audioContexts, boolean forceSend) {
if (needsToSendFocusRequestLocked(focusToRequest, streamsToRequest, extFocus,
@@ -1313,17 +1314,8 @@
Log.d(TAG_FOCUS, "focus request to car:" + mLastFocusRequestToCar + " context:0x" +
Integer.toHexString(audioContexts));
}
- try {
- mAudioHal.requestAudioFocusChange(focusToRequest, streamsToRequest, extFocus,
- audioContexts);
- } catch (IllegalArgumentException e) {
- // can happen when mocking ends. ignore. timeout will handle it properly.
- }
- try {
- mLock.wait(mFocusResponseWaitTimeoutMs);
- } catch (InterruptedException e) {
- //ignore
- }
+ doSendFocusRequestToCarLocked(focusToRequest, streamsToRequest, extFocus,
+ audioContexts);
return true;
}
return false;
@@ -1384,15 +1376,15 @@
} else {
newTopInfo = mPendingFocusChanges.getFirst();
mPendingFocusChanges.clear();
- if (mTopFocusInfo != null &&
- newTopInfo.getClientId().equals(mTopFocusInfo.getClientId()) &&
- newTopInfo.getGainRequest() == mTopFocusInfo.getGainRequest() &&
+ if (mPrimaryFocusInfo != null &&
+ newTopInfo.getClientId().equals(mPrimaryFocusInfo.getClientId()) &&
+ newTopInfo.getGainRequest() == mPrimaryFocusInfo.getGainRequest() &&
isAudioAttributesSame(
- newTopInfo.getAttributes(), mTopFocusInfo.getAttributes()) &&
+ newTopInfo.getAttributes(), mPrimaryFocusInfo.getAttributes()) &&
!triggeredByStreamChange) {
if (DBG) {
Log.d(TAG_FOCUS, "doHandleAndroidFocusChange, no change in top state:" +
- dumpAudioFocusInfo(mTopFocusInfo));
+ dumpAudioFocusInfo(mPrimaryFocusInfo));
}
// already in top somehow, no need to make any change
return;
@@ -1401,14 +1393,14 @@
if (newTopInfo != null) {
if (newTopInfo.getGainRequest() ==
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) {
- mSecondFocusInfo = mTopFocusInfo;
+ mSecondaryFocusInfo = mPrimaryFocusInfo;
} else {
- mSecondFocusInfo = null;
+ mSecondaryFocusInfo = null;
}
if (DBG) {
Log.d(TAG_FOCUS, "top focus changed to:" + dumpAudioFocusInfo(newTopInfo));
}
- mTopFocusInfo = newTopInfo;
+ mPrimaryFocusInfo = newTopInfo;
}
focusRequested = handleCarFocusRequestAndResponseLocked();
}
@@ -1423,7 +1415,7 @@
if (DBG) {
if (!focusRequested) {
Log.i(TAG_FOCUS, "focus not requested for top focus:" +
- dumpAudioFocusInfo(mTopFocusInfo) + " currentState:" + mCurrentFocusState);
+ dumpAudioFocusInfo(mPrimaryFocusInfo) + " currentState:" + mCurrentFocusState);
}
}
if (focusRequested) {
@@ -1459,20 +1451,11 @@
}
mLastFocusRequestToCar = FocusRequest.STATE_RELEASE;
sent = true;
- try {
- if (mExternalRoutingHintSupported) {
- mAudioHal.setExternalRoutingSource(mExternalRoutingsForFocusRelease);
- }
- mAudioHal.requestAudioFocusChange(
- AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, 0, 0);
- } catch (IllegalArgumentException e) {
- // can happen when mocking ends. ignore. timeout will handle it properly.
+ if (mExternalRoutingHintSupported) {
+ mAudioHal.setExternalRoutingSource(mExternalRoutingsForFocusRelease);
}
- try {
- mLock.wait(mFocusResponseWaitTimeoutMs);
- } catch (InterruptedException e) {
- //ignore
- }
+ doSendFocusRequestToCarLocked(AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE,
+ 0, 0, 0);
mCurrentPrimaryAudioContext = 0;
mCurrentPrimaryPhysicalStream = 0;
if (mCarAudioContextChangeHandler != null) {
@@ -1578,9 +1561,6 @@
private class BottomAudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
@Override
public void onAudioFocusChange(int focusChange) {
- synchronized (mLock) {
- mBottomFocusState = focusChange;
- }
}
}
diff --git a/service/src/com/android/car/CarBluetoothUserService.java b/service/src/com/android/car/CarBluetoothUserService.java
index 7ee02cf..55365a0 100644
--- a/service/src/com/android/car/CarBluetoothUserService.java
+++ b/service/src/com/android/car/CarBluetoothUserService.java
@@ -31,7 +31,7 @@
public class CarBluetoothUserService extends ICarBluetoothUserService.Stub {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String TAG = "CarBluetoothUsrSvc";
private BluetoothAdapter mBluetoothAdapter = null;
private final PerUserCarService mService;
@@ -164,6 +164,36 @@
}
/**
+ * Set the priority of the given Bluetooth profile for the given remote device
+ * @param profile - Bluetooth profile
+ * @param device - remote Bluetooth device
+ * @param priority - priority to set
+ */
+ @Override
+ public void setProfilePriority(int profile, BluetoothDevice device, int priority) {
+ if (!isBluetoothConnectionProxyAvailable(profile)) {
+ Log.e(TAG, "Cannot connect to Profile. Proxy Unavailable");
+ return;
+ }
+ switch (profile) {
+ case BluetoothProfile.A2DP_SINK:
+ mBluetoothA2dpSink.setPriority(device, priority);
+ break;
+ case BluetoothProfile.HEADSET_CLIENT:
+ mBluetoothHeadsetClient.setPriority(device, priority);
+ break;
+ case BluetoothProfile.MAP_CLIENT:
+ mBluetoothMapClient.setPriority(device, priority);
+ break;
+ case BluetoothProfile.PBAP_CLIENT:
+ mBluetoothPbapClient.setPriority(device, priority);
+ break;
+ default:
+ Log.d(TAG, "Unknown Profile");
+ break;
+ }
+ }
+ /**
* All the BluetoothProfile.ServiceListeners to get the Profile Proxy objects
*/
private BluetoothProfile.ServiceListener mProfileListener =
diff --git a/service/src/com/android/car/CarDiagnosticService.java b/service/src/com/android/car/CarDiagnosticService.java
index c14a385..88e7e34 100644
--- a/service/src/com/android/car/CarDiagnosticService.java
+++ b/service/src/com/android/car/CarDiagnosticService.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
+import com.android.car.hal.DiagnosticHalService.DiagnosticCapabilities;
import com.android.car.internal.CarPermission;
import com.android.car.Listeners.ClientWithRate;
import com.android.car.hal.DiagnosticHalService;
@@ -161,10 +162,10 @@
if (event.isLiveFrame()) {
// record recent-most live frame information
setRecentmostLiveFrame(event);
- listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE);
+ listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_LIVE);
} else if (event.isFreezeFrame()) {
setRecentmostFreezeFrame(event);
- listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE);
+ listeners = mDiagnosticListeners.get(CarDiagnosticManager.FRAME_TYPE_FREEZE);
} else {
Log.w(
CarLog.TAG_DIAGNOSTIC,
@@ -198,24 +199,6 @@
processDiagnosticData(events);
}
- private List<CarDiagnosticEvent> getCachedEventsLocked(int frameType) {
- ArrayList<CarDiagnosticEvent> events = new ArrayList<>();
- switch (frameType) {
- case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
- mLiveFrameDiagnosticRecord.lock();
- events.add(mLiveFrameDiagnosticRecord.getLastEvent());
- mLiveFrameDiagnosticRecord.unlock();
- break;
- case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
- mFreezeFrameDiagnosticRecords.lock();
- mFreezeFrameDiagnosticRecords.getEvents().forEach(events::add);
- mFreezeFrameDiagnosticRecords.unlock();
- break;
- default: break;
- }
- return events;
- }
-
@Override
public boolean registerOrUpdateDiagnosticListener(int frameType, int rate,
ICarDiagnosticEventListener listener) {
@@ -242,8 +225,6 @@
}
mClients.add(diagnosticClient);
}
- // If we have a cached event for this diagnostic, send the event.
- diagnosticClient.dispatchDiagnosticUpdate(getCachedEventsLocked(frameType));
diagnosticListeners = mDiagnosticListeners.get(frameType);
if (diagnosticListeners == null) {
diagnosticListeners = new Listeners<>(rate);
@@ -306,21 +287,21 @@
return false;
}
switch (frameType) {
- case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
+ case CarDiagnosticManager.FRAME_TYPE_LIVE:
if (mLiveFrameDiagnosticRecord.isEnabled()) {
return true;
}
- if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_LIVE,
rate)) {
mLiveFrameDiagnosticRecord.enable();
return true;
}
break;
- case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
+ case CarDiagnosticManager.FRAME_TYPE_FREEZE:
if (mFreezeFrameDiagnosticRecords.isEnabled()) {
return true;
}
- if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
+ if (diagnosticHal.requestSensorStart(CarDiagnosticManager.FRAME_TYPE_FREEZE,
rate)) {
mFreezeFrameDiagnosticRecords.enable();
return true;
@@ -394,13 +375,13 @@
return;
}
switch (frameType) {
- case CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE:
+ case CarDiagnosticManager.FRAME_TYPE_LIVE:
if (mLiveFrameDiagnosticRecord.disableIfNeeded())
- diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE);
+ diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_LIVE);
break;
- case CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE:
+ case CarDiagnosticManager.FRAME_TYPE_FREEZE:
if (mFreezeFrameDiagnosticRecords.disableIfNeeded())
- diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE);
+ diagnosticHal.requestSensorStop(CarDiagnosticManager.FRAME_TYPE_FREEZE);
break;
}
}
@@ -409,6 +390,29 @@
return mDiagnosticHal;
}
+ // Expose DiagnosticCapabilities
+ public boolean isLiveFrameSupported() {
+ return getDiagnosticHal().getDiagnosticCapabilities().isLiveFrameSupported();
+ }
+
+ public boolean isFreezeFrameSupported() {
+ return getDiagnosticHal().getDiagnosticCapabilities().isFreezeFrameSupported();
+ }
+
+ public boolean isFreezeFrameTimestampSupported() {
+ DiagnosticCapabilities diagnosticCapabilities =
+ getDiagnosticHal().getDiagnosticCapabilities();
+ return diagnosticCapabilities.isFreezeFrameInfoSupported() &&
+ diagnosticCapabilities.isFreezeFrameSupported();
+ }
+
+ public boolean isFreezeFrameClearSupported() {
+ DiagnosticCapabilities diagnosticCapabilities =
+ getDiagnosticHal().getDiagnosticCapabilities();
+ return diagnosticCapabilities.isFreezeFrameClearSupported() &&
+ diagnosticCapabilities.isFreezeFrameSupported();
+ }
+
// ICarDiagnostic implementations
@Override
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 2d65c0a..298290b 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -16,7 +16,6 @@
package com.android.car;
import static android.os.SystemClock.elapsedRealtime;
-import static com.android.car.internal.FeatureConfiguration.ENABLE_VEHICLE_HAL_V2_1;
import android.annotation.Nullable;
import android.app.Service;
@@ -178,7 +177,7 @@
try {
boolean anyVersion = interfaceName == null || interfaceName.isEmpty();
IVehicle vehicle = null;
- if (ENABLE_VEHICLE_HAL_V2_1 && (anyVersion || IVHAL_21.equals(interfaceName))) {
+ if (anyVersion || IVHAL_21.equals(interfaceName)) {
vehicle = android.hardware.automotive.vehicle.V2_1.IVehicle
.getService();
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index a53771f..09e1226 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -116,10 +116,7 @@
mVmsSubscriberService = new VmsSubscriberService(serviceContext, mHal.getVmsHal());
mVmsPublisherService = new VmsPublisherService(serviceContext, mHal.getVmsHal());
}
- if (FeatureConfiguration.ENABLE_DIAGNOSTIC) {
- mCarDiagnosticService = new CarDiagnosticService(serviceContext,
- mHal.getDiagnosticHal());
- }
+ mCarDiagnosticService = new CarDiagnosticService(serviceContext, mHal.getDiagnosticHal());
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>(Arrays.asList(
@@ -141,15 +138,13 @@
mSystemStateControllerService,
mCarVendorExtensionService,
mCarBluetoothService,
+ mCarDiagnosticService,
mPerUserCarServiceHelper
));
if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
allServices.add(mVmsSubscriberService);
allServices.add(mVmsPublisherService);
}
- if (FeatureConfiguration.ENABLE_DIAGNOSTIC) {
- allServices.add(mCarDiagnosticService);
- }
mAllServices = allServices.toArray(new CarServiceBase[0]);
}
@@ -192,11 +187,8 @@
assertCabinPermission(mContext);
return mCarCabinService;
case Car.DIAGNOSTIC_SERVICE:
- FeatureUtil.assertFeature(FeatureConfiguration.ENABLE_DIAGNOSTIC);
- if (FeatureConfiguration.ENABLE_DIAGNOSTIC) {
- assertAnyDiagnosticPermission(mContext);
- return mCarDiagnosticService;
- }
+ assertAnyDiagnosticPermission(mContext);
+ return mCarDiagnosticService;
case Car.HVAC_SERVICE:
assertHvacPermission(mContext);
return mCarHvacService;
diff --git a/service/src/com/android/car/VmsLayersAvailability.java b/service/src/com/android/car/VmsLayersAvailability.java
index 5f5ac30..d6e89f2 100644
--- a/service/src/com/android/car/VmsLayersAvailability.java
+++ b/service/src/com/android/car/VmsLayersAvailability.java
@@ -83,7 +83,7 @@
/**
* Returns a collection of all the layers which may be published.
*/
- public Collection<VmsLayer> getAvailableLayers() {
+ public Set<VmsLayer> getAvailableLayers() {
synchronized (mLock) {
return mAvailableLayers;
}
@@ -93,7 +93,7 @@
* Returns a collection of all the layers which publishers could have published if the
* dependencies were satisfied.
*/
- public Collection<VmsLayer> getUnavailableLayers() {
+ public Set<VmsLayer> getUnavailableLayers() {
synchronized (mLock) {
return mUnavailableLayers;
}
diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java
index 8f7fba3..37d265a 100644
--- a/service/src/com/android/car/VmsPublisherService.java
+++ b/service/src/com/android/car/VmsPublisherService.java
@@ -27,9 +27,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import com.android.car.hal.VmsHalService;
@@ -37,7 +40,9 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -56,7 +61,7 @@
private final Context mContext;
private final VmsHalService mHal;
private final VmsPublisherManager mPublisherManager;
- private final Map<IBinder, VmsLayersOffering> mRawOffering = new HashMap<>();
+ private Set<String> mSafePermissions;
public VmsPublisherService(Context context, VmsHalService hal) {
mContext = context;
@@ -68,6 +73,9 @@
@Override
public void init() {
mHal.addPublisherListener(this);
+ // Load permissions that can be granted to publishers.
+ mSafePermissions = new HashSet<>(
+ Arrays.asList(mContext.getResources().getStringArray(R.array.vmsSafePermissions)));
// Launch publishers.
String[] publisherNames = mContext.getResources().getStringArray(
R.array.vmsPublisherClients);
@@ -96,12 +104,7 @@
@Override
public void setLayersOffering(IBinder token, VmsLayersOffering offering) {
- // Store the raw dependencies
- mRawOffering.put(token, offering);
-
- //TODO(asafro): Calculate the new available layers
-
- //TODO(asafro): Notify the subscribers that there is a change in availability
+ mHal.setPublisherLayersOffering(token, offering);
}
// Implements IVmsPublisherService interface.
@@ -141,6 +144,12 @@
return mHal.getSubscriptionState();
}
+ @Override
+ public int getPublisherStaticId(byte[] publisherInfo) {
+ ICarImpl.assertVmsPublisherPermission(mContext);
+ return mHal.getPublisherStaticId(publisherInfo);
+ }
+
// Implements VmsHalListener interface
/**
* This method is only invoked by VmsHalService.notifyPublishers which is synchronized.
@@ -193,11 +202,12 @@
// Already registered, nothing to do.
return;
}
+ grantPermissions(name);
Intent intent = new Intent();
intent.setComponent(name);
PublisherConnection connection = new PublisherConnection();
- if (publisherService.mContext.bindService(intent, connection,
- Context.BIND_AUTO_CREATE)) {
+ if (publisherService.mContext.bindServiceAsUser(intent, connection,
+ Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
mPublisherConnectionMap.put(publisherName, connection);
} else {
Log.e(TAG, "unable to bind to: " + publisherName);
@@ -250,6 +260,39 @@
mPublisherMap.clear();
}
+ private void grantPermissions(ComponentName component) {
+ VmsPublisherService publisherService = mPublisherService.get();
+ if (publisherService == null) return;
+ final PackageManager packageManager = publisherService.mContext.getPackageManager();
+ final String packageName = component.getPackageName();
+ PackageInfo packageInfo;
+ try {
+ packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Error getting package info for " + packageName, e);
+ return;
+ }
+ if (packageInfo.requestedPermissions == null) return;
+ for (String permission : packageInfo.requestedPermissions) {
+ if (!publisherService.mSafePermissions.contains(permission)) {
+ continue;
+ }
+ if (packageManager.checkPermission(permission, packageName)
+ == PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+ try {
+ packageManager.grantRuntimePermission(packageName, permission,
+ UserHandle.SYSTEM);
+ Log.d(TAG, "Permission " + permission + " granted to " + packageName);
+ } catch (SecurityException | IllegalArgumentException e) {
+ Log.e(TAG, "Error while trying to grant " + permission + " to " + packageName,
+ e);
+ }
+ }
+ }
+
class PublisherConnection implements ServiceConnection {
private final IBinder mToken = new Binder();
diff --git a/service/src/com/android/car/VmsPublishersInfo.java b/service/src/com/android/car/VmsPublishersInfo.java
new file mode 100644
index 0000000..04ee82f
--- /dev/null
+++ b/service/src/com/android/car/VmsPublishersInfo.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 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.android.car;
+
+
+import android.car.annotation.FutureFeature;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import com.android.internal.annotations.GuardedBy;
+import android.util.Log;
+
+@FutureFeature
+public class VmsPublishersInfo {
+ private static final String TAG = "VmsPublishersInfo";
+ private static final boolean DBG = true;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final Map<InfoWrapper, Integer> mPublishersIds = new HashMap();
+ @GuardedBy("mLock")
+ private final Map<Integer, byte[]> mPublishersInfo = new HashMap();
+
+ private static class InfoWrapper {
+ private final byte[] mInfo;
+
+ public InfoWrapper(byte[] info) {
+ mInfo = info;
+ }
+
+ public byte[] getInfo() {
+ return mInfo;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof InfoWrapper)) {
+ return false;
+ }
+ InfoWrapper p = (InfoWrapper) o;
+ return Arrays.equals(this.mInfo, p.mInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mInfo);
+ }
+ }
+
+ /**
+ * Returns the ID associated with the publisher info. When called for the first time for a
+ * publisher info will store the info and assign an ID
+ */
+ public int getIdForInfo(byte[] publisherInfo) {
+ Integer publisherId;
+ InfoWrapper wrappedPublisherInfo = new InfoWrapper(publisherInfo);
+ synchronized (mLock) {
+ maybeAddPublisherInfoLocked(wrappedPublisherInfo);
+ publisherId = mPublishersIds.get(wrappedPublisherInfo);
+ }
+ if (DBG) {
+ Log.i(TAG, "Publisher ID is: " + publisherId);
+ }
+ return publisherId;
+ }
+
+ public byte[] getPublisherInfo(int publisherId) {
+ synchronized (mLock) {
+ return mPublishersInfo.get(publisherId).clone();
+ }
+ }
+
+ private void maybeAddPublisherInfoLocked(InfoWrapper wrappedPublisherInfo) {
+ if (!mPublishersIds.containsKey(wrappedPublisherInfo)) {
+ // Assign ID to the info
+ Integer publisherId = mPublishersIds.size();
+
+ mPublishersIds.put(wrappedPublisherInfo, publisherId);
+ mPublishersInfo.put(publisherId, wrappedPublisherInfo.getInfo());
+ }
+ }
+}
+
diff --git a/service/src/com/android/car/VmsRouting.java b/service/src/com/android/car/VmsRouting.java
index fc2cbac..2829cc0 100644
--- a/service/src/com/android/car/VmsRouting.java
+++ b/service/src/com/android/car/VmsRouting.java
@@ -20,16 +20,14 @@
import android.car.vms.IVmsSubscriberClient;
import android.car.vms.VmsLayer;
import android.car.vms.VmsSubscriptionState;
-
+import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import com.android.internal.annotations.GuardedBy;
-
/**
* Manages all the VMS subscriptions:
* + Subscriptions to data messages of individual layer + version.
@@ -62,6 +60,7 @@
* @param layer the layer subscribing to.
*/
public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
+ //TODO(b/36902947): revise if need to sync, and return value.
synchronized (mLock) {
++mSequenceNumber;
// Get or create the list of listeners for layer and version.
@@ -142,8 +141,8 @@
}
/**
- * Returns all the listeners for a layer and version. This include the subscribers which
- * explicitly subscribed to this layer and version and the promiscuous subscribers.
+ * Returns a list with all the listeners for a layer and version. This include the subscribers
+ * which explicitly subscribed to this layer and version and the promiscuous subscribers.
*
* @param layer to get listeners to.
* @return a list of the listeners.
@@ -162,6 +161,21 @@
}
/**
+ * Returns a list with all the listeners.
+ */
+ public Set<IVmsSubscriberClient> getAllListeners() {
+ Set<IVmsSubscriberClient> listeners = new HashSet<>();
+ synchronized (mLock) {
+ for (VmsLayer layer : mLayerSubscriptions.keySet()) {
+ listeners.addAll(mLayerSubscriptions.get(layer));
+ }
+ // Add the promiscuous subscribers.
+ listeners.addAll(mPromiscuousSubscribers);
+ }
+ return listeners;
+ }
+
+ /**
* Checks if a listener is subscribed to any messages.
* @param listener that may have subscription.
* @return true if the listener uis subscribed to messages.
diff --git a/service/src/com/android/car/VmsSubscriberService.java b/service/src/com/android/car/VmsSubscriberService.java
index 97ed27f..fc0a885 100644
--- a/service/src/com/android/car/VmsSubscriberService.java
+++ b/service/src/com/android/car/VmsSubscriberService.java
@@ -262,6 +262,13 @@
}
@Override
+ public byte[] getPublisherInfo(int publisherId) {
+ synchronized (mSubscriberServiceLock) {
+ return mHal.getPublisherInfo(publisherId);
+ }
+ }
+
+ @Override
public List<VmsLayer> getAvailableLayers() {
//TODO(asafro): return the list of available layers once logic is implemented.
return Collections.emptyList();
@@ -269,18 +276,13 @@
// Implements VmsHalSubscriberListener interface
@Override
- public void onChange(VmsLayer layer, byte[] payload) {
+ public void onDataMessage(VmsLayer layer, byte[] payload) {
if(DBG) {
Log.d(TAG, "Publishing a message for layer: " + layer);
}
Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
- // If there are no listeners we're done.
- if ((listeners == null)) {
- return;
- }
-
for (IVmsSubscriberClient subscriber : listeners) {
try {
subscriber.onVmsMessageReceived(layer, payload);
@@ -290,6 +292,24 @@
Log.e(TAG, "onVmsMessageReceived calling failed: ", e);
}
}
+ }
+ @Override
+ public void onLayersAvaiabilityChange(List<VmsLayer> availableLayers) {
+ if(DBG) {
+ Log.d(TAG, "Publishing layers availability change: " + availableLayers);
+ }
+
+ Set<IVmsSubscriberClient> listeners = mHal.getAllListeners();
+
+ for (IVmsSubscriberClient subscriber : listeners) {
+ try {
+ subscriber.onLayersAvailabilityChange(availableLayers);
+ } catch (RemoteException e) {
+ // If we could not send a record, its likely the connection snapped. Let the binder
+ // death handle the situation.
+ Log.e(TAG, "onLayersAvailabilityChange calling failed: ", e);
+ }
+ }
}
}
diff --git a/service/src/com/android/car/hal/DiagnosticHalService.java b/service/src/com/android/car/hal/DiagnosticHalService.java
index 84e3678..98a3b3d 100644
--- a/service/src/com/android/car/hal/DiagnosticHalService.java
+++ b/service/src/com/android/car/hal/DiagnosticHalService.java
@@ -95,13 +95,13 @@
mVehiclePropertyToConfig.put(propConfig.prop, propConfig);
Log.i(CarLog.TAG_DIAGNOSTIC, String.format("configArray for OBD2_LIVE_FRAME is %s",
propConfig.configArray));
- return CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE;
+ return CarDiagnosticManager.FRAME_TYPE_LIVE;
case VehicleProperty.OBD2_FREEZE_FRAME:
mDiagnosticCapabilities.setSupported(propConfig.prop);
mVehiclePropertyToConfig.put(propConfig.prop, propConfig);
Log.i(CarLog.TAG_DIAGNOSTIC, String.format("configArray for OBD2_FREEZE_FRAME is %s",
propConfig.configArray));
- return CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE;
+ return CarDiagnosticManager.FRAME_TYPE_FREEZE;
case VehicleProperty.OBD2_FREEZE_FRAME_INFO:
mDiagnosticCapabilities.setSupported(propConfig.prop);
return propConfig.prop;
@@ -310,7 +310,7 @@
VehiclePropValue value = mHal.get(builder.build());
return createCarDiagnosticEvent(value);
} catch (PropertyTimeoutException e) {
- Log.e(CarLog.TAG_DIAGNOSTIC, "timeout trying to read OBD2_DTC_INFO");
+ Log.e(CarLog.TAG_DIAGNOSTIC, "timeout trying to read OBD2_FREEZE_FRAME");
return null;
} catch (IllegalArgumentException e) {
Log.e(CarLog.TAG_DIAGNOSTIC,
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index 5bf4e64..c60d16d 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -111,9 +111,7 @@
if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
mVmsHal = new VmsHalService(this);
}
- if(FeatureConfiguration.ENABLE_DIAGNOSTIC) {
- mDiagnosticHal = new DiagnosticHalService(this);
- }
+ mDiagnosticHal = new DiagnosticHalService(this);
mAllServices.addAll(Arrays.asList(mPowerHal,
mSensorHal,
mInfoHal,
@@ -122,13 +120,11 @@
mRadioHal,
mHvacHal,
mInputHal,
- mVendorExtensionHal));
+ mVendorExtensionHal,
+ mDiagnosticHal));
if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
mAllServices.add(mVmsHal);
}
- if(FeatureConfiguration.ENABLE_DIAGNOSTIC) {
- mAllServices.add(mDiagnosticHal);
- }
mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/);
}
@@ -148,14 +144,11 @@
mHvacHal = hvacHal;
mInputHal = null;
mVendorExtensionHal = null;
+ mDiagnosticHal = null;
if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
- // TODO(antoniocortes): do we need a test version of VmsHalService?
mVmsHal = null;
}
- if(FeatureConfiguration.ENABLE_DIAGNOSTIC) {
- mDiagnosticHal = null;
- }
mHalClient = halClient;
}
@@ -177,7 +170,6 @@
mHvacHal = hvacHal;
mInputHal = null;
mVendorExtensionHal = null;
- // TODO(antoniocortes): do we need a test version of VmsHalService?
mVmsHal = null;
mHalClient = halClient;
mDiagnosticHal = diagnosticHal;
diff --git a/service/src/com/android/car/hal/VmsHalService.java b/service/src/com/android/car/hal/VmsHalService.java
index c23f36a..8ab5427 100644
--- a/service/src/com/android/car/hal/VmsHalService.java
+++ b/service/src/com/android/car/hal/VmsHalService.java
@@ -22,24 +22,33 @@
import android.car.annotation.FutureFeature;
import android.car.vms.IVmsSubscriberClient;
import android.car.vms.VmsLayer;
+import android.car.vms.VmsLayerDependency;
+import android.car.vms.VmsLayersOffering;
import android.car.vms.VmsSubscriptionState;
import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
-import android.os.SystemClock;
+import android.hardware.automotive.vehicle.V2_1.VmsOfferingMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
+import android.os.Binder;
+import android.os.IBinder;
import android.util.Log;
import com.android.car.CarLog;
+import com.android.car.VmsLayersAvailability;
+import com.android.car.VmsPublishersInfo;
import com.android.car.VmsRouting;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -49,25 +58,27 @@
*/
@FutureFeature
public class VmsHalService extends HalServiceBase {
+
private static final boolean DBG = true;
private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE;
private static final String TAG = "VmsHalService";
- private static final Set<Integer> SUPPORTED_MESSAGE_TYPES =
- new HashSet<Integer>(
- Arrays.asList(
- VmsMessageType.SUBSCRIBE,
- VmsMessageType.UNSUBSCRIBE,
- VmsMessageType.DATA));
private boolean mIsSupported = false;
private CopyOnWriteArrayList<VmsHalPublisherListener> mPublisherListeners =
new CopyOnWriteArrayList<>();
private CopyOnWriteArrayList<VmsHalSubscriberListener> mSubscriberListeners =
new CopyOnWriteArrayList<>();
+
+ private final IBinder mHalPublisherToken = new Binder();
private final VehicleHal mVehicleHal;
- @GuardedBy("mLock")
- private VmsRouting mRouting = new VmsRouting();
+
private final Object mLock = new Object();
+ private final VmsRouting mRouting = new VmsRouting();
+ @GuardedBy("mLock")
+ private final Map<IBinder, VmsLayersOffering> mOfferings = new HashMap<>();
+ @GuardedBy("mLock")
+ private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability();
+ private final VmsPublishersInfo mPublishersInfo = new VmsPublishersInfo();
/**
* The VmsPublisherService implements this interface to receive data from the HAL.
@@ -80,7 +91,11 @@
* The VmsSubscriberService implements this interface to receive data from the HAL.
*/
public interface VmsHalSubscriberListener {
- void onChange(VmsLayer layer, byte[] payload);
+ // Notify listener on a data Message.
+ void onDataMessage(VmsLayer layer, byte[] payload);
+
+ // Notify listener on a change in available layers.
+ void onLayersAvaiabilityChange(List<VmsLayer> availableLayers);
}
/**
@@ -110,21 +125,21 @@
}
public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
+ boolean firstSubscriptionForLayer = false;
synchronized (mLock) {
// Check if publishers need to be notified about this change in subscriptions.
- boolean firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
+ firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
// Add the listeners subscription to the layer
mRouting.addSubscription(listener, layer);
-
- // Notify the publishers
- if (firstSubscriptionForLayer) {
- notifyPublishers(layer, true);
- }
+ }
+ if (firstSubscriptionForLayer) {
+ notifyPublishers(layer, true);
}
}
public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
+ boolean layerHasSubscribers = true;
synchronized (mLock) {
if (!mRouting.hasLayerSubscriptions(layer)) {
Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
@@ -135,12 +150,10 @@
mRouting.removeSubscription(listener, layer);
// Check if publishers need to be notified about this change in subscriptions.
- boolean layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
-
- // Notify the publishers
- if (!layerHasSubscribers) {
- notifyPublishers(layer, false);
- }
+ layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
+ }
+ if (!layerHasSubscribers) {
+ notifyPublishers(layer, false);
}
}
@@ -168,6 +181,12 @@
}
}
+ public Set<IVmsSubscriberClient> getAllListeners() {
+ synchronized (mLock) {
+ return mRouting.getAllListeners();
+ }
+ }
+
public boolean isHalSubscribed(VmsLayer layer) {
synchronized (mLock) {
return mRouting.isHalSubscribed(layer);
@@ -180,21 +199,46 @@
}
}
+ /**
+ * Assigns an idempotent ID for publisherInfo and stores it. The idempotency in this case means
+ * that the same publisherInfo will always, within a trip of the vehicle, return the same ID.
+ * The publisherInfo should be static for a binary and should only change as part of a software
+ * update. The publisherInfo is a serialized proto message which VMS clients can interpret.
+ */
+ public int getPublisherStaticId(byte[] publisherInfo) {
+ if (DBG) {
+ Log.i(TAG, "Getting publisher static ID");
+ }
+ synchronized (mLock) {
+ return mPublishersInfo.getIdForInfo(publisherInfo);
+ }
+ }
+
+ public byte[] getPublisherInfo(int publisherId) {
+ if (DBG) {
+ Log.i(TAG, "Getting information for publisher ID: " + publisherId);
+ }
+ synchronized (mLock) {
+ return mPublishersInfo.getPublisherInfo(publisherId);
+ }
+ }
+
public void addHalSubscription(VmsLayer layer) {
+ boolean firstSubscriptionForLayer = true;
synchronized (mLock) {
// Check if publishers need to be notified about this change in subscriptions.
- boolean firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
+ firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
// Add the listeners subscription to the layer
mRouting.addHalSubscription(layer);
-
- if (firstSubscriptionForLayer) {
- notifyPublishers(layer, true);
- }
+ }
+ if (firstSubscriptionForLayer) {
+ notifyPublishers(layer, true);
}
}
public void removeHalSubscription(VmsLayer layer) {
+ boolean layerHasSubscribers = true;
synchronized (mLock) {
if (!mRouting.hasLayerSubscriptions(layer)) {
Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
@@ -205,12 +249,10 @@
mRouting.removeHalSubscription(layer);
// Check if publishers need to be notified about this change in subscriptions.
- boolean layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
-
- // Notify the publishers
- if (!layerHasSubscribers) {
- notifyPublishers(layer, false);
- }
+ layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
+ }
+ if (!layerHasSubscribers) {
+ notifyPublishers(layer, false);
}
}
@@ -220,6 +262,22 @@
}
}
+ public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering){
+ Set<VmsLayer> availableLayers = Collections.EMPTY_SET;
+ synchronized (mLock) {
+ updateOffering(publisherToken, offering);
+ availableLayers = mAvailableLayers.getAvailableLayers();
+ }
+ notifySubscribers(availableLayers);
+ }
+
+ public Set<VmsLayer> getAvailableLayers() {
+ //TODO(b/36872877): wrap available layers in VmsAvailabilityState similar to VmsSubscriptionState.
+ synchronized (mLock) {
+ return mAvailableLayers.getAvailableLayers();
+ }
+ }
+
/**
* Notify all the publishers and the HAL on subscription changes regardless of who triggered
* the change.
@@ -227,18 +285,31 @@
* @param layer layer which is being subscribed to or unsubscribed from.
* @param hasSubscribers indicates if the notification is for subscription or unsubscription.
*/
- public void notifyPublishers(VmsLayer layer, boolean hasSubscribers) {
- synchronized (mLock) {
- // notify the HAL
- setSubscriptionRequest(layer, hasSubscribers);
+ private void notifyPublishers(VmsLayer layer, boolean hasSubscribers) {
+ // notify the HAL
+ setSubscriptionRequest(layer, hasSubscribers);
- // Notify the App publishers
- for (VmsHalPublisherListener listener : mPublisherListeners) {
- // Besides the list of layers, also a timestamp is provided to the clients.
- // They should ignore any notification with a timestamp that is older than the most
- // recent timestamp they have seen.
- listener.onChange(getSubscriptionState());
- }
+ // Notify the App publishers
+ for (VmsHalPublisherListener listener : mPublisherListeners) {
+ // Besides the list of layers, also a timestamp is provided to the clients.
+ // They should ignore any notification with a timestamp that is older than the most
+ // recent timestamp they have seen.
+ listener.onChange(getSubscriptionState());
+ }
+ }
+
+ /**
+ * Notify all the subscribers and the HAL on layers availability change.
+ *
+ * @param availableLayers the layers which publishers claim they made publish.
+ */
+ private void notifySubscribers(Set<VmsLayer> availableLayers) {
+ // notify the HAL
+ setAvailableLayers(availableLayers);
+
+ // Notify the App subscribers
+ for (VmsHalSubscriberListener listener : mSubscriberListeners) {
+ listener.onLayersAvaiabilityChange(new ArrayList<>(availableLayers));
}
}
@@ -281,6 +352,10 @@
return taken;
}
+ /**
+ * Consumes/produces HAL messages. The format of these messages is defined in:
+ * hardware/interfaces/automotive/vehicle/2.1/types.hal
+ */
@Override
public void handleHalEvents(List<VehiclePropValue> values) {
if (DBG) {
@@ -288,42 +363,206 @@
}
for (VehiclePropValue v : values) {
ArrayList<Integer> vec = v.value.int32Values;
- int messageType = vec.get(VmsMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
- int layerId = vec.get(VmsMessageIntegerValuesIndex.VMS_LAYER_ID);
- int layerVersion = vec.get(VmsMessageIntegerValuesIndex.VMS_LAYER_VERSION);
-
- // Check if message type is supported.
- if (!SUPPORTED_MESSAGE_TYPES.contains(messageType)) {
- throw new IllegalArgumentException("Unexpected message type. " +
- "Expecting: " + SUPPORTED_MESSAGE_TYPES +
- ". Got: " + messageType);
-
- }
+ int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
if (DBG) {
- Log.d(TAG,
- "Received message for Type: " + messageType +
- " Layer Id: " + layerId +
- "Version: " + layerVersion);
+ Log.d(TAG, "Handling VMS message type: " + messageType);
}
- // This is a data message intended for subscribers.
- if (messageType == VmsMessageType.DATA) {
- // Get the payload.
- byte[] payload = toByteArray(v.value.bytes);
-
- // Send the message.
- for (VmsHalSubscriberListener listener : mSubscriberListeners) {
- listener.onChange(new VmsLayer(layerId, layerVersion), payload);
- }
- } else if (messageType == VmsMessageType.SUBSCRIBE) {
- addHalSubscription(new VmsLayer(layerId, layerVersion));
- } else {
- // messageType == VmsMessageType.UNSUBSCRIBE
- removeHalSubscription(new VmsLayer(layerId, layerVersion));
+ switch(messageType) {
+ case VmsMessageType.DATA:
+ handleDataEvent(vec, toByteArray(v.value.bytes));
+ break;
+ case VmsMessageType.SUBSCRIBE:
+ handleSubscribeEvent(vec);
+ break;
+ case VmsMessageType.UNSUBSCRIBE:
+ handleUnsubscribeEvent(vec);
+ break;
+ case VmsMessageType.OFFERING:
+ handleOfferingEvent(vec);
+ break;
+ case VmsMessageType.AVAILABILITY_REQUEST:
+ handleAvailabilityEvent();
+ break;
+ case VmsMessageType.SUBSCRIPTION_REQUEST:
+ handleSubscriptionRequestEvent();
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message type: " + messageType);
}
}
}
+ /**
+ * Data message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Layer id.
+ * <li>Layer version.
+ * <li>Payload.
+ * </ul>
+ */
+ private void handleDataEvent(List<Integer> integerValues, byte[] payload) {
+ int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
+ int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ if (DBG) {
+ Log.d(TAG,
+ "Handling a data event for Layer Id: " + layerId +
+ " Version: " + layerVersion);
+ }
+
+ // Send the message.
+ for (VmsHalSubscriberListener listener : mSubscriberListeners) {
+ listener.onDataMessage(new VmsLayer(layerId, layerVersion), payload);
+ }
+ }
+
+ /**
+ * Subscribe message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Layer id.
+ * <li>Layer version.
+ * </ul>
+ */
+ private void handleSubscribeEvent(List<Integer> integerValues) {
+ int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
+ int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ if (DBG) {
+ Log.d(TAG,
+ "Handling a subscribe event for Layer Id: " + layerId +
+ " Version: " + layerVersion);
+ }
+ addHalSubscription(new VmsLayer(layerId, layerVersion));
+ }
+
+ /**
+ * Unsubscribe message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Layer id.
+ * <li>Layer version.
+ * </ul>
+ */
+ private void handleUnsubscribeEvent(List<Integer> integerValues) {
+ int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
+ int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ if (DBG) {
+ Log.d(TAG,
+ "Handling an unsubscribe event for Layer Id: " + layerId +
+ " Version: " + layerVersion);
+ }
+ removeHalSubscription(new VmsLayer(layerId, layerVersion));
+ }
+
+ /**
+ * Offering message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Number of offerings.
+ * <li>Each offering consists of:
+ * <ul>
+ * <li>Layer id.
+ * <li>Layer version.
+ * <li>Number of layer dependencies.
+ * <li>Layer type/version pairs.
+ * </ul>
+ * </ul>
+ */
+ private void handleOfferingEvent(List<Integer> integerValues) {
+ int numLayersDependencies =
+ integerValues.get(VmsOfferingMessageIntegerValuesIndex.VMS_NUMBER_OF_LAYERS_DEPENDENCIES);
+ int idx = VmsOfferingMessageIntegerValuesIndex.FIRST_DEPENDENCIES_INDEX;
+
+ List<VmsLayerDependency> offeredLayers = new ArrayList<>();
+
+ // An offering is layerId, LayerVersion, NumDeps, <LayerId, LayerVersion> X NumDeps.
+ for (int i = 0; i < numLayersDependencies; i++) {
+ int layerId = integerValues.get(idx++);
+ int layerVersion = integerValues.get(idx++);
+ VmsLayer offeredLayer = new VmsLayer(layerId, layerVersion);
+
+ int numDependenciesForLayer = integerValues.get(idx++);
+ if (numDependenciesForLayer == 0) {
+ offeredLayers.add(new VmsLayerDependency(offeredLayer));
+ } else {
+ Set<VmsLayer> dependencies = new HashSet<>();
+
+ for (int j = 0; j < numDependenciesForLayer; j++) {
+ int dependantLayerId = integerValues.get(idx++);
+ int dependantLayerVersion = integerValues.get(idx++);
+
+ VmsLayer dependantLayer = new VmsLayer(dependantLayerId, dependantLayerVersion);
+ dependencies.add(dependantLayer);
+ }
+ offeredLayers.add(new VmsLayerDependency(offeredLayer, dependencies));
+ }
+ }
+ // Store the HAL offering.
+ VmsLayersOffering offering = new VmsLayersOffering(offeredLayers);
+ synchronized (mLock) {
+ updateOffering(mHalPublisherToken, offering);
+ }
+ }
+
+ /**
+ * Availability message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Number of layers.
+ * <li>Layer type/version pairs.
+ * </ul>
+ */
+ private void handleAvailabilityEvent() {
+ synchronized (mLock) {
+ Collection<VmsLayer> availableLayers = mAvailableLayers.getAvailableLayers();
+ VehiclePropValue vehiclePropertyValue = toVehiclePropValue(
+ VmsMessageType.AVAILABILITY_RESPONSE, availableLayers);
+ setPropertyValue(vehiclePropertyValue);
+ }
+ }
+
+ /**
+ * VmsSubscriptionRequestFormat:
+ * <ul>
+ * <li>Message type.
+ * </ul>
+ *
+ * VmsSubscriptionResponseFormat:
+ * <ul>
+ * <li>Message type.
+ * <li>Sequence number.
+ * <li>Number of layers.
+ * <li>Layer type/version pairs.
+ * </ul>
+ */
+ private void handleSubscriptionRequestEvent() {
+ VmsSubscriptionState subscription = getSubscriptionState();
+ VehiclePropValue vehicleProp = toVehiclePropValue(VmsMessageType.SUBSCRIPTION_RESPONSE);
+ VehiclePropValue.RawValue v = vehicleProp.value;
+ v.int32Values.add(subscription.getSequenceNumber());
+ List<VmsLayer> layers = subscription.getLayers();
+ v.int32Values.add(layers.size());
+ for (VmsLayer layer : layers) {
+ v.int32Values.add(layer.getId());
+ v.int32Values.add(layer.getVersion());
+ }
+ setPropertyValue(vehicleProp);
+ }
+
+ private void updateOffering(IBinder publisherToken, VmsLayersOffering offering) {
+ Set<VmsLayer> availableLayers = Collections.EMPTY_SET;
+ synchronized (mLock) {
+ mOfferings.put(publisherToken, offering);
+
+ // Update layers availability.
+ mAvailableLayers.setPublishersOffering(mOfferings.values());
+
+ availableLayers = mAvailableLayers.getAvailableLayers();
+ }
+ notifySubscribers(availableLayers);
+ }
+
@Override
public void dump(PrintWriter writer) {
writer.println(TAG);
@@ -339,14 +578,22 @@
*/
public boolean setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers) {
VehiclePropValue vehiclePropertyValue = toVehiclePropValue(
- hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer);
+ hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer);
return setPropertyValue(vehiclePropertyValue);
}
public boolean setDataMessage(VmsLayer layer, byte[] payload) {
VehiclePropValue vehiclePropertyValue = toVehiclePropValue(VmsMessageType.DATA,
- layer,
- payload);
+ layer,
+ payload);
+ return setPropertyValue(vehiclePropertyValue);
+ }
+
+ public boolean setAvailableLayers(Collection<VmsLayer> availableLayers) {
+ VehiclePropValue vehiclePropertyValue =
+ toVehiclePropValue(VmsMessageType.AVAILABILITY_RESPONSE,
+ availableLayers);
+
return setPropertyValue(vehiclePropertyValue);
}
@@ -361,13 +608,20 @@
}
/** Creates a {@link VehiclePropValue} */
- private static VehiclePropValue toVehiclePropValue(int messageType, VmsLayer layer) {
+ private static VehiclePropValue toVehiclePropValue(int messageType) {
VehiclePropValue vehicleProp = new VehiclePropValue();
vehicleProp.prop = HAL_PROPERTY_ID;
vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_NONE;
VehiclePropValue.RawValue v = vehicleProp.value;
v.int32Values.add(messageType);
+ return vehicleProp;
+ }
+
+ /** Creates a {@link VehiclePropValue} */
+ private static VehiclePropValue toVehiclePropValue(int messageType, VmsLayer layer) {
+ VehiclePropValue vehicleProp = toVehiclePropValue(messageType);
+ VehiclePropValue.RawValue v = vehicleProp.value;
v.int32Values.add(layer.getId());
v.int32Values.add(layer.getVersion());
return vehicleProp;
@@ -375,8 +629,8 @@
/** Creates a {@link VehiclePropValue} with payload */
private static VehiclePropValue toVehiclePropValue(int messageType,
- VmsLayer layer,
- byte[] payload) {
+ VmsLayer layer,
+ byte[] payload) {
VehiclePropValue vehicleProp = toVehiclePropValue(messageType, layer);
VehiclePropValue.RawValue v = vehicleProp.value;
v.bytes.ensureCapacity(payload.length);
@@ -385,4 +639,18 @@
}
return vehicleProp;
}
-}
\ No newline at end of file
+
+ /** Creates a {@link VehiclePropValue} with payload */
+ private static VehiclePropValue toVehiclePropValue(int messageType,
+ Collection<VmsLayer> layers) {
+ VehiclePropValue vehicleProp = toVehiclePropValue(messageType);
+ VehiclePropValue.RawValue v = vehicleProp.value;
+ int numLayers = layers.size();
+ v.int32Values.add(numLayers);
+ for (VmsLayer layer : layers) {
+ v.int32Values.add(layer.getId());
+ v.int32Values.add(layer.getVersion());
+ }
+ return vehicleProp;
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/diagnostic.xml b/tests/EmbeddedKitchenSinkApp/res/layout/diagnostic.xml
new file mode 100644
index 0000000..7fd234c
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/diagnostic.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!-- Copyright (C) 2017 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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+android:layout_width="match_parent"
+android:layout_height="match_parent"
+android:orientation="vertical" >
+<!-- dummy one for top area -->
+<LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:orientation="vertical"
+ android:layout_weight="1" />
+<LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="1" >
+ <TextView
+ android:id="@+id/live_diagnostic_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Live Information"
+ android:minLines="10"/>
+ <TextView
+ android:id="@+id/freeze_diagnostic_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Freeze Information"
+ android:minLines="10"/>
+</LinearLayout>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
index 6a3d9ca..7554508 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
@@ -2,7 +2,7 @@
<!-- We use this container to place kitchen app fragments. It insets the fragment contents -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/kitchen_content"
- android:background="@android:color/black"
+ android:background="#A8A9AA"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="56dp"
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index 884d982..e68c3fa 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -41,6 +41,7 @@
import com.google.android.car.kitchensink.bluetooth.MapMceTestFragment;
import com.google.android.car.kitchensink.cluster.InstrumentClusterFragment;
import com.google.android.car.kitchensink.cube.CubesTestFragment;
+import com.google.android.car.kitchensink.diagnostic.DiagnosticTestFragment;
import com.google.android.car.kitchensink.hvac.HvacTestFragment;
import com.google.android.car.kitchensink.input.InputTestFragment;
import com.google.android.car.kitchensink.job.JobSchedulerFragment;
@@ -50,47 +51,122 @@
import com.google.android.car.kitchensink.setting.CarServiceSettingsActivity;
import com.google.android.car.kitchensink.touch.TouchTestFragment;
import com.google.android.car.kitchensink.volume.VolumeTestFragment;
+import java.util.ArrayList;
+import java.util.List;
public class KitchenSinkActivity extends CarDrawerActivity {
private static final String TAG = "KitchenSinkActivity";
- private static final String MENU_AUDIO = "audio";
- private static final String MENU_ASSISTANT = "assistant";
- private static final String MENU_HVAC = "hvac";
- private static final String MENU_QUIT = "quit";
- private static final String MENU_JOB = "job_scheduler";
- private static final String MENU_CLUSTER = "inst cluster";
- private static final String MENU_INPUT_TEST = "input test";
- private static final String MENU_RADIO = "radio";
- private static final String MENU_SENSORS = "sensors";
- private static final String MENU_VOLUME_TEST = "volume test";
- private static final String MENU_TOUCH_TEST = "touch test";
- private static final String MENU_CUBES_TEST = "cubes test";
- private static final String MENU_CAR_SETTINGS = "car service settings";
- private static final String MENU_ORIENTATION = "orientation test";
- private static final String MENU_BLUETOOTH_HEADSET = "bluetooth headset";
- private static final String MENU_MAP_MESSAGING = "bluetooth messaging test";
+ 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() {
+ 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);
+ } else {
+ Log.e(TAG, "cannot show fragment for " + getText());
+ }
+ }
+ }
+
+ private final List<MenuEntry> mMenuEntries = new ArrayList<MenuEntry>() {
+ {
+ add("audio", AudioTestFragment.class);
+ add("hvac", HvacTestFragment.class);
+ add("job scheduler", JobSchedulerFragment.class);
+ add("inst cluster", InstrumentClusterFragment.class);
+ add("input test", InputTestFragment.class);
+ add("radio", RadioTestFragment.class);
+ add("assistant", CarAssistantFragment.class);
+ add("sensors", SensorsTestFragment.class);
+ add("diagnostic", DiagnosticTestFragment.class);
+ add("volume test", VolumeTestFragment.class);
+ add("touch test", TouchTestFragment.class);
+ add("cubes test", CubesTestFragment.class);
+ add("orientation test", OrientationTestFragment.class);
+ add("bluetooth headset",BluetoothHeadsetFragment.class);
+ add("bluetooth messaging test", MapMceTestFragment.class);
+ add("car service settings", () -> {
+ Intent intent = new Intent(KitchenSinkActivity.this,
+ CarServiceSettingsActivity.class);
+ startActivity(intent);
+ });
+ add("quit", KitchenSinkActivity.this::finish);
+ }
+
+ <T extends Fragment> void add(String text, Class<T> clazz) {
+ add(new FragmentMenuEntry(text, clazz));
+ }
+ void add(String text, ClickHandler onClick) {
+ add(new OnClickMenuEntry(text, onClick));
+ }
+ };
private Car mCarApi;
private CarHvacManager mHvacManager;
private CarSensorManager mCarSensorManager;
private CarAppFocusManager mCarAppFocusManager;
- private AudioTestFragment mAudioTestFragment;
- private RadioTestFragment mRadioTestFragment;
- private SensorsTestFragment mSensorsTestFragment;
- private HvacTestFragment mHvacTestFragment;
- private JobSchedulerFragment mJobFragment;
- private InstrumentClusterFragment mInstrumentClusterFragment;
- private InputTestFragment mInputTestFragment;
- private VolumeTestFragment mVolumeTestFragment;
- private TouchTestFragment mTouchTestFragment;
- private CubesTestFragment mCubesTestFragment;
- private OrientationTestFragment mOrientationFragment;
- private MapMceTestFragment mMapMceTestFragment;
- private BluetoothHeadsetFragment mBluetoothHeadsetFragement;
- private CarAssistantFragment mAssistantFragment;
-
private final CarSensorManager.OnSensorChangedListener mListener = (manager, event) -> {
switch (event.sensorType) {
case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
@@ -99,6 +175,10 @@
}
};
+ public CarHvacManager getHvacManager() {
+ return mHvacManager;
+ }
+
@Override
protected CarDrawerAdapter getRootAdapter() {
return new DrawerAdapter();
@@ -194,132 +274,30 @@
private final class DrawerAdapter extends CarDrawerAdapter {
- private final String mAllMenus[] = {
- MENU_AUDIO, MENU_ASSISTANT, MENU_RADIO, MENU_HVAC, MENU_JOB,
- MENU_CLUSTER, MENU_INPUT_TEST, MENU_SENSORS, MENU_VOLUME_TEST,
- MENU_TOUCH_TEST, MENU_CUBES_TEST, MENU_CAR_SETTINGS, MENU_ORIENTATION,
- MENU_BLUETOOTH_HEADSET, MENU_MAP_MESSAGING, MENU_QUIT
- };
-
public DrawerAdapter() {
- super(KitchenSinkActivity.this, true /* showDisabledOnListOnEmpty */,
- true /* smallLayout */);
+ super(KitchenSinkActivity.this, true /* showDisabledOnListOnEmpty */);
setTitle(getString(R.string.app_title));
}
@Override
protected int getActualItemCount() {
- return mAllMenus.length;
+ return mMenuEntries.size();
}
@Override
protected void populateViewHolder(DrawerItemViewHolder holder, int position) {
- holder.getTitle().setText(mAllMenus[position]);
+ holder.getTitle().setText(mMenuEntries.get(position).getText());
}
@Override
public void onItemClick(int position) {
-
- switch (mAllMenus[position]) {
- case MENU_AUDIO:
- if (mAudioTestFragment == null) {
- mAudioTestFragment = new AudioTestFragment();
- }
- showFragment(mAudioTestFragment);
- break;
- case MENU_ASSISTANT:
- if (mAssistantFragment == null) {
- mAssistantFragment = new CarAssistantFragment();
- }
- showFragment(mAssistantFragment);
- break;
- case MENU_RADIO:
- if (mRadioTestFragment == null) {
- mRadioTestFragment = new RadioTestFragment();
- }
- showFragment(mRadioTestFragment);
- break;
- case MENU_SENSORS:
- if (mSensorsTestFragment == null) {
- mSensorsTestFragment = new SensorsTestFragment();
- }
- showFragment(mSensorsTestFragment);
- break;
- case MENU_HVAC:
- if (mHvacManager != null) {
- if (mHvacTestFragment == null) {
- mHvacTestFragment = new HvacTestFragment();
- mHvacTestFragment.setHvacManager(mHvacManager);
- }
- // Don't allow HVAC fragment to start if we don't have a manager.
- showFragment(mHvacTestFragment);
- }
- break;
- case MENU_JOB:
- if (mJobFragment == null) {
- mJobFragment = new JobSchedulerFragment();
- }
- showFragment(mJobFragment);
- break;
- case MENU_CLUSTER:
- if (mInstrumentClusterFragment == null) {
- mInstrumentClusterFragment = new InstrumentClusterFragment();
- }
- showFragment(mInstrumentClusterFragment);
- break;
- case MENU_INPUT_TEST:
- if (mInputTestFragment == null) {
- mInputTestFragment = new InputTestFragment();
- }
- showFragment(mInputTestFragment);
- break;
- case MENU_VOLUME_TEST:
- if (mVolumeTestFragment == null) {
- mVolumeTestFragment = new VolumeTestFragment();
- }
- showFragment(mVolumeTestFragment);
- break;
- case MENU_TOUCH_TEST:
- if (mTouchTestFragment == null) {
- mTouchTestFragment = new TouchTestFragment();
- }
- showFragment(mTouchTestFragment);
- break;
- case MENU_CUBES_TEST:
- if (mCubesTestFragment == null) {
- mCubesTestFragment = new CubesTestFragment();
- }
- showFragment(mCubesTestFragment);
- break;
- case MENU_CAR_SETTINGS:
- Intent intent = new Intent(KitchenSinkActivity.this,
- CarServiceSettingsActivity.class);
- startActivity(intent);
- break;
- case MENU_ORIENTATION:
- if (mOrientationFragment == null) {
- mOrientationFragment = new OrientationTestFragment();
- }
- showFragment(mOrientationFragment);
- break;
- case MENU_BLUETOOTH_HEADSET:
- if (mBluetoothHeadsetFragement == null) {
- mBluetoothHeadsetFragement = new BluetoothHeadsetFragment();
- }
- showFragment(mBluetoothHeadsetFragement);
- break;
- case MENU_MAP_MESSAGING:
- if (mMapMceTestFragment == null) {
- mMapMceTestFragment = new MapMceTestFragment();
- }
- showFragment(mMapMceTestFragment);
- break;
- case MENU_QUIT:
- finish();
- break;
- default:
- Log.wtf(TAG, "Unknown menu item: " + mAllMenus[position]);
+ if ((position < 0) || (position >= mMenuEntries.size())) {
+ Log.wtf(TAG, "Unknown menu item: " + position);
+ return;
}
+
+ mMenuEntries.get(position).onClick();
+
closeDrawer();
}
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
new file mode 100644
index 0000000..71deee8
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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.diagnostic;
+
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.hardware.CarDiagnosticEvent;
+import android.car.hardware.CarDiagnosticManager;
+import android.car.hardware.CarDiagnosticManager.OnDiagnosticEventListener;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.car.hardware.CarSensorManager;
+import android.support.v4.app.Fragment;
+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.util.Objects;
+
+
+public class DiagnosticTestFragment extends Fragment {
+ private static final String TAG = "CAR.DIAGNOSTIC.KS";
+
+ private KitchenSinkActivity mActivity;
+ private TextView mLiveDiagnosticInfo;
+ private TextView mFreezeDiagnosticInfo;
+ private CarDiagnosticManager mDiagnosticManager;
+
+ private final class TestListener implements OnDiagnosticEventListener {
+ private final TextView mTextView;
+
+ TestListener(TextView view) {
+ mTextView = Objects.requireNonNull(view);
+ }
+
+ @Override
+ public void onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent) {
+ mTextView.post(() -> mTextView.setText(carDiagnosticEvent.toString()));
+ }
+ }
+
+ private OnDiagnosticEventListener mLiveListener;
+ private OnDiagnosticEventListener mFreezeListener;
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.diagnostic, container, false);
+ mActivity = (KitchenSinkActivity) getHost();
+
+ mLiveDiagnosticInfo = (TextView) view.findViewById(R.id.live_diagnostic_info);
+ mLiveDiagnosticInfo.setTextColor(Color.RED);
+ mLiveListener = new TestListener(mLiveDiagnosticInfo);
+
+ mFreezeDiagnosticInfo = (TextView) view.findViewById(R.id.freeze_diagnostic_info);
+ mFreezeDiagnosticInfo.setTextColor(Color.RED);
+ mFreezeListener = new TestListener(mFreezeDiagnosticInfo);
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ resumeDiagnosticManager();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ pauseDiagnosticManager();
+ }
+
+ private void resumeDiagnosticManager() {
+ try {
+ mDiagnosticManager =
+ (CarDiagnosticManager)mActivity.getCar().getCarManager(Car.DIAGNOSTIC_SERVICE);
+ if (mLiveListener != null) {
+ mDiagnosticManager.registerListener(mLiveListener,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
+ CarSensorManager.SENSOR_RATE_NORMAL);
+ }
+ if (mFreezeListener != null) {
+ mDiagnosticManager.registerListener(mFreezeListener,
+ CarDiagnosticManager.FRAME_TYPE_FREEZE,
+ CarSensorManager.SENSOR_RATE_NORMAL);
+ }
+ } catch (android.car.CarNotConnectedException|android.support.car.CarNotConnectedException e) {
+ Log.e(TAG, "Car not connected or not supported", e);
+ }
+ }
+
+ private void pauseDiagnosticManager() {
+ if (mDiagnosticManager != null) {
+ if (mLiveListener != null) {
+ mDiagnosticManager.unregisterListener(mLiveListener);
+ }
+ if (mFreezeListener != null) {
+ mDiagnosticManager.unregisterListener(mFreezeListener);
+ }
+ }
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
index 05e33e8..3f5ef86 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
@@ -18,6 +18,7 @@
import static java.lang.Integer.toHexString;
+import com.google.android.car.kitchensink.KitchenSinkActivity;
import com.google.android.car.kitchensink.R;
import android.car.CarNotConnectedException;
@@ -166,6 +167,10 @@
}
};
+ public HvacTestFragment() {
+ setHvacManager( ((KitchenSinkActivity)getActivity()).getHvacManager() );
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/tests/VmsPublisherClientSample/Android.mk b/tests/VmsPublisherClientSample/Android.mk
index 6bb5bf7..2aa6c40 100644
--- a/tests/VmsPublisherClientSample/Android.mk
+++ b/tests/VmsPublisherClientSample/Android.mk
@@ -28,7 +28,7 @@
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_CERTIFICATE := platform
+LOCAL_CERTIFICATE := testkey
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/VmsPublisherClientSample/AndroidManifest.xml b/tests/VmsPublisherClientSample/AndroidManifest.xml
index d3ac195..fdc1a31 100644
--- a/tests/VmsPublisherClientSample/AndroidManifest.xml
+++ b/tests/VmsPublisherClientSample/AndroidManifest.xml
@@ -15,8 +15,11 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.car.vms.publisher"
- android:sharedUserId="android.uid.system">
+ package="com.google.android.car.vms.publisher">
+
+ <uses-permission android:name="android.car.permission.VMS_PUBLISHER" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion='25'/>
@@ -24,7 +27,8 @@
android:icon="@mipmap/ic_launcher"
android:directBootAware="true">
<service android:name=".VmsPublisherClientSampleService"
- android:exported="false">
+ android:exported="true"
+ android:singleUser="true">
</service>
</application>
</manifest>
diff --git a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
index c310464..08d37cd 100644
--- a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
+++ b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
@@ -22,7 +22,6 @@
import android.os.Handler;
import android.os.Message;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -31,7 +30,7 @@
*/
public class VmsPublisherClientSampleService extends VmsPublisherClientService {
public static final int PUBLISH_EVENT = 0;
- public static final VmsLayer TEST_LAYER = new VmsLayer(0,0);
+ public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0);
private byte mCounter = 0;
private AtomicBoolean mInitialized = new AtomicBoolean(false);
@@ -39,7 +38,7 @@
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- if (msg.what == PUBLISH_EVENT) {
+ if (msg.what == PUBLISH_EVENT && mInitialized.get()) {
periodicPublish();
}
}
@@ -51,6 +50,8 @@
*/
@Override
public void onVmsPublisherServiceReady() {
+ VmsSubscriptionState subscriptionState = getSubscriptions();
+ onVmsSubscriptionChange(subscriptionState);
}
@Override
@@ -64,6 +65,13 @@
}
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mInitialized.set(false);
+ mHandler.removeMessages(PUBLISH_EVENT);
+ }
+
private void periodicPublish() {
publish(TEST_LAYER, new byte[]{mCounter});
++mCounter;
diff --git a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
index 4ee95a1..fe32ab9 100644
--- a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
+++ b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
@@ -108,5 +108,10 @@
public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
mTextView.setText(String.valueOf(availableLayers));
}
+
+ @Override
+ public void onCarDisconnected() {
+
+ }
};
}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
index 4385e41..21fb5e0 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
@@ -25,15 +25,11 @@
import android.os.Looper;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-import com.android.car.internal.FeatureConfiguration;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@MediumTest
public class CarDiagnosticManagerTest extends AndroidTestCase {
- private static final String TAG = CarDiagnosticManagerTest.class.getSimpleName();
-
private static final long DEFAULT_WAIT_TIMEOUT_MS = 5000;
private final Semaphore mConnectionWait = new Semaphore(0);
@@ -64,25 +60,14 @@
mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
}
- private boolean isFeatureEnabled() {
- return FeatureConfiguration.ENABLE_DIAGNOSTIC;
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
mCar = Car.createCar(getContext(), mConnectionListener);
mCar.connect();
waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
-
- if (isFeatureEnabled()) {
- Log.i(TAG, "attempting to get DIAGNOSTIC_SERVICE");
- mCarDiagnosticManager =
- (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE);
- assertNotNull(mCarDiagnosticManager);
- } else {
- Log.i(TAG, "skipping diagnostic tests as ENABLE_DIAGNOSTIC flag is false");
- }
+ mCarDiagnosticManager = (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE);
+ assertNotNull(mCarDiagnosticManager);
}
@Override
@@ -97,11 +82,6 @@
* @throws Exception
*/
public void testLiveFrame() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testLiveFrame as diagnostics API is not enabled");
- return;
- }
-
CarDiagnosticEvent liveFrame = mCarDiagnosticManager.getLatestLiveFrame();
if (null != liveFrame) {
assertTrue(liveFrame.isLiveFrame());
@@ -115,11 +95,6 @@
* @throws Exception
*/
public void testFreezeFrames() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testFreezeFrames as diagnostics API is not enabled");
- return;
- }
-
long[] timestamps = mCarDiagnosticManager.getFreezeFrameTimestamps();
if (null != timestamps) {
for (long timestamp : timestamps) {
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarActivityTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarActivityTest.java
deleted file mode 100644
index 0fc8dc6..0000000
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarActivityTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.android.support.car.apitest;
-
-import android.support.car.Car;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-
-@MediumTest
-public class CarActivityTest extends ActivityInstrumentationTestCase2<TestCarProxyActivity> {
- private static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
-
- private TestCarProxyActivity mActivity;
-
- public CarActivityTest() {
- super(TestCarProxyActivity.class);
- }
-
- public CarActivityTest(Class<TestCarProxyActivity> activityClass) {
- super(activityClass);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- TestCarActivity.testCleanup();
- }
-
- public void testCycle() throws Throwable {
- TestCarActivity.sCreateTestAction = new TestAction<TestCarActivity>() {
- @Override
- public void run(TestCarActivity param) {
- // TODO: Add tests
- }
- };
- TestCarActivity.sStartTestAction = new TestAction<TestCarActivity>() {
- @Override
- public void run(TestCarActivity param) {
- // TODO: Add tests
- }
- };
- TestCarActivity.sResumeTestAction = new TestAction<TestCarActivity>() {
- @Override
- public void run(TestCarActivity param) {
- // TODO: Add tests
- }
- };
- TestCarActivity.sPauseTestAction = new TestAction<TestCarActivity>() {
- @Override
- public void run(TestCarActivity param) {
- // TODO: Add tests
- }
- };
- TestCarActivity.sStopTestAction = new TestAction<TestCarActivity>() {
- @Override
- public void run(TestCarActivity param) {
- // TODO: Add tests
- }
- };
- TestCarActivity.sDestroyTestAction = new TestAction<TestCarActivity>() {
- @Override
- public void run(TestCarActivity param) {
- // TODO: Add tests
- }
- };
- mActivity = getActivity();
- TestCarActivity.sCreateTestAction.assertTestRun(DEFAULT_WAIT_TIMEOUT_MS);
- TestCarActivity.sStartTestAction.assertTestRun(DEFAULT_WAIT_TIMEOUT_MS);
- TestCarActivity.sResumeTestAction.assertTestRun(DEFAULT_WAIT_TIMEOUT_MS);
- mActivity.finish();
- TestCarActivity.sPauseTestAction.assertTestRun(DEFAULT_WAIT_TIMEOUT_MS);
- TestCarActivity.sStopTestAction.assertTestRun(DEFAULT_WAIT_TIMEOUT_MS);
- TestCarActivity.sDestroyTestAction.assertTestRun(DEFAULT_WAIT_TIMEOUT_MS);
- }
-
-}
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarActivity.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarActivity.java
deleted file mode 100644
index da19b72..0000000
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/TestCarActivity.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.android.support.car.apitest;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.support.car.Car;
-import android.support.car.app.CarActivity;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-@MediumTest
-public class TestCarActivity extends CarActivity {
- private static final String TAG = TestCarActivity.class.getSimpleName();
-
- public static volatile TestAction<TestCarActivity> sCreateTestAction;
- public static volatile TestAction<TestCarActivity> sStartTestAction;
- public static volatile TestAction<TestCarActivity> sResumeTestAction;
- public static volatile TestAction<TestCarActivity> sPauseTestAction;
- public static volatile TestAction<TestCarActivity> sStopTestAction;
- public static volatile TestAction<TestCarActivity> sDestroyTestAction;
-
- public TestCarActivity(CarActivity.Proxy proxy, Context context, Car car) {
- super(proxy, context, car);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Log.d(TAG, "onCreate");
- super.onCreate(savedInstanceState);
- Log.d(TAG, "TestAction " + sCreateTestAction);
- doRunTest(sCreateTestAction);
- }
-
-
- @Override
- protected void onStart() {
- Log.d(TAG, "onStart");
- super.onStart();
- doRunTest(sStartTestAction);
- }
-
- @Override
- protected void onResume() {
- Log.d(TAG, "onResume");
- super.onResume();
- doRunTest(sResumeTestAction);
-
- }
-
- @Override
- protected void onPause() {
- Log.d(TAG, "onPause");
- super.onPause();
- doRunTest(sPauseTestAction);
- }
-
-
- @Override
- protected void onStop() {
- Log.d(TAG, "onStop");
- super.onStop();
- doRunTest(sStopTestAction);
- }
-
- @Override
- protected void onDestroy() {
- Log.d(TAG, "onDestroy");
- super.onDestroy();
- doRunTest(sDestroyTestAction);
- }
-
- private void doRunTest(TestAction<TestCarActivity> test) {
- Log.d(TAG, "doRunTest " + test);
- if (test != null) {
- test.doRun(this);
- }
- }
-
- public static void testCleanup() {
- sCreateTestAction = null;
- sStartTestAction = null;
- sResumeTestAction = null;
- sPauseTestAction = null;
- sStopTestAction = null;
- sDestroyTestAction = null;
- }
-}
diff --git a/tests/carservice_test/AndroidManifest.xml b/tests/carservice_test/AndroidManifest.xml
index 6a1e2bf..6f7ba34 100644
--- a/tests/carservice_test/AndroidManifest.xml
+++ b/tests/carservice_test/AndroidManifest.xml
@@ -43,5 +43,9 @@
android:process="com.android.car.carservicetest.activityC"/>
<activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$BlockingActivity"
android:taskAffinity="com.android.car.carservicetest.block"/>
+ <service android:name=".SimpleVmsPublisherClientService"
+ android:exported="true"
+ />
+ <service android:name=".VmsPublisherClientMockService" android:exported="true" />
</application>
</manifest>
diff --git a/tests/carservice_test/src/com/android/car/test/AudioTestUtils.java b/tests/carservice_test/src/com/android/car/test/AudioTestUtils.java
new file mode 100644
index 0000000..8c3de7c
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/AudioTestUtils.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.android.car.test;
+
+import android.media.AudioAttributes;
+import android.media.AudioFocusRequest;
+import android.media.AudioManager;
+import android.media.AudioManager.OnAudioFocusChangeListener;
+
+final class AudioTestUtils {
+ private AudioTestUtils() {}
+
+ static int doRequestFocus(
+ AudioManager audioManager,
+ OnAudioFocusChangeListener listener,
+ int streamType,
+ int androidFocus) {
+ AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder();
+ attributesBuilder.setLegacyStreamType(streamType);
+ return doRequestFocus(audioManager, listener, attributesBuilder.build(), androidFocus);
+ }
+
+ static int doRequestFocus(
+ AudioManager audioManager,
+ OnAudioFocusChangeListener listener,
+ AudioAttributes attributes,
+ int androidFocus) {
+ return doRequestFocus(audioManager, listener, attributes, androidFocus, false);
+ }
+
+ static int doRequestFocus(
+ AudioManager audioManager,
+ OnAudioFocusChangeListener listener,
+ AudioAttributes attributes,
+ int androidFocus,
+ boolean acceptsDelayedFocus) {
+ AudioFocusRequest.Builder focusBuilder = new AudioFocusRequest.Builder(androidFocus);
+ focusBuilder.setOnAudioFocusChangeListener(listener).setAcceptsDelayedFocusGain(
+ acceptsDelayedFocus);
+ focusBuilder.setAudioAttributes(attributes);
+
+ return audioManager.requestAudioFocus(focusBuilder.build());
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java b/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java
index 047ad41..71e0d04 100644
--- a/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java
@@ -16,6 +16,7 @@
package com.android.car.test;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS;
+import static com.android.car.test.AudioTestUtils.doRequestFocus;
import static java.lang.Integer.toHexString;
import android.car.Car;
@@ -226,7 +227,7 @@
public void testMediaNavFocus() throws Exception {
//music start
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -248,8 +249,8 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
assertEquals(0x3, request[1]);
@@ -292,7 +293,7 @@
public void testMediaExternalMediaNavFocus() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -322,8 +323,8 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
request[0]);
@@ -368,8 +369,8 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- int res = mAudioManager.requestAudioFocus(listenerRadio,
- radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ int res = doRequestFocus(mAudioManager, listenerRadio,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
@@ -388,8 +389,8 @@
AudioFocusListener listenerNav = new AudioFocusListener();
AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
- res = mAudioManager.requestAudioFocus(listenerNav,
- extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ res = doRequestFocus(mAudioManager, listenerNav,
+ extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
@@ -437,7 +438,7 @@
public void testMediaExternalNav() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -457,8 +458,8 @@
AudioFocusListener listenerNav = new AudioFocusListener();
AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
- res = mAudioManager.requestAudioFocus(listenerNav,
- extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ res = doRequestFocus(mAudioManager, listenerNav,
+ extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
@@ -513,8 +514,8 @@
AudioFocusListener listenerIntNav = new AudioFocusListener();
AudioAttributes intNavAttributes = mCarAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE);
- int res = mAudioManager.requestAudioFocus(listenerIntNav, intNavAttributes,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ int res = doRequestFocus(mAudioManager, listenerIntNav, intNavAttributes,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
@@ -533,8 +534,8 @@
AudioFocusListener listenerExtNav = new AudioFocusListener();
AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
- res = mAudioManager.requestAudioFocus(listenerExtNav,
- extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ res = doRequestFocus(mAudioManager, listenerExtNav,
+ extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
@@ -581,7 +582,7 @@
public void testMediaExternalRadioNavMediaFocus() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -604,8 +605,8 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- res = mAudioManager.requestAudioFocus(listenerRadio,
- radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ res = doRequestFocus(mAudioManager, listenerRadio,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
@@ -626,8 +627,8 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
request[0]);
@@ -762,8 +763,8 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(mediaUsage);
Log.i(TAG, "request media Focus");
- int res = mAudioManager.requestAudioFocus(listenerMedia,
- radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ int res = doRequestFocus(mAudioManager, listenerMedia,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
@@ -806,8 +807,8 @@
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
Log.i(TAG, "request nav Focus");
- res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
request[0]);
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioFocusSystemSoundTest.java b/tests/carservice_test/src/com/android/car/test/CarAudioFocusSystemSoundTest.java
index 99958cc..afafb28 100644
--- a/tests/carservice_test/src/com/android/car/test/CarAudioFocusSystemSoundTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarAudioFocusSystemSoundTest.java
@@ -17,6 +17,7 @@
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_STREAM_STATE;
+import static com.android.car.test.AudioTestUtils.doRequestFocus;
import com.google.android.collect.Lists;
@@ -136,8 +137,8 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- int res = mAudioManager.requestAudioFocus(listenerRadio,
- radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ int res = doRequestFocus(mAudioManager, listenerRadio,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
@@ -190,7 +191,7 @@
public void testMusicSystemSound() throws Exception {
// music start
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -246,8 +247,8 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- int res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ int res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java b/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
index 9ead75d..6f5da5f 100644
--- a/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
@@ -16,6 +16,7 @@
package com.android.car.test;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS;
+import static com.android.car.test.AudioTestUtils.doRequestFocus;
import android.car.Car;
import android.car.media.CarAudioManager;
@@ -135,7 +136,7 @@
public void testMediaNavFocus() throws Exception {
//music start
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -155,8 +156,8 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
assertEquals(0x3, request[1]);
@@ -193,7 +194,7 @@
public void testMediaExternalMediaNavFocus() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -221,8 +222,8 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
request[0]);
@@ -258,7 +259,7 @@
public void testMediaExternalRadioNavMediaFocus() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -279,8 +280,8 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- res = mAudioManager.requestAudioFocus(listenerRadio,
- radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ res = doRequestFocus(mAudioManager, listenerRadio,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
@@ -299,8 +300,8 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
request[0]);
@@ -360,7 +361,7 @@
int context)
throws Exception {
AudioFocusListener lister = new AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(lister,
+ int res = doRequestFocus(mAudioManager, lister,
streamType,
androidFocus);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -426,8 +427,8 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(mediaUsage);
Log.i(TAG, "request media Focus");
- int res = mAudioManager.requestAudioFocus(listenerMedia,
- radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ int res = doRequestFocus(mAudioManager, listenerMedia,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
@@ -461,8 +462,8 @@
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
Log.i(TAG, "request nav Focus");
- res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
request[0]);
diff --git a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
index 6562542..d316ba6 100644
--- a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
@@ -33,11 +33,16 @@
import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.MediumTest;
+import android.util.JsonReader;
+import android.util.JsonWriter;
import android.util.Log;
import com.android.car.internal.FeatureConfiguration;
import com.android.car.vehiclehal.DiagnosticEventBuilder;
+import com.android.car.vehiclehal.DiagnosticJson;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -198,10 +203,6 @@
mFreezeFrameProperties.mFreezeFrameClearHandler);
}
- private boolean isFeatureEnabled() {
- return FeatureConfiguration.ENABLE_DIAGNOSTIC;
- }
-
@Override
protected void setUp() throws Exception {
mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
@@ -225,21 +226,12 @@
super.setUp();
- if (isFeatureEnabled()) {
- Log.i(TAG, "attempting to get DIAGNOSTIC_SERVICE");
- mCarDiagnosticManager =
- (CarDiagnosticManager) getCar().getCarManager(Car.DIAGNOSTIC_SERVICE);
- } else {
- Log.i(TAG, "skipping diagnostic tests as ENABLE_DIAGNOSTIC flag is false");
- }
+ Log.i(TAG, "attempting to get DIAGNOSTIC_SERVICE");
+ mCarDiagnosticManager =
+ (CarDiagnosticManager) getCar().getCarManager(Car.DIAGNOSTIC_SERVICE);
}
public void testLiveFrameRead() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testLiveFrameRead as diagnostics API is not enabled");
- return;
- }
-
CarDiagnosticEvent liveFrame = mCarDiagnosticManager.getLatestLiveFrame();
assertNotNull(liveFrame);
@@ -273,15 +265,10 @@
}
public void testLiveFrameEvent() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testLiveFrameEvent as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
listener.reset();
@@ -302,15 +289,10 @@
}
public void testMissingSensorRead() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testMissingSensorRead as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build());
@@ -341,15 +323,10 @@
}
public void testFuelSystemStatus() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testFuelSystemStatus as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build());
@@ -369,15 +346,10 @@
}
public void testSecondaryAirStatus() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testSecondaryAirStatus as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
mLiveFrameEventBuilder.addIntSensor(
@@ -403,15 +375,10 @@
}
public void testIgnitionMonitors() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testIgnitionMonitors as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
// cfr. CarDiagnosticEvent for the meaning of the several bits
@@ -505,15 +472,10 @@
}
public void testFuelType() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testFuelType as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
mLiveFrameEventBuilder.addIntSensor(
@@ -532,22 +494,65 @@
assertEquals(FuelType.BIFUEL_RUNNING_LPG, liveFrame.getFuelType().intValue());
}
- public void testMultipleListeners() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testMultipleListeners as diagnostics API is not enabled");
- return;
- }
+ public void testDiagnosticJson() throws Exception {
+ Listener listener = new Listener();
+ mCarDiagnosticManager.registerListener(
+ listener,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
+ android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
+ mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE, 74);
+ mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE, 0.125f);
+
+ long timestamp = SystemClock.elapsedRealtimeNanos();
+ getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
+
+ assertTrue(listener.waitForEvent(timestamp));
+
+ CarDiagnosticEvent liveFrame = listener.getLastEvent();
+ assertNotNull(liveFrame);
+
+ assertEquals(
+ 74,
+ liveFrame
+ .getSystemIntegerSensor(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
+ .intValue());
+ assertEquals(
+ 0.125f,
+ liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
+
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+
+ liveFrame.writeToJson(jsonWriter);
+ jsonWriter.flush();
+
+ StringReader stringReader = new StringReader(stringWriter.toString());
+ JsonReader jsonReader = new JsonReader(stringReader);
+ DiagnosticJson diagnosticJson = DiagnosticJson.build(jsonReader);
+
+ assertEquals(
+ 74,
+ diagnosticJson
+ .intValues
+ .get(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
+ .intValue());
+ assertEquals(
+ 0.125f,
+ diagnosticJson.floatValues.get(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
+ }
+
+ public void testMultipleListeners() throws Exception {
Listener listener1 = new Listener();
Listener listener2 = new Listener();
mCarDiagnosticManager.registerListener(
listener1,
- CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
mCarDiagnosticManager.registerListener(
listener2,
- CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
listener1.reset();
@@ -560,6 +565,15 @@
CarDiagnosticEvent event1 = listener1.getLastEvent();
CarDiagnosticEvent event2 = listener2.getLastEvent();
+
+ assertTrue(event1.equals(event1));
+ assertTrue(event2.equals(event2));
+ assertTrue(event1.equals(event2));
+ assertTrue(event2.equals(event1));
+
+ assertTrue(event1.hashCode() == event1.hashCode());
+ assertTrue(event1.hashCode() == event2.hashCode());
+
assertEquals(
5000,
event1.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
@@ -583,6 +597,8 @@
event2 = listener2.getLastEvent();
assertTrue(event1.isEarlierThan(event2));
+ assertFalse(event1.equals(event2));
+ assertFalse(event2.equals(event1));
assertEquals(
5000,
@@ -591,15 +607,10 @@
}
public void testFreezeFrameEvent() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testFreezeFrameEvent as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
+ CarDiagnosticManager.FRAME_TYPE_FREEZE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
listener.reset();
@@ -634,15 +645,10 @@
}
public void testFreezeFrameTimestamps() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testFreezeFrameTimestamps as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
+ CarDiagnosticManager.FRAME_TYPE_FREEZE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
Set<Long> generatedTimestamps = new HashSet<>();
@@ -668,15 +674,10 @@
}
public void testClearFreezeFrameTimestamps() throws Exception {
- if (!isFeatureEnabled()) {
- Log.i(TAG, "skipping testClearFreezeFrameTimestamps as diagnostics API is not enabled");
- return;
- }
-
Listener listener = new Listener();
mCarDiagnosticManager.registerListener(
listener,
- CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
+ CarDiagnosticManager.FRAME_TYPE_FREEZE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
VehiclePropValue injectedEvent =
@@ -689,6 +690,51 @@
assertNull(mCarDiagnosticManager.getFreezeFrame(injectedEvent.timestamp));
}
+ public void testListenerUnregister() throws Exception {
+ Listener listener1 = new Listener();
+ Listener listener2 = new Listener();
+ mCarDiagnosticManager.registerListener(
+ listener1,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
+ android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
+ mCarDiagnosticManager.registerListener(
+ listener1,
+ CarDiagnosticManager.FRAME_TYPE_FREEZE,
+ android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
+
+ mCarDiagnosticManager.unregisterListener(listener1);
+
+ // you need a listener to be registered before MockedVehicleHal will actually dispatch
+ // your events - add one, but do it *after* unregistering the first listener
+ mCarDiagnosticManager.registerListener(
+ listener2,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
+ android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
+ mCarDiagnosticManager.registerListener(
+ listener2,
+ CarDiagnosticManager.FRAME_TYPE_FREEZE,
+ android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
+
+ VehiclePropValue injectedEvent =
+ mFreezeFrameProperties.addNewEvent(mFreezeFrameEventBuilder);
+ long time = injectedEvent.timestamp;
+ getMockedVehicleHal().injectEvent(injectedEvent);
+ assertFalse(listener1.waitForEvent(time));
+ assertTrue(listener2.waitForEvent(time));
+
+ time += 1000;
+ getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(time));
+ assertFalse(listener1.waitForEvent(time));
+ assertTrue(listener2.waitForEvent(time));
+ }
+
+ public void testIsSupportedApiCalls() throws Exception {
+ assertTrue(mCarDiagnosticManager.isLiveFrameSupported());
+ assertTrue(mCarDiagnosticManager.isFreezeFrameSupported());
+ assertTrue(mCarDiagnosticManager.isFreezeFrameTimestampSupported());
+ assertTrue(mCarDiagnosticManager.isFreezeFrameClearSupported());
+ }
+
class Listener implements CarDiagnosticManager.OnDiagnosticEventListener {
private final Object mSync = new Object();
diff --git a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java b/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
index cdd8838..df2b532 100644
--- a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
@@ -15,6 +15,8 @@
*/
package com.android.car.test;
+import static com.android.car.test.AudioTestUtils.doRequestFocus;
+
import com.google.android.collect.Lists;
import android.car.Car;
@@ -121,7 +123,7 @@
// first give focus to system sound
CarAudioFocusTest.AudioFocusListener listenerMusic =
new CarAudioFocusTest.AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_SYSTEM,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -137,8 +139,8 @@
AudioAttributes callAttrib = (new AudioAttributes.Builder()).
setUsage(AudioAttributes.USAGE_ALARM).
build();
- res = mAudioManager.requestAudioFocus(listenerAlarm, callAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ res = doRequestFocus(mAudioManager, listenerAlarm, callAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
mAudioFocusPropertyHandler.sendAudioFocusState(
@@ -169,7 +171,7 @@
CarAudioFocusTest.AudioFocusListener listenerMusic =
new CarAudioFocusTest.AudioFocusListener();
- int res = mAudioManager.requestAudioFocus(listenerMusic,
+ int res = doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -191,8 +193,8 @@
AudioAttributes callAttrib = (new AudioAttributes.Builder()).
setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).
build();
- mAudioManager.requestAudioFocus(listenerCall, callAttrib,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ doRequestFocus(mAudioManager, listenerCall, callAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
mAudioFocusPropertyHandler.sendAudioFocusState(
VehicleAudioFocusState.STATE_GAIN, request[1],
diff --git a/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java b/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java
new file mode 100644
index 0000000..c8badf2
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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.android.car.test;
+
+import android.car.annotation.FutureFeature;
+import android.car.vms.VmsPublisherClientService;
+import android.car.vms.VmsSubscriptionState;
+
+/**
+ * This service is launched during the tests in VmsPublisherClientServiceTest.
+ */
+@FutureFeature
+public class SimpleVmsPublisherClientService extends VmsPublisherClientService {
+ @Override
+ public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
+
+ }
+
+ @Override
+ public void onVmsPublisherServiceReady() {
+ // Publish a property that is going to be verified in the test.
+ publish(VmsPublisherClientServiceTest.MOCK_PUBLISHER_LAYER,
+ VmsPublisherClientServiceTest.PAYLOAD);
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java b/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java
new file mode 100644
index 0000000..240598a
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 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.android.car.test;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.car.VehicleAreaType;
+import android.car.annotation.FutureFeature;
+import android.car.vms.VmsLayer;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
+import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+import android.hardware.automotive.vehicle.V2_1.VmsSubscriptionResponseFormat;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@FutureFeature
+@MediumTest
+public class VmsHalServiceSubscriptionEventTest extends MockedCarTestBase {
+ private static final String TAG = "VmsHalServiceTest";
+
+ private HalHandler mHalHandler;
+ private MockedVehicleHal mHal;
+ // Used to block until the HAL property is updated in HalHandler.onPropertySet.
+ private Semaphore mHalHandlerSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.setUp();
+ mHal = getMockedVehicleHal();
+ mHalHandlerSemaphore = new Semaphore(0);
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.tearDown();
+ }
+
+ public void testEmptySubscriptions() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ List<VmsLayer> layers = new ArrayList<>();
+ subscriptionTestLogic(layers);
+ }
+
+ public void testOneSubscription() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ List<VmsLayer> layers = Arrays.asList(new VmsLayer(8, 3));
+ subscriptionTestLogic(layers);
+ }
+
+ public void testManySubscriptions() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ List<VmsLayer> layers = Arrays.asList(
+ new VmsLayer(8, 3),
+ new VmsLayer(5, 1),
+ new VmsLayer(3, 9),
+ new VmsLayer(2, 7),
+ new VmsLayer(9, 3));
+ subscriptionTestLogic(layers);
+ }
+
+ /**
+ * First, it subscribes to the given layers. Then it validates that a subscription request
+ * responds with the same layers.
+ */
+ private void subscriptionTestLogic(List<VmsLayer> layers) throws Exception {
+ for (VmsLayer layer : layers) {
+ subscribeViaHal(layer);
+ }
+ // Send subscription request.
+ mHal.injectEvent(createHalSubscriptionRequest());
+ // Wait for response.
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ // Validate response.
+ ArrayList<Integer> v = mHalHandler.getValues();
+ int messageType = v.get(VmsSubscriptionResponseFormat.VMS_MESSAGE_TYPE);
+ int sequenceNumber = v.get(VmsSubscriptionResponseFormat.SEQUENCE_NUMBER);
+ int numberLayers = v.get(VmsSubscriptionResponseFormat.NUMBER_OF_LAYERS);
+ assertEquals(VmsMessageType.SUBSCRIPTION_RESPONSE, messageType);
+ assertEquals(layers.size(), sequenceNumber);
+ assertEquals(layers.size(), numberLayers);
+ List<VmsLayer> receivedLayers = new ArrayList<>();
+ int start = VmsSubscriptionResponseFormat.FIRST_LAYER;
+ int end = VmsSubscriptionResponseFormat.FIRST_LAYER + 2 * numberLayers;
+ while (start < end) {
+ int id = v.get(start++);
+ int version = v.get(start++);
+ receivedLayers.add(new VmsLayer(id, version));
+ }
+ assertEquals(new HashSet<>(layers), new HashSet<>(receivedLayers));
+ }
+
+ /**
+ * Subscribes to a layer, waits for the event to propagate back to the HAL layer and validates
+ * the propagated message.
+ */
+ private void subscribeViaHal(VmsLayer layer) throws Exception {
+ // Send subscribe request.
+ mHal.injectEvent(createHalSubscribeRequest(layer));
+ // Wait for response.
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ // Validate response.
+ ArrayList<Integer> v = mHalHandler.getValues();
+ int messsageType = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
+ int layerId = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
+ int layerVersion = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ assertEquals(VmsMessageType.SUBSCRIBE, messsageType);
+ assertEquals(layer.getId(), layerId);
+ assertEquals(layer.getVersion(), layerVersion);
+ }
+
+ private VehiclePropValue createHalSubscribeRequest(VmsLayer layer) {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIBE)
+ .addIntValue(layer.getId())
+ .addIntValue(layer.getVersion())
+ .build();
+ }
+
+ private VehiclePropValue createHalSubscriptionRequest() {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIPTION_REQUEST)
+ .build();
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ private ArrayList<Integer> mValues;
+
+ @Override
+ public synchronized void onPropertySet(VehiclePropValue value) {
+ mValues = value.value.int32Values;
+ mHalHandlerSemaphore.release();
+ }
+
+ public ArrayList<Integer> getValues() {
+ return mValues;
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
new file mode 100644
index 0000000..d8e344b
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 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.android.car.test;
+
+import android.car.annotation.FutureFeature;
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsLayerDependency;
+import android.car.vms.VmsLayersOffering;
+import android.car.vms.VmsPublisherClientService;
+import android.car.vms.VmsSubscriptionState;
+import android.util.Log;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * This service is launched during the tests in VmsPublisherSubscriberTest. It publishes a property
+ * that is going to be verified in the test.
+ *
+ * Note that the subscriber can subscribe before the publisher finishes initialization. To cover
+ * both potential scenarios, this service publishes the test message in onVmsSubscriptionChange
+ * and in onVmsPublisherServiceReady. See comments below.
+ */
+@FutureFeature
+public class VmsPublisherClientMockService extends VmsPublisherClientService {
+ private static final String TAG = "VmsPublisherClientMockService";
+
+ @Override
+ public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
+ // Case when the publisher finished initialization before the subscription request.
+ initializeMockPublisher(subscriptionState);
+ }
+
+ @Override
+ public void onVmsPublisherServiceReady() {
+ // Case when the subscription request was sent before the publisher was ready.
+ VmsSubscriptionState subscriptionState = getSubscriptions();
+ initializeMockPublisher(subscriptionState);
+ }
+
+ private void initializeMockPublisher(VmsSubscriptionState subscriptionState) {
+ Log.d(TAG, "Initializing Mock publisher");
+ getPublisherStaticId(VmsPublisherSubscriberTest.PAYLOAD);
+ publishIfNeeded(subscriptionState);
+ declareOffering(subscriptionState);
+ }
+
+ private void publishIfNeeded(VmsSubscriptionState subscriptionState) {
+ for (VmsLayer layer : subscriptionState.getLayers()) {
+ if (layer.equals(VmsPublisherSubscriberTest.LAYER)) {
+ publish(VmsPublisherSubscriberTest.LAYER, VmsPublisherSubscriberTest.PAYLOAD);
+ }
+ }
+ }
+
+ private void declareOffering(VmsSubscriptionState subscriptionState) {
+ List<VmsLayerDependency> dependencies = new ArrayList<>();
+ for( VmsLayer layer : subscriptionState.getLayers()) {
+ dependencies.add(new VmsLayerDependency(layer));
+ }
+ VmsLayersOffering offering = new VmsLayersOffering(dependencies);
+ setLayersOffering(offering);
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java
new file mode 100644
index 0000000..c22f63a
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 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.android.car.test;
+
+import android.annotation.ArrayRes;
+import android.car.VehicleAreaType;
+import android.car.annotation.FutureFeature;
+import android.car.vms.VmsLayer;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
+import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import com.android.car.R;
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@FutureFeature
+@MediumTest
+public class VmsPublisherClientServiceTest extends MockedCarTestBase {
+ private static final String TAG = "VmsPublisherTest";
+ private static final int MOCK_PUBLISHER_LAYER_ID = 12;
+ private static final int MOCK_PUBLISHER_LAYER_VERSION = 34;
+ public static final VmsLayer MOCK_PUBLISHER_LAYER = new VmsLayer(MOCK_PUBLISHER_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_VERSION);
+ public static final byte[] PAYLOAD = new byte[]{1, 1, 2, 3, 5, 8, 13};
+
+ private HalHandler mHalHandler;
+ // Used to block until the HAL property is updated in HalHandler.onPropertySet.
+ private Semaphore mHalHandlerSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ /**
+ * Creates a context with the resource vmsPublisherClients overridden. The overridden value
+ * contains the name of the test service defined also in this test package.
+ */
+ @Override
+ protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
+ Context context = getContext()
+ .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
+ Resources resources = new Resources(context.getAssets(),
+ context.getResources().getDisplayMetrics(),
+ context.getResources().getConfiguration()) {
+ @Override
+ public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
+ if (id == R.array.vmsPublisherClients) {
+ return new String[]{"com.android.car.test/.SimpleVmsPublisherClientService"};
+ }
+ return super.getStringArray(id);
+ }
+ };
+ ContextWrapper wrapper = new ContextWrapper(context) {
+ @Override
+ public Resources getResources() {
+ return resources;
+ }
+ };
+ return wrapper;
+ }
+
+ private VehiclePropValue getHalSubscriptionRequest() {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIBE)
+ .addIntValue(MOCK_PUBLISHER_LAYER_ID)
+ .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
+ .build();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ /**
+ * First init the semaphore, setUp will start a series of events that will ultimately
+ * update the HAL layer and release this semaphore.
+ */
+ mHalHandlerSemaphore = new Semaphore(0);
+ super.setUp();
+
+ // Inject a subscribe event which simulates the HAL is subscribed to the Mock Publisher.
+ MockedVehicleHal mHal = getMockedVehicleHal();
+ mHal.injectEvent(getHalSubscriptionRequest());
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.tearDown();
+ }
+
+ /**
+ * The method setUp initializes all the Car services, including the VmsPublisherService.
+ * The VmsPublisherService will start and configure its list of clients. This list was
+ * overridden in the method getCarServiceContext.
+ * Therefore, only SimpleVmsPublisherClientService will be started.
+ * The service SimpleVmsPublisherClientService will publish one message, which is validated in
+ * this test.
+ */
+ public void testPublish() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ //TODO: This test is using minial synchronisation between clients.
+ // If more complexity is added this may result in publisher
+ // publishing before the subscriber subscribed, in which case
+ // the semaphore will not be released.
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ VehiclePropValue.RawValue rawValue = mHalHandler.getValue().value;
+ int messageType = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
+ int layerId = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
+ int layerVersion = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ byte[] payload = new byte[rawValue.bytes.size()];
+ for (int i = 0; i < rawValue.bytes.size(); ++i) {
+ payload[i] = rawValue.bytes.get(i);
+ }
+ assertEquals(VmsMessageType.DATA, messageType);
+ assertEquals(MOCK_PUBLISHER_LAYER_ID, layerId);
+ assertEquals(MOCK_PUBLISHER_LAYER_VERSION, layerVersion);
+ assertTrue(Arrays.equals(PAYLOAD, payload));
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ private VehiclePropValue mValue;
+
+ @Override
+ public synchronized void onPropertySet(VehiclePropValue value) {
+ mValue = value;
+
+ // If this is the data message release the semaphone so the test can continue.
+ ArrayList<Integer> int32Values = value.value.int32Values;
+ if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) ==
+ VmsMessageType.DATA) {
+ mHalHandlerSemaphore.release();
+ }
+ }
+
+ @Override
+ public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ return mValue != null ? mValue : value;
+ }
+
+ @Override
+ public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
+ Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
+ }
+
+ @Override
+ public synchronized void onPropertyUnsubscribe(int property) {
+ Log.d(TAG, "onPropertyUnSubscribe property " + property);
+ }
+
+ public VehiclePropValue getValue() {
+ return mValue;
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
new file mode 100644
index 0000000..739f5d0
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.android.car.test;
+
+import android.annotation.ArrayRes;
+import android.car.VehicleAreaType;
+import android.car.annotation.FutureFeature;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
+import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+
+import com.android.car.R;
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@FutureFeature
+public class VmsPublisherPermissionsTest extends MockedCarTestBase {
+ private static final String TAG = "VmsPublisherTest";
+ private static final int MOCK_PUBLISHER_LAYER_ID = 0;
+ private static final int MOCK_PUBLISHER_LAYER_VERSION = 0;
+
+ private HalHandler mHalHandler;
+ // Used to block until the HAL property is updated in HalHandler.onPropertySet.
+ private Semaphore mHalHandlerSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ /**
+ * Creates a context with the resource vmsPublisherClients overridden. The overridden value
+ * contains the name of the test service defined also in this test package.
+ */
+ @Override
+ protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
+ Context context = getContext()
+ .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
+ Resources resources = new Resources(context.getAssets(),
+ context.getResources().getDisplayMetrics(),
+ context.getResources().getConfiguration()) {
+ @Override
+ public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
+ if (id == R.array.vmsPublisherClients) {
+ return new String[]{
+ "com.google.android.car.vms.publisher/"
+ + ".VmsPublisherClientSampleService"};
+ } else if (id == R.array.vmsSafePermissions) {
+ return new String[]{"android.permission.ACCESS_FINE_LOCATION"};
+ }
+ return super.getStringArray(id);
+ }
+ };
+ ContextWrapper wrapper = new ContextWrapper(context) {
+ @Override
+ public Resources getResources() {
+ return resources;
+ }
+ };
+ return wrapper;
+ }
+
+ private VehiclePropValue getHalSubscriptionRequest() {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIBE)
+ .addIntValue(MOCK_PUBLISHER_LAYER_ID)
+ .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
+ .build();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ /**
+ * First init the semaphore, setUp will start a series of events that will ultimately
+ * update the HAL layer and release this semaphore.
+ */
+ mHalHandlerSemaphore = new Semaphore(0);
+ super.setUp();
+
+ // Inject a subscribe event which simulates the HAL is subscribed to the Sample Publisher.
+ MockedVehicleHal mHal = getMockedVehicleHal();
+ mHal.injectEvent(getHalSubscriptionRequest());
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.tearDown();
+ }
+
+ /**
+ * The method setUp initializes all the Car services, including the VmsPublisherService.
+ * The VmsPublisherService will start and configure its list of clients. This list was
+ * overridden in the method getCarServiceContext.
+ * Therefore, only VmsPublisherClientSampleService will be started.
+ * The service VmsPublisherClientSampleService will publish one message, which is validated in
+ * this test.
+ */
+ public void testPermissions() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ // At this point the client initialization finished. Let's validate the permissions.
+ // The VMS service is only allowed to grant ACCESS_FINE_LOCATION but not CAMERA.
+ assertTrue(
+ getContext().getPackageManager().checkPermission(
+ "android.permission.ACCESS_FINE_LOCATION",
+ "com.google.android.car.vms.publisher")
+ == PackageManager.PERMISSION_GRANTED);
+ assertFalse(getContext().getPackageManager().checkPermission(
+ "android.permission.CAMERA", "com.google.android.car.vms.publisher")
+ == PackageManager.PERMISSION_GRANTED);
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ @Override
+ public synchronized void onPropertySet(VehiclePropValue value) {
+ // If this is the data message release the semaphore so the test can continue.
+ ArrayList<Integer> int32Values = value.value.int32Values;
+ if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) ==
+ VmsMessageType.DATA) {
+ mHalHandlerSemaphore.release();
+ }
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
new file mode 100644
index 0000000..3b3a94f
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2017 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.android.car.test;
+
+import android.annotation.ArrayRes;
+import android.car.Car;
+import android.car.VehicleAreaType;
+import android.car.annotation.FutureFeature;
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsSubscriberManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
+import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@FutureFeature
+public class VmsPublisherSubscriberTest extends MockedCarTestBase {
+ private static final int LAYER_ID = 88;
+ private static final int LAYER_VERSION = 19;
+ private static final int EXPECTED_PUBLISHER_ID = 0;
+ private static final String TAG = "VmsPubSubTest";
+
+ public static final VmsLayer LAYER = new VmsLayer(LAYER_ID, LAYER_VERSION);
+ public static final byte[] PAYLOAD = new byte[]{2, 3, 5, 7, 11, 13, 17};
+ private static final List<VmsLayer> AVAILABLE_LAYERS = new ArrayList<>(Arrays.asList(LAYER));
+
+ private HalHandler mHalHandler;
+ // Used to block until a value is propagated to the TestListener.onVmsMessageReceived.
+ private Semaphore mSubscriberSemaphore;
+ private Semaphore mAvailabilitySemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ /**
+ * Creates a context with the resource vmsPublisherClients overridden. The overridden value
+ * contains the name of the test service defined also in this test package.
+ */
+ @Override
+ protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
+ Context context = getContext()
+ .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
+ Resources resources = new Resources(context.getAssets(),
+ context.getResources().getDisplayMetrics(),
+ context.getResources().getConfiguration()) {
+ @Override
+ public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
+ if (id == com.android.car.R.array.vmsPublisherClients) {
+ return new String[]{"com.android.car.test/.VmsPublisherClientMockService"};
+ }
+ return super.getStringArray(id);
+ }
+ };
+ ContextWrapper wrapper = new ContextWrapper(context) {
+ @Override
+ public Resources getResources() {
+ return resources;
+ }
+ };
+ return wrapper;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.setUp();
+ mSubscriberSemaphore = new Semaphore(0);
+ mAvailabilitySemaphore = new Semaphore(0);
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.tearDown();
+ }
+
+ /**
+ * The method setUp initializes all the Car services, including the VmsPublisherService.
+ * The VmsPublisherService will start and configure its list of clients. This list was
+ * overridden in the method getCarServiceContext. Therefore, only VmsPublisherClientMockService
+ * will be started. This test method subscribes to a layer and triggers
+ * VmsPublisherClientMockService.onVmsSubscriptionChange. In turn, the mock service will publish
+ * a message, which is validated in this test.
+ */
+ public void testPublisherToSubscriber() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestListener listener = new TestListener();
+ vmsSubscriberManager.setListener(listener);
+ vmsSubscriberManager.subscribe(LAYER);
+
+ assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(LAYER, listener.getLayer());
+ assertTrue(Arrays.equals(PAYLOAD, listener.getPayload()));
+ }
+
+ /**
+ * The Mock service will get a publisher ID by sending its information when it will get
+ * ServiceReady as well as on SubscriptionChange. Since clients are not notified when
+ * publishers are assigned IDs, this test waits until the availability is changed which indicates
+ * that the Mock service has gotten its ServiceReady and publisherId.
+ */
+ public void testPublisherInfo() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ // Subscribe to layer as a way to make sure the mock client completed setting the information.
+ TestListener listener = new TestListener();
+ vmsSubscriberManager.setListener(listener);
+ vmsSubscriberManager.subscribe(LAYER);
+
+ assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+
+ byte[] info = vmsSubscriberManager.getPublisherInfo(EXPECTED_PUBLISHER_ID);
+ assertTrue(Arrays.equals(PAYLOAD, info));
+ }
+
+ /**
+ * The Mock service offers all the subscribed layers as available layers, so in this
+ * test the listener subscribes to a layer and verifies that it gets the notification that it
+ * is available.
+ */
+ public void testAvailability() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestListener listener = new TestListener();
+ vmsSubscriberManager.setListener(listener);
+ vmsSubscriberManager.subscribe(LAYER);
+
+ assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(AVAILABLE_LAYERS, listener.getAvailalbeLayers());
+ }
+
+ private class HalHandler implements MockedVehicleHal.VehicleHalPropertyHandler {
+ }
+
+ private class TestListener implements VmsSubscriberManager.VmsSubscriberClientListener {
+ private VmsLayer mLayer;
+ private byte[] mPayload;
+ private List<VmsLayer> mAvailableLayers;
+
+ @Override
+ public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
+ assertEquals(LAYER, layer);
+ assertTrue(Arrays.equals(PAYLOAD, payload));
+ mLayer = layer;
+ mPayload = payload;
+ mSubscriberSemaphore.release();
+ }
+
+ @Override
+ public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
+ assertEquals(AVAILABLE_LAYERS, availableLayers);
+ mAvailableLayers = availableLayers;
+ mAvailabilitySemaphore.release();
+ }
+
+ @Override
+ public void onCarDisconnected() {
+
+ }
+
+ public VmsLayer getLayer() {
+ return mLayer;
+ }
+
+ public byte[] getPayload() {
+ return mPayload;
+ }
+
+ public List<VmsLayer> getAvailalbeLayers() {
+ return mAvailableLayers;
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java b/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java
new file mode 100644
index 0000000..063d5cf
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2017 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.android.car.test;
+
+import android.car.Car;
+import android.car.VehicleAreaType;
+import android.car.annotation.FutureFeature;
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsSubscriberManager;
+import android.car.vms.VmsSubscriberManager.VmsSubscriberClientListener;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
+import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@FutureFeature
+@MediumTest
+public class VmsSubscriberManagerTest extends MockedCarTestBase {
+ private static final String TAG = "VmsSubscriberManagerTest";
+ private static final int SUBSCRIPTION_LAYER_ID = 2;
+ private static final int SUBSCRIPTION_LAYER_VERSION = 3;
+ private static final VmsLayer SUBSCRIPTION_LAYER = new VmsLayer(SUBSCRIPTION_LAYER_ID,
+ SUBSCRIPTION_LAYER_VERSION);
+
+ private static final int SUBSCRIPTION_DEPENDANT_LAYER_ID_1 = 4;
+ private static final int SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1 = 5;
+ private static final VmsLayer SUBSCRIPTION_DEPENDANT_LAYER_1 =
+ new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_1, SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1);
+
+ private static final int SUBSCRIPTION_DEPENDANT_LAYER_ID_2 = 6;
+ private static final int SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2 = 7;
+ private static final VmsLayer SUBSCRIPTION_DEPENDANT_LAYER_2 =
+ new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_2, SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2);
+
+ private static final int SUBSCRIPTION_UNSUPPORTED_LAYER_ID = 100;
+ private static final int SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION = 200;
+
+
+ private HalHandler mHalHandler;
+ // Used to block until the HAL property is updated in HalHandler.onPropertySet.
+ private Semaphore mHalHandlerSemaphore;
+ // Used to block until a value is propagated to the TestListener.onVmsMessageReceived.
+ private Semaphore mSubscriberSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.setUp();
+ mSubscriberSemaphore = new Semaphore(0);
+ mHalHandlerSemaphore = new Semaphore(0);
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.tearDown();
+ }
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber.
+ public void testSubscribe() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestListener listener = new TestListener();
+ vmsSubscriberManager.setListener(listener);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
+
+ // Inject a value and wait for its callback in TestListener.onVmsMessageReceived.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ v.value.int32Values.add(VmsMessageType.DATA); // MessageType
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.bytes.add((byte) 0xa);
+ v.value.bytes.add((byte) 0xb);
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ getMockedVehicleHal().injectEvent(v);
+ assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(SUBSCRIPTION_LAYER, listener.getLayer());
+ byte[] expectedPayload = {(byte) 0xa, (byte) 0xb};
+ assertTrue(Arrays.equals(expectedPayload, listener.getPayload()));
+ }
+
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber.
+ public void testSubscribeAll() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestListener listener = new TestListener();
+ vmsSubscriberManager.setListener(listener);
+ vmsSubscriberManager.subscribeAll();
+
+ // Inject a value and wait for its callback in TestListener.onVmsMessageReceived.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ v.value.int32Values.add(VmsMessageType.DATA); // MessageType
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.bytes.add((byte) 0xa);
+ v.value.bytes.add((byte) 0xb);
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ getMockedVehicleHal().injectEvent(v);
+ assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(SUBSCRIPTION_LAYER, listener.getLayer());
+ byte[] expectedPayload = {(byte) 0xa, (byte) 0xb};
+ assertTrue(Arrays.equals(expectedPayload, listener.getPayload()));
+ }
+
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber.
+ public void testSimpleAvailableLayers() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestListener listener = new TestListener();
+ vmsSubscriberManager.setListener(listener);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
+
+ // Inject a value and wait for its callback in TestListener.onLayersAvailabilityChange.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ /*
+ Offering:
+ Layer | Dependency
+ ====================
+ (2, 3) | {}
+
+ Expected availability:
+ {(2, 3)}
+ */
+ v.value.int32Values.addAll(
+ Arrays.asList(
+ VmsMessageType.OFFERING, // MessageType
+ 1, // Number of offered layers
+
+ SUBSCRIPTION_LAYER_ID,
+ SUBSCRIPTION_LAYER_VERSION,
+ 0 // number of dependencies for layer
+ )
+ );
+
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ List<VmsLayer> expectedAvailableLayers = new ArrayList<>(Arrays.asList(SUBSCRIPTION_LAYER));
+
+ getMockedVehicleHal().injectEvent(v);
+ assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(expectedAvailableLayers, listener.getAvailableLayers());
+ }
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber.
+ public void testComplexAvailableLayers() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestListener listener = new TestListener();
+ vmsSubscriberManager.setListener(listener);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
+
+ // Inject a value and wait for its callback in TestListener.onLayersAvailabilityChange.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ /*
+ Offering:
+ Layer | Dependency
+ ====================
+ (2, 3) | {}
+ (4, 5) | {(2, 3)}
+ (6, 7) | {(2, 3), (4, 5)}
+ (6, 7) | {(100, 200)}
+
+ Expected availability:
+ {(2, 3), (4, 5), (6, 7)}
+ */
+
+ v.value.int32Values.addAll(
+ Arrays.asList(
+ VmsMessageType.OFFERING, // MessageType
+ 4, // Number of offered layers
+
+ SUBSCRIPTION_LAYER_ID,
+ SUBSCRIPTION_LAYER_VERSION,
+ 0, // number of dependencies for layer
+
+ SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1,
+ 1, // number of dependencies for layer
+ SUBSCRIPTION_LAYER_ID,
+ SUBSCRIPTION_LAYER_VERSION,
+
+ SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2,
+ 2, // number of dependencies for layer
+ SUBSCRIPTION_LAYER_ID,
+ SUBSCRIPTION_LAYER_VERSION,
+ SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1,
+
+ SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2,
+ 1, // number of dependencies for layer
+ SUBSCRIPTION_UNSUPPORTED_LAYER_ID,
+ SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION
+ )
+ );
+
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ List<VmsLayer> expectedAvailableLayers =
+ new ArrayList<>(Arrays.asList(SUBSCRIPTION_LAYER,
+ SUBSCRIPTION_DEPENDANT_LAYER_1,
+ SUBSCRIPTION_DEPENDANT_LAYER_2));
+
+ getMockedVehicleHal().injectEvent(v);
+ assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(expectedAvailableLayers, listener.getAvailableLayers());
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ private VehiclePropValue mValue;
+
+ @Override
+ public synchronized void onPropertySet(VehiclePropValue value) {
+ mValue = value;
+ mHalHandlerSemaphore.release();
+ }
+
+ @Override
+ public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ return mValue != null ? mValue : value;
+ }
+
+ @Override
+ public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
+ Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
+ }
+
+ @Override
+ public synchronized void onPropertyUnsubscribe(int property) {
+ Log.d(TAG, "onPropertyUnSubscribe property " + property);
+ }
+
+ public VehiclePropValue getValue() {
+ return mValue;
+ }
+ }
+
+
+ private class TestListener implements VmsSubscriberClientListener{
+ private VmsLayer mLayer;
+ private byte[] mPayload;
+ private List<VmsLayer> mAvailableLayers = new ArrayList<>();
+
+ @Override
+ public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
+ Log.d(TAG, "onVmsMessageReceived: layer: " + layer + " Payload: " + payload);
+ mLayer = layer;
+ mPayload = payload;
+ mSubscriberSemaphore.release();
+ }
+
+ @Override
+ public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
+ Log.d(TAG, "onLayersAvailabilityChange: Layers: " + availableLayers);
+ mAvailableLayers.addAll(availableLayers);
+ mSubscriberSemaphore.release();
+ }
+
+ @Override
+ public void onCarDisconnected() {
+
+ }
+
+ public VmsLayer getLayer() {
+ return mLayer;
+ }
+
+ public byte[] getPayload() {
+ return mPayload;
+ }
+
+ public List<VmsLayer> getAvailableLayers() {
+ return mAvailableLayers;
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsTestUtils.java b/tests/carservice_test/src/com/android/car/test/VmsTestUtils.java
new file mode 100644
index 0000000..2f3af52
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsTestUtils.java
@@ -0,0 +1,16 @@
+package com.android.car.test;
+
+import android.car.annotation.FutureFeature;
+import android.util.Log;
+
+import com.android.car.internal.FeatureConfiguration;
+
+@FutureFeature
+public class VmsTestUtils {
+ public static boolean canRunTest(String tag) {
+ if (!FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
+ Log.i(tag, "Skipping test because ENABLE_VEHICLE_MAP_SERVICE = false");
+ }
+ return FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE;
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
index ac9654c..d6fd68d 100644
--- a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
@@ -23,6 +23,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -64,6 +65,15 @@
super.setUp();
}
+ public void testNoOffering() {
+ assertTrue(mLayersAvailability.getAvailableLayers().isEmpty());
+ }
+
+ public void testEmptyOffering() {
+ mLayersAvailability.setPublishersOffering(Collections.EMPTY_LIST);
+ assertTrue(mLayersAvailability.getAvailableLayers().isEmpty());
+ }
+
public void testSingleLayerNoDeps() throws Exception {
Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
expectedAvailableLayers.add(LAYER_X);
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java b/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java
new file mode 100644
index 0000000..2b75012
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 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.android.car;
+
+import android.car.annotation.FutureFeature;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.Arrays;
+import java.util.Map;
+
+@FutureFeature
+@SmallTest
+public class VmsPublishersInfoTest extends AndroidTestCase {
+ public static final byte[] MOCK_INFO_0 = new byte[]{2, 3, 5, 7, 11, 13, 17};
+ public static final byte[] SAME_MOCK_INFO_0 = new byte[]{2, 3, 5, 7, 11, 13, 17};
+ public static final byte[] MOCK_INFO_1 = new byte[]{2, 3, 5, 7, 11, 13, 17, 19};
+
+ private VmsPublishersInfo mVmsPublishersInfo;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mVmsPublishersInfo = new VmsPublishersInfo();
+ }
+
+ // Test one info sanity
+ public void testSingleInfo() throws Exception {
+ int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
+ assertEquals(0, id);
+
+ byte[] info = mVmsPublishersInfo.getPublisherInfo(id);
+ assertTrue(Arrays.equals(MOCK_INFO_0, info));
+ }
+
+ // Test one info sanity - wrong ID fails.
+ public void testSingleInfoWrongId() throws Exception {
+ int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
+ assertEquals(0, id);
+
+ try {
+ byte[] info = mVmsPublishersInfo.getPublisherInfo(id + 1);
+ }
+ catch (NullPointerException e) {
+ return;
+ }
+ fail();
+ }
+
+ // Test two infos.
+ public void testTwoInfos() throws Exception {
+ int id0 = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
+ int id1 = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_1);
+ assertEquals(0, id0);
+ assertEquals(1, id1);
+
+ byte[] info0 = mVmsPublishersInfo.getPublisherInfo(id0);
+ byte[] info1 = mVmsPublishersInfo.getPublisherInfo(id1);
+ assertTrue(Arrays.equals(MOCK_INFO_0, info0));
+ assertTrue(Arrays.equals(MOCK_INFO_1, info1));
+ }
+
+ // Test same info twice get the same ID.
+ public void testSingleInfoInsertedTwice() throws Exception {
+ int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
+ assertEquals(0, id);
+
+ int sameId = mVmsPublishersInfo.getIdForInfo(SAME_MOCK_INFO_0);
+ assertEquals(sameId, id);
+ }
+}
diff --git a/car-ui-provider/Android.mk b/tests/obd2_app/Android.mk
similarity index 69%
rename from car-ui-provider/Android.mk
rename to tests/obd2_app/Android.mk
index f63674a..7e1e22f 100644
--- a/car-ui-provider/Android.mk
+++ b/tests/obd2_app/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2015 The Android Open Source Project
+# Copyright (C) 2017 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.
@@ -14,23 +14,27 @@
#
#
-#disble build in PDK, should add prebuilts/fullsdk to make this work
-ifneq ($(TARGET_BUILD_PDK),true)
-
LOCAL_PATH:= $(call my-dir)
+
include $(CLEAR_VARS)
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := optional
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := CarUiProvider
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := nostripping
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-include packages/services/Car/car-support-lib/car-support.mk
+LOCAL_PACKAGE_NAME := Obd2App
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_STATIC_JAVA_LIBRARIES += \
+ com.android.car.obd2 \
+
+LOCAL_JAVA_VERSION := 1.8
include $(BUILD_PACKAGE)
-
-endif #TARGET_BUILD_PDK
diff --git a/tests/obd2_app/AndroidManifest.xml b/tests/obd2_app/AndroidManifest.xml
new file mode 100644
index 0000000..184d939
--- /dev/null
+++ b/tests/obd2_app/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2017 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
+
+
+ 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.car.obd2app">
+
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application
+ android:allowBackup="true"
+ android:debuggable="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".SettingsActivity"/>
+ </application>
+
+</manifest>
diff --git a/tests/obd2_app/res/drawable/ic_info_black_24dp.xml b/tests/obd2_app/res/drawable/ic_info_black_24dp.xml
new file mode 100644
index 0000000..b9139d1
--- /dev/null
+++ b/tests/obd2_app/res/drawable/ic_info_black_24dp.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24dp">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z"/>
+</vector>
diff --git a/tests/obd2_app/res/drawable/ic_notifications_black_24dp.xml b/tests/obd2_app/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 0000000..486956c
--- /dev/null
+++ b/tests/obd2_app/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24dp">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z"/>
+</vector>
diff --git a/tests/obd2_app/res/drawable/ic_sync_black_24dp.xml b/tests/obd2_app/res/drawable/ic_sync_black_24dp.xml
new file mode 100644
index 0000000..8511efa
--- /dev/null
+++ b/tests/obd2_app/res/drawable/ic_sync_black_24dp.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24dp">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01,-.25 1.97,-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0,-4.42,-3.58,-8,-8,-8zm0 14c-3.31 0,-6,-2.69,-6,-6 0,-1.01.25,-1.97.7,-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4,-4,-4,-4v3z"/>
+</vector>
\ No newline at end of file
diff --git a/tests/obd2_app/res/layout/activity_main.xml b/tests/obd2_app/res/layout/activity_main.xml
new file mode 100644
index 0000000..29d7bd2
--- /dev/null
+++ b/tests/obd2_app/res/layout/activity_main.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/activity_main"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ tools:context="com.google.android.car.obd2app.MainActivity">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentStart="true"
+ android:id="@+id/statusBar"
+ android:layout_alignParentEnd="true"
+ android:text="Nothing to say"
+ android:minLines="10"/>
+ <Button
+ android:text="Connect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:id="@+id/connection"/>
+ <Button
+ android:text="Settings"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/settings"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:onClick="doSettings"/>
+</RelativeLayout>
diff --git a/tests/obd2_app/res/mipmap-hdpi/ic_launcher.png b/tests/obd2_app/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/tests/obd2_app/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/obd2_app/res/mipmap-mdpi/ic_launcher.png b/tests/obd2_app/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/tests/obd2_app/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/obd2_app/res/mipmap-xhdpi/ic_launcher.png b/tests/obd2_app/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/tests/obd2_app/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/obd2_app/res/mipmap-xxhdpi/ic_launcher.png b/tests/obd2_app/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/tests/obd2_app/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/obd2_app/res/mipmap-xxxhdpi/ic_launcher.png b/tests/obd2_app/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/tests/obd2_app/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/obd2_app/res/values-w820dp/dimens.xml b/tests/obd2_app/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..1da9658
--- /dev/null
+++ b/tests/obd2_app/res/values-w820dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/car-ui-provider/AndroidManifest.xml b/tests/obd2_app/res/values/arrays.xml
similarity index 60%
copy from car-ui-provider/AndroidManifest.xml
copy to tests/obd2_app/res/values/arrays.xml
index ffbdfed..2bd7ce3 100644
--- a/car-ui-provider/AndroidManifest.xml
+++ b/tests/obd2_app/res/values/arrays.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 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.
@@ -13,11 +13,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.car.ui.provider" >
- <uses-sdk android:minSdkVersion="23"
- android:targetSdkVersion="23" />
- <application android:label="@string/app_name" />
-</manifest>
+<resources>
+ <string-array name="scan_delay_entries">
+ <item>2 seconds</item>
+ <item>5 seconds</item>
+ <item>10 seconds</item>
+ </string-array>
+ <string-array name="scan_delay_entryValues">
+ <item>2</item>
+ <item>5</item>
+ <item>10</item>
+ </string-array>
+</resources>
diff --git a/car-ui-provider/AndroidManifest.xml b/tests/obd2_app/res/values/colors.xml
similarity index 60%
rename from car-ui-provider/AndroidManifest.xml
rename to tests/obd2_app/res/values/colors.xml
index ffbdfed..49a370a 100644
--- a/car-ui-provider/AndroidManifest.xml
+++ b/tests/obd2_app/res/values/colors.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 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.
@@ -14,10 +14,8 @@
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.car.ui.provider" >
- <uses-sdk android:minSdkVersion="23"
- android:targetSdkVersion="23" />
- <application android:label="@string/app_name" />
-</manifest>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/car-ui-provider/AndroidManifest.xml b/tests/obd2_app/res/values/dimens.xml
similarity index 60%
copy from car-ui-provider/AndroidManifest.xml
copy to tests/obd2_app/res/values/dimens.xml
index ffbdfed..261477e 100644
--- a/car-ui-provider/AndroidManifest.xml
+++ b/tests/obd2_app/res/values/dimens.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 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.
@@ -13,11 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.car.ui.provider" >
- <uses-sdk android:minSdkVersion="23"
- android:targetSdkVersion="23" />
- <application android:label="@string/app_name" />
-</manifest>
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/tests/obd2_app/res/values/strings.xml b/tests/obd2_app/res/values/strings.xml
new file mode 100644
index 0000000..c615c4f
--- /dev/null
+++ b/tests/obd2_app/res/values/strings.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<resources>
+ <string name="app_name">Obd2App</string>
+ <string name="title_activity_settings">Settings</string>
+
+ <!-- Strings related to Settings -->
+
+ <!-- Example General settings -->
+ <string name="pref_header_general">General</string>
+
+ <string name="pref_title_social_recommendations">Enable social recommendations</string>
+ <string name="pref_description_social_recommendations">Recommendations for people to contact based
+ on your message history
+ </string>
+
+ <string name="pref_title_display_name">Display name</string>
+ <string name="pref_default_display_name">John Smith</string>
+
+ <string name="pref_title_add_friends_to_messages">Add friends to messages</string>
+ <string-array name="pref_example_list_titles">
+ <item>Always</item>
+ <item>When possible</item>
+ <item>Never</item>
+ </string-array>
+ <string-array name="pref_example_list_values">
+ <item>1</item>
+ <item>0</item>
+ <item>-1</item>
+ </string-array>
+
+ <!-- Example settings for Data & Sync -->
+ <string name="pref_header_data_sync">Data & sync</string>
+
+ <string name="pref_title_sync_frequency">Sync frequency</string>
+ <string-array name="pref_sync_frequency_titles">
+ <item>15 minutes</item>
+ <item>30 minutes</item>
+ <item>1 hour</item>
+ <item>3 hours</item>
+ <item>6 hours</item>
+ <item>Never</item>
+ </string-array>
+ <string-array name="pref_sync_frequency_values">
+ <item>15</item>
+ <item>30</item>
+ <item>60</item>
+ <item>180</item>
+ <item>360</item>
+ <item>-1</item>
+ </string-array>
+
+ <string-array name="list_preference_entries">
+ <item>Entry 1</item>
+ <item>Entry 2</item>
+ <item>Entry 3</item>
+ </string-array>
+
+ <string-array name="list_preference_entry_values">
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </string-array>
+
+ <string-array name="multi_select_list_preference_default_value"/>
+
+ <string name="pref_title_system_sync_settings">System sync settings</string>
+
+ <!-- Example settings for Notifications -->
+ <string name="pref_header_notifications">Notifications</string>
+
+ <string name="pref_title_new_message_notifications">New message notifications</string>
+
+ <string name="pref_title_ringtone">Ringtone</string>
+ <string name="pref_ringtone_silent">Silent</string>
+
+ <string name="pref_title_vibrate">Vibrate</string>
+</resources>
diff --git a/car-ui-provider/AndroidManifest.xml b/tests/obd2_app/res/values/styles.xml
similarity index 60%
copy from car-ui-provider/AndroidManifest.xml
copy to tests/obd2_app/res/values/styles.xml
index ffbdfed..c21c0ac 100644
--- a/car-ui-provider/AndroidManifest.xml
+++ b/tests/obd2_app/res/values/styles.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 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.
@@ -13,11 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<resources>
+ <style name="AppTheme" parent="android:Theme.Material">
+ </style>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.car.ui.provider" >
- <uses-sdk android:minSdkVersion="23"
- android:targetSdkVersion="23" />
- <application android:label="@string/app_name" />
-</manifest>
+</resources>
diff --git a/tests/obd2_app/res/xml/preferences.xml b/tests/obd2_app/res/xml/preferences.xml
new file mode 100644
index 0000000..0c6f534
--- /dev/null
+++ b/tests/obd2_app/res/xml/preferences.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <com.google.android.car.obd2app.BluetoothPreference
+ android:key="bluetooth_mac"
+ android:title="OBD2 Dongle"
+ android:dialogTitle="Select OBD2 Scanner" />
+ <com.google.android.car.obd2app.IntegerListPreference
+ android:key="scan_delay"
+ android:title="Time between queries"
+ android:dialogTitle="Select Delay"
+ android:entries="@array/scan_delay_entries"
+ android:entryValues="@array/scan_delay_entryValues"
+ android:defaultValue="2"/>
+</PreferenceScreen>
diff --git a/tests/obd2_app/src/com/google/android/car/obd2app/BluetoothPreference.java b/tests/obd2_app/src/com/google/android/car/obd2app/BluetoothPreference.java
new file mode 100644
index 0000000..59da4c0
--- /dev/null
+++ b/tests/obd2_app/src/com/google/android/car/obd2app/BluetoothPreference.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.obd2app;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BluetoothPreference extends ListPreference {
+ private static final class DeviceEntry {
+ private final String mName;
+ private final String mAddress;
+
+ DeviceEntry(BluetoothDevice device) {
+ mAddress = device.getAddress();
+ if (device.getName() == null) {
+ mName = mAddress;
+ } else {
+ mName = device.getName();
+ }
+ }
+
+ String getName() {
+ return mName;
+ }
+
+ String getAddress() {
+ return mAddress;
+ }
+ }
+
+ public BluetoothPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter();
+ List<DeviceEntry> pairedDevices = new ArrayList<>();
+ defaultAdapter
+ .getBondedDevices()
+ .forEach((BluetoothDevice device) -> pairedDevices.add(new DeviceEntry(device)));
+ setEntries(pairedDevices.stream().map(DeviceEntry::getName).toArray(String[]::new));
+ setEntryValues(pairedDevices.stream().map(DeviceEntry::getAddress).toArray(String[]::new));
+ }
+
+ public BluetoothPreference(Context context) {
+ this(context, null);
+ }
+}
diff --git a/tests/obd2_app/src/com/google/android/car/obd2app/IntegerListPreference.java b/tests/obd2_app/src/com/google/android/car/obd2app/IntegerListPreference.java
new file mode 100644
index 0000000..6e9e9dc
--- /dev/null
+++ b/tests/obd2_app/src/com/google/android/car/obd2app/IntegerListPreference.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.obd2app;
+
+import android.content.Context;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+
+public class IntegerListPreference extends ListPreference {
+ public IntegerListPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public IntegerListPreference(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected boolean persistString(String value) {
+ return value != null && persistInt(Integer.valueOf(value));
+ }
+
+ @Override
+ protected String getPersistedString(String defaultReturnValue) {
+ if (getSharedPreferences().contains(getKey())) {
+ return String.valueOf(getPersistedInt(2));
+ } else {
+ return defaultReturnValue;
+ }
+ }
+}
diff --git a/tests/obd2_app/src/com/google/android/car/obd2app/MainActivity.java b/tests/obd2_app/src/com/google/android/car/obd2app/MainActivity.java
new file mode 100644
index 0000000..dc38b5c
--- /dev/null
+++ b/tests/obd2_app/src/com/google/android/car/obd2app/MainActivity.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 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.obd2app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+import java.util.Timer;
+
+public class MainActivity extends Activity implements StatusNotification {
+ public static final String TAG = MainActivity.class.getSimpleName();
+
+ private static final String BLUETOOTH_MAC_PREFERENCE_ID = "bluetooth_mac";
+ private static final String SCAN_DELAY_PREFERENCE_ID = "scan_delay";
+
+ private Obd2CollectionTask mCollectionTask = null;
+ private final Timer mTimer = new Timer("com.google.android.car.obd2app.collection");
+
+ private String getBluetoothDongleMacFromPreferences(String defaultValue) {
+ SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ return appPreferences.getString(BLUETOOTH_MAC_PREFERENCE_ID, defaultValue);
+ }
+
+ private int getScanDelayFromPreferences(int defaultValue) {
+ SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ return appPreferences.getInt(SCAN_DELAY_PREFERENCE_ID, defaultValue);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
+ String bluetoothDongleMac = getBluetoothDongleMacFromPreferences("");
+ if (TextUtils.isEmpty(bluetoothDongleMac)) {
+ notifyNoDongle();
+ } else {
+ notifyPaired(bluetoothDongleMac);
+ }
+ findViewById(R.id.connection)
+ .setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleConnection(v);
+ }
+ });
+ Log.i(TAG, "I did all the things");
+ }
+
+ private void stopConnection() {
+ mCollectionTask.cancel();
+ mTimer.purge();
+ mCollectionTask = null;
+ }
+
+ @Override
+ protected void onDestroy() {
+ stopConnection();
+ }
+
+ public void doSettings(View view) {
+ Intent launchSettings = new Intent(this, SettingsActivity.class);
+ startActivity(launchSettings);
+ }
+
+ @Override
+ public void notify(String status) {
+ Log.i(TAG, status);
+ runOnUiThread(() -> ((TextView) findViewById(R.id.statusBar)).setText(status));
+ }
+
+ public void handleConnection(View view) {
+ String deviceAddress = getBluetoothDongleMacFromPreferences("");
+ Log.i(TAG, "Considering a connection to " + deviceAddress);
+ if (TextUtils.isEmpty(deviceAddress)) {
+ notifyNoDongle();
+ }
+ if (mCollectionTask == null) {
+ mCollectionTask = Obd2CollectionTask.create(this, this, deviceAddress);
+ if (null == mCollectionTask) {
+ notifyConnectionFailed();
+ return;
+ }
+ final int delay = 1000 * getScanDelayFromPreferences(2);
+ mTimer.scheduleAtFixedRate(mCollectionTask, delay, delay);
+ ((Button) view).setText("Disconnect");
+ } else {
+ stopConnection();
+ ((Button) view).setText("Connect");
+ }
+ }
+}
diff --git a/tests/obd2_app/src/com/google/android/car/obd2app/Obd2CollectionTask.java b/tests/obd2_app/src/com/google/android/car/obd2app/Obd2CollectionTask.java
new file mode 100644
index 0000000..b38cf30
--- /dev/null
+++ b/tests/obd2_app/src/com/google/android/car/obd2app/Obd2CollectionTask.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 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.obd2app;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.util.JsonWriter;
+import android.util.Log;
+import com.android.car.obd2.Obd2Connection;
+import com.android.car.obd2.Obd2FreezeFrameGenerator;
+import com.android.car.obd2.Obd2LiveFrameGenerator;
+import com.android.car.obd2.connections.BluetoothConnection;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Objects;
+import java.util.TimerTask;
+
+public class Obd2CollectionTask extends TimerTask {
+ private final Obd2Connection mConnection;
+ private final Obd2LiveFrameGenerator mLiveFrameGenerator;
+ private final Obd2FreezeFrameGenerator mFreezeFrameGenerator;
+ private final StatusNotification mStatusNotification;
+ private final JsonWriter mJsonWriter;
+
+ public static @Nullable Obd2CollectionTask create(
+ Context context, StatusNotification statusNotification, String deviceAddress) {
+ try {
+ return new Obd2CollectionTask(
+ Objects.requireNonNull(context),
+ Objects.requireNonNull(statusNotification),
+ Objects.requireNonNull(deviceAddress));
+ } catch (IOException | InterruptedException | IllegalStateException e) {
+ Log.i(MainActivity.TAG, "Connection failed due to exception", e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean cancel() {
+ synchronized (mJsonWriter) {
+ try {
+ mJsonWriter.endArray();
+ mJsonWriter.flush();
+ mJsonWriter.close();
+ } catch (IOException e) {
+ Log.w(MainActivity.TAG, "IOException during close", e);
+ }
+ return super.cancel();
+ }
+ }
+
+ @Override
+ public void run() {
+ if (!mConnection.isConnected()) {
+ if (!mConnection.reconnect()) {
+ mStatusNotification.notifyDisconnected();
+ return;
+ }
+ }
+
+ try {
+ synchronized (mJsonWriter) {
+ mLiveFrameGenerator.generate(mJsonWriter);
+ mFreezeFrameGenerator.generate(mJsonWriter);
+ mJsonWriter.flush();
+ }
+ mStatusNotification.notifyDataCapture();
+ } catch (Exception e) {
+ mStatusNotification.notifyException(e);
+ }
+ }
+
+ Obd2CollectionTask(Context context, StatusNotification statusNotification, String deviceAddress)
+ throws IOException, InterruptedException {
+ if (!isExternalStorageWriteable())
+ throw new IOException("Cannot write data to external storage");
+ mStatusNotification = statusNotification;
+ BluetoothConnection bluetoothConnection = new BluetoothConnection(deviceAddress);
+ if (!bluetoothConnection.isConnected()) {
+ statusNotification.notifyConnectionFailed();
+ throw new IllegalStateException("Unable to connect to remote end.");
+ }
+ mConnection = new Obd2Connection(bluetoothConnection);
+ mLiveFrameGenerator = new Obd2LiveFrameGenerator(mConnection);
+ mFreezeFrameGenerator = new Obd2FreezeFrameGenerator(mConnection);
+ mJsonWriter =
+ new JsonWriter(
+ new OutputStreamWriter(
+ new FileOutputStream(getFilenameForStorage(context))));
+ mJsonWriter.beginArray();
+ }
+
+ private static boolean isExternalStorageWriteable() {
+ String state = Environment.getExternalStorageState();
+ return (Environment.MEDIA_MOUNTED.equals(state));
+ }
+
+ private static File getFilenameForStorage(Context context) {
+ String basename = String.format("obd2app.capture.%d", SystemClock.elapsedRealtimeNanos());
+ return new File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), basename);
+ }
+}
diff --git a/tests/obd2_app/src/com/google/android/car/obd2app/SettingsActivity.java b/tests/obd2_app/src/com/google/android/car/obd2app/SettingsActivity.java
new file mode 100644
index 0000000..23f120e
--- /dev/null
+++ b/tests/obd2_app/src/com/google/android/car/obd2app/SettingsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.obd2app;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+public class SettingsActivity extends PreferenceActivity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+ }
+}
diff --git a/tests/obd2_app/src/com/google/android/car/obd2app/StatusNotification.java b/tests/obd2_app/src/com/google/android/car/obd2app/StatusNotification.java
new file mode 100644
index 0000000..185c384
--- /dev/null
+++ b/tests/obd2_app/src/com/google/android/car/obd2app/StatusNotification.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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.obd2app;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+interface StatusNotification {
+ void notify(String status);
+
+ default void notifyNoDongle() {
+ notify("No OBD2 dongle paired. Go to Settings.");
+ }
+
+ default void notifyPaired(String deviceAddress) {
+ notify("Paired to " + deviceAddress + ". Ready to capture data.");
+ }
+
+ default void notifyConnectionFailed() {
+ notify("Unable to connect.");
+ }
+
+ default void notifyConnected(String deviceAddress) {
+ notify("Connected to " + deviceAddress + ". Starting data capture.");
+ }
+
+ default void notifyDataCapture() {
+ LocalDateTime now = LocalDateTime.now();
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MMMM dd yyyy hh:mm:ssa");
+ notify("Successfully captured data at " + now.format(dateTimeFormatter));
+ }
+
+ default void notifyException(Exception e) {
+ StringWriter stringWriter = new StringWriter(1024);
+ e.printStackTrace(new PrintWriter(stringWriter));
+ notify("Exception occurred.\n" + stringWriter.toString());
+ }
+
+ default void notifyDisconnected() {
+ notify("Lost connection to remote end. Will try to reconnect.");
+ }
+}
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/IntegerArrayStreamTest.java b/tests/obd2_test/src/com/android/car/obd2/test/IntegerArrayStreamTest.java
index 3c3b1e6..e9bf98b 100644
--- a/tests/obd2_test/src/com/android/car/obd2/test/IntegerArrayStreamTest.java
+++ b/tests/obd2_test/src/com/android/car/obd2/test/IntegerArrayStreamTest.java
@@ -59,4 +59,14 @@
assertFalse(stream.expect(4, 6));
assertEquals(5, stream.peek());
}
+
+ @Test
+ public void testIsEmpty() {
+ IntegerArrayStream stream = new IntegerArrayStream(DATA_SET);
+ assertFalse(stream.isEmpty());
+ stream.expect(1, 2, 3, 4, 5);
+ assertFalse(stream.isEmpty());
+ stream.consume();
+ assertTrue(stream.isEmpty());
+ }
}
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2CommandTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2CommandTest.java
index b342cbc..f4ac13d 100644
--- a/tests/obd2_test/src/com/android/car/obd2/test/Obd2CommandTest.java
+++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2CommandTest.java
@@ -106,7 +106,7 @@
String[] commandToSend = new String[] {String.format("02%02X 01\r", pid)};
String[] responseToGet =
- new String[] {String.format("02 %02X 01 %s", pid, responseBytes), OBD2_PROMPT};
+ new String[] {String.format("42 %02X 01 %s", pid, responseBytes), OBD2_PROMPT};
MockObd2UnderlyingTransport transport =
new MockObd2UnderlyingTransport(
@@ -134,7 +134,7 @@
String[] commandToSend = new String[] {String.format("02%02X 01\r", pid)};
String[] responseToGet =
- new String[] {String.format("02 %02X 01 %s", pid, responseBytes), OBD2_PROMPT};
+ new String[] {String.format("42 %02X 01 %s", pid, responseBytes), OBD2_PROMPT};
MockObd2UnderlyingTransport transport =
new MockObd2UnderlyingTransport(
@@ -213,7 +213,7 @@
@Test
public void testRpm() {
- checkCommand(0x0C, "12 0F", 4611);
+ checkCommand(0x0C, "12 0F", 1155);
}
@Test
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java
new file mode 100644
index 0000000..20919a1
--- /dev/null
+++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 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.android.car.obd2.test;
+
+import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_FREEZE_FRAME;
+import static com.android.car.obd2.test.Utils.concatIntArrays;
+import static com.android.car.obd2.test.Utils.stringsToIntArray;
+import static org.junit.Assert.*;
+
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import com.android.car.obd2.Obd2Connection;
+import com.android.car.obd2.Obd2FreezeFrameGenerator;
+import com.android.car.vehiclehal.DiagnosticJsonReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import org.junit.Test;
+
+public class Obd2FreezeFrameGeneratorTest {
+ private static final String[] EXPECTED_INIT_COMMANDS =
+ new String[] {
+ "ATD\r", "ATZ\r", "AT E0\r", "AT L0\r", "AT S0\r", "AT H0\r", "AT SP 0\r"
+ };
+
+ private static final String OBD2_PROMPT = ">";
+
+ private static final String[] EXPECTED_INIT_RESPONSES =
+ new String[] {
+ OBD2_PROMPT,
+ OBD2_PROMPT,
+ OBD2_PROMPT,
+ OBD2_PROMPT,
+ OBD2_PROMPT,
+ OBD2_PROMPT,
+ OBD2_PROMPT
+ };
+
+ private static final String[] EXPECTED_DISCOVERY_COMMANDS =
+ new String[] {"0100\r", "0120\r", "0140\r", "0160\r"};
+
+ private static final String[] EXPECTED_DISCOVERY_RESPONSES =
+ new String[] {"00 00 00 18 00 00", OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT};
+
+ private static final String[] EXPECTED_MODE3_COMMANDS = new String[] {"03\r"};
+
+ private static final String[] EXPECTED_MODE3_RESPONSES =
+ new String[] {
+ "0300E0:4306010002001:030043008200C12:0000000000000043010101", OBD2_PROMPT
+ };
+
+ private static final String[] EXPECTED_FRAME_COMMANDS =
+ new String[] {"020C 00\r", "020D 00\r", "020C 01\r", "020D 01\r"};
+
+ private static final String[] EXPECTED_FRAME_RESPONSES =
+ new String[] {
+ "42 0C 00 12 0F",
+ OBD2_PROMPT,
+ "42 0D 00 82",
+ OBD2_PROMPT,
+ "42 0C 01 12 0F",
+ OBD2_PROMPT,
+ "42 0D 01 83",
+ OBD2_PROMPT
+ };
+
+ @Test
+ public void testObd2FreezeFrameGeneration() throws Exception {
+ MockObd2UnderlyingTransport transport =
+ new MockObd2UnderlyingTransport(
+ concatIntArrays(
+ stringsToIntArray(EXPECTED_INIT_COMMANDS),
+ stringsToIntArray(EXPECTED_DISCOVERY_COMMANDS),
+ stringsToIntArray(EXPECTED_MODE3_COMMANDS),
+ stringsToIntArray(EXPECTED_FRAME_COMMANDS)),
+ concatIntArrays(
+ stringsToIntArray(EXPECTED_INIT_RESPONSES),
+ stringsToIntArray(EXPECTED_DISCOVERY_RESPONSES),
+ stringsToIntArray(EXPECTED_MODE3_RESPONSES),
+ stringsToIntArray(EXPECTED_FRAME_RESPONSES)));
+ Obd2Connection obd2Connection = new Obd2Connection(transport);
+ Obd2FreezeFrameGenerator obd2Generator = new Obd2FreezeFrameGenerator(obd2Connection);
+ StringWriter stringWriter = new StringWriter(1024);
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ obd2Generator.generate(jsonWriter);
+ jsonWriter.endArray();
+ JsonReader jsonReader = new JsonReader(new StringReader(stringWriter.toString()));
+ DiagnosticJsonReader diagnosticJsonReader = new DiagnosticJsonReader();
+ jsonReader.beginArray();
+ VehiclePropValue vehiclePropValue = diagnosticJsonReader.build(jsonReader);
+ assertEquals(OBD2_FREEZE_FRAME, vehiclePropValue.prop);
+ assertEquals(1155, (long) vehiclePropValue.value.int32Values.get(0xC));
+ assertEquals(130, (long) vehiclePropValue.value.int32Values.get(0xD));
+ vehiclePropValue = diagnosticJsonReader.build(jsonReader);
+ assertEquals(OBD2_FREEZE_FRAME, vehiclePropValue.prop);
+ assertEquals(1155, (long) vehiclePropValue.value.int32Values.get(0xC));
+ assertEquals(131, (long) vehiclePropValue.value.int32Values.get(0xD));
+ }
+}
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
index e2022c1..ba3dbb8 100644
--- a/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
+++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
@@ -54,7 +54,7 @@
new String[] {"0100\r", "0120\r", "0140\r", "0160\r"};
private static final String[] EXPECTED_DISCOVERY_RESPONSES =
- new String[] {"00 00 00 0C 00 00", OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT};
+ new String[] {"00 00 00 18 00 00", OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT};
private static final String[] EXPECTED_FRAME_COMMANDS = new String[] {"010C\r", "010D\r"};
@@ -82,7 +82,7 @@
DiagnosticJsonReader diagnosticJsonReader = new DiagnosticJsonReader();
VehiclePropValue vehiclePropValue = diagnosticJsonReader.build(jsonReader);
assertEquals(OBD2_LIVE_FRAME, vehiclePropValue.prop);
- assertEquals(4611, (long) vehiclePropValue.value.int32Values.get(0xC));
+ assertEquals(1155, (long) vehiclePropValue.value.int32Values.get(0xC));
assertEquals(130, (long) vehiclePropValue.value.int32Values.get(0xD));
}
}
diff --git a/tools/bootanalyze/bootanalyze.py b/tools/bootanalyze/bootanalyze.py
index da874ce..480c9d4 100755
--- a/tools/bootanalyze/bootanalyze.py
+++ b/tools/bootanalyze/bootanalyze.py
@@ -522,7 +522,7 @@
print "timeout waiting for event, continue", time_left
break
read_r = read_poll.poll(time_left * 1000.0)
- if read_r:
+ if len(read_r) > 0 and read_r[0][1] == select.POLLIN:
line = process.stdout.readline()
else:
print "poll timeout waiting for event, continue", time_left
diff --git a/tools/emulator/__init__.py b/tools/emulator/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/emulator/__init__.py
diff --git a/tools/emulator/diagjson.example b/tools/emulator/diagjson.example
new file mode 100644
index 0000000..1029dfc
--- /dev/null
+++ b/tools/emulator/diagjson.example
@@ -0,0 +1,12011 @@
+[
+ {
+ "timestamp": 72375175786629,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 18
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 64
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.22222
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72377177593287,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 20
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 64
+ },
+ {
+ "id": 8,
+ "value": 3084
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.22222
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72379176544788,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 22
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 64
+ },
+ {
+ "id": 8,
+ "value": 3078
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72381179373780,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 24
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 64
+ },
+ {
+ "id": 8,
+ "value": 2873
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72383179413967,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 26
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 65
+ },
+ {
+ "id": 8,
+ "value": 3078
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72385179454210,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 28
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 65
+ },
+ {
+ "id": 8,
+ "value": 2869
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72387176856679,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 30
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 66
+ },
+ {
+ "id": 8,
+ "value": 3090
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 52
+ },
+ {
+ "id": 2,
+ "value": 230.46875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72389180204642,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 32
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 66
+ },
+ {
+ "id": 8,
+ "value": 4110
+ },
+ {
+ "id": 9,
+ "value": 1
+ },
+ {
+ "id": 0,
+ "value": 68.44444
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72391178997905,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 34
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 66
+ },
+ {
+ "id": 8,
+ "value": 3633
+ },
+ {
+ "id": 9,
+ "value": 5
+ },
+ {
+ "id": 0,
+ "value": 51.11111
+ },
+ {
+ "id": 2,
+ "value": 223.4375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72393179466175,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 36
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 67
+ },
+ {
+ "id": 8,
+ "value": 3135
+ },
+ {
+ "id": 9,
+ "value": 4
+ },
+ {
+ "id": 0,
+ "value": 50.666668
+ },
+ {
+ "id": 2,
+ "value": 230.46875
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72395177442840,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 38
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 67
+ },
+ {
+ "id": 8,
+ "value": 3125
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 48.444443
+ },
+ {
+ "id": 2,
+ "value": 228.125
+ },
+ {
+ "id": 42,
+ "value": 27.450981
+ }
+ ]
+ },
+ {
+ "timestamp": 72397179771686,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 40
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 68
+ },
+ {
+ "id": 8,
+ "value": 2857
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 55.555557
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72399177882285,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 42
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 68
+ },
+ {
+ "id": 8,
+ "value": 4899
+ },
+ {
+ "id": 9,
+ "value": 6
+ },
+ {
+ "id": 0,
+ "value": 82.666664
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 17.254902
+ }
+ ]
+ },
+ {
+ "timestamp": 72401178271883,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 44
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 68
+ },
+ {
+ "id": 8,
+ "value": 8483
+ },
+ {
+ "id": 9,
+ "value": 17
+ },
+ {
+ "id": 0,
+ "value": 45.333332
+ },
+ {
+ "id": 2,
+ "value": 237.5
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72403179389890,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 46
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 69
+ },
+ {
+ "id": 8,
+ "value": 5158
+ },
+ {
+ "id": 9,
+ "value": 18
+ },
+ {
+ "id": 0,
+ "value": 28
+ },
+ {
+ "id": 2,
+ "value": 188.28125
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72405178339774,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 48
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 69
+ },
+ {
+ "id": 8,
+ "value": 6184
+ },
+ {
+ "id": 9,
+ "value": 20
+ },
+ {
+ "id": 0,
+ "value": 36
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72407178346527,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 50
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 70
+ },
+ {
+ "id": 8,
+ "value": 6207
+ },
+ {
+ "id": 9,
+ "value": 21
+ },
+ {
+ "id": 0,
+ "value": 31.11111
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72409177960280,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 52
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 70
+ },
+ {
+ "id": 8,
+ "value": 5409
+ },
+ {
+ "id": 9,
+ "value": 19
+ },
+ {
+ "id": 0,
+ "value": 28.88889
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 36.47059
+ }
+ ]
+ },
+ {
+ "timestamp": 72411176941375,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 54
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 71
+ },
+ {
+ "id": 8,
+ "value": 5653
+ },
+ {
+ "id": 9,
+ "value": 18
+ },
+ {
+ "id": 0,
+ "value": 92.888885
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 24.705883
+ }
+ ]
+ },
+ {
+ "timestamp": 72413177348486,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 56
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 71
+ },
+ {
+ "id": 8,
+ "value": 7988
+ },
+ {
+ "id": 9,
+ "value": 26
+ },
+ {
+ "id": 0,
+ "value": 31.555555
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72415176509329,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 58
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 72
+ },
+ {
+ "id": 8,
+ "value": 4134
+ },
+ {
+ "id": 9,
+ "value": 21
+ },
+ {
+ "id": 0,
+ "value": 33.333332
+ },
+ {
+ "id": 2,
+ "value": 176.5625
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72417178548720,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 60
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 72
+ },
+ {
+ "id": 8,
+ "value": 3109
+ },
+ {
+ "id": 9,
+ "value": 14
+ },
+ {
+ "id": 0,
+ "value": 49.333332
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72417178548720,
+ "type": "freeze",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 60
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 72
+ },
+ {
+ "id": 8,
+ "value": 3109
+ },
+ {
+ "id": 9,
+ "value": 14
+ },
+ {
+ "id": 0,
+ "value": 49.333332
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ],
+ "stringValue": "P0420"
+ },
+ {
+ "timestamp": 72419178377617,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 62
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 73
+ },
+ {
+ "id": 8,
+ "value": 3635
+ },
+ {
+ "id": 9,
+ "value": 16
+ },
+ {
+ "id": 0,
+ "value": 44.88889
+ },
+ {
+ "id": 2,
+ "value": 223.4375
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72421177803572,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 64
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 73
+ },
+ {
+ "id": 8,
+ "value": 3619
+ },
+ {
+ "id": 9,
+ "value": 11
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 228.125
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72423176710009,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 66
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 74
+ },
+ {
+ "id": 8,
+ "value": 4637
+ },
+ {
+ "id": 9,
+ "value": 10
+ },
+ {
+ "id": 0,
+ "value": 36
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 35.686275
+ }
+ ]
+ },
+ {
+ "timestamp": 72425176560994,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 68
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 74
+ },
+ {
+ "id": 8,
+ "value": 7199
+ },
+ {
+ "id": 9,
+ "value": 15
+ },
+ {
+ "id": 0,
+ "value": 36
+ },
+ {
+ "id": 2,
+ "value": 228.125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72427175927486,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 70
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 75
+ },
+ {
+ "id": 8,
+ "value": 3894
+ },
+ {
+ "id": 9,
+ "value": 11
+ },
+ {
+ "id": 0,
+ "value": 47.555557
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 24.705883
+ }
+ ]
+ },
+ {
+ "timestamp": 72429178132978,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 72
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 75
+ },
+ {
+ "id": 8,
+ "value": 3108
+ },
+ {
+ "id": 9,
+ "value": 1
+ },
+ {
+ "id": 0,
+ "value": 48
+ },
+ {
+ "id": 2,
+ "value": 218.75
+ },
+ {
+ "id": 42,
+ "value": 27.450981
+ }
+ ]
+ },
+ {
+ "timestamp": 72431177381669,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 74
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 75
+ },
+ {
+ "id": 8,
+ "value": 3087
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 27.450981
+ }
+ ]
+ },
+ {
+ "timestamp": 72433178039132,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 76
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 76
+ },
+ {
+ "id": 8,
+ "value": 3091
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72435178023153,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 78
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 76
+ },
+ {
+ "id": 8,
+ "value": 3090
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72437177860993,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 80
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 77
+ },
+ {
+ "id": 8,
+ "value": 3083
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72439177936939,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 82
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 77
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.333332
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72441178444751,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 84
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 77
+ },
+ {
+ "id": 8,
+ "value": 3078
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72443176930748,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 86
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 78
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72445178255526,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 88
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 78
+ },
+ {
+ "id": 8,
+ "value": 3078
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72447175742708,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 90
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 78
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 31.37255
+ }
+ ]
+ },
+ {
+ "timestamp": 72449178336056,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 92
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 79
+ },
+ {
+ "id": 8,
+ "value": 3076
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.333332
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72451176649982,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 94
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 79
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 30.588236
+ }
+ ]
+ },
+ {
+ "timestamp": 72453177388904,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 96
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 79
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72455178049520,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 98
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 79
+ },
+ {
+ "id": 8,
+ "value": 3076
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72457178189969,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 100
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 80
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72459177706926,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 102
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 80
+ },
+ {
+ "id": 8,
+ "value": 2877
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72461176388948,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 104
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 80
+ },
+ {
+ "id": 8,
+ "value": 3076
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72463178194570,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 106
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 81
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72465178204165,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 108
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 81
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72467178005994,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 110
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 81
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72469178443939,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 112
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 82
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72471176771874,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 114
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 82
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72473179942657,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 116
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 82
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72475177449291,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 118
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 82
+ },
+ {
+ "id": 8,
+ "value": 2877
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72477179209812,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 120
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 83
+ },
+ {
+ "id": 8,
+ "value": 3079
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72479178421641,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 122
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 83
+ },
+ {
+ "id": 8,
+ "value": 3073
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72481179539631,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 124
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 83
+ },
+ {
+ "id": 8,
+ "value": 3078
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.666668
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72483178480110,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 126
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 83
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72485180096548,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 128
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 84
+ },
+ {
+ "id": 8,
+ "value": 3073
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72487178424406,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 130
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 84
+ },
+ {
+ "id": 8,
+ "value": 3075
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 47.11111
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72489180054622,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 132
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 84
+ },
+ {
+ "id": 8,
+ "value": 3081
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72491179557027,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 134
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 2878
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.22222
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72493177727317,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 136
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.666668
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72495177794472,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 138
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.666668
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72497177210035,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 140
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 47.11111
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72499180474808,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 142
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 3085
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 46.666668
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72501178578880,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 144
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 47.11111
+ },
+ {
+ "id": 2,
+ "value": 188.28125
+ },
+ {
+ "id": 42,
+ "value": 27.450981
+ }
+ ]
+ },
+ {
+ "timestamp": 72503179731578,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 146
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 7177
+ },
+ {
+ "id": 9,
+ "value": 11
+ },
+ {
+ "id": 0,
+ "value": 49.77778
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 22.745098
+ }
+ ]
+ },
+ {
+ "timestamp": 72505177292755,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 148
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 4126
+ },
+ {
+ "id": 9,
+ "value": 15
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 22.745098
+ }
+ ]
+ },
+ {
+ "timestamp": 72507176773477,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 150
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 6438
+ },
+ {
+ "id": 9,
+ "value": 20
+ },
+ {
+ "id": 0,
+ "value": 77.333336
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 22.745098
+ }
+ ]
+ },
+ {
+ "timestamp": 72509177195082,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 152
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 8746
+ },
+ {
+ "id": 9,
+ "value": 30
+ },
+ {
+ "id": 0,
+ "value": 112.888885
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 8.627451
+ }
+ ]
+ },
+ {
+ "timestamp": 72511175989969,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 154
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 14082
+ },
+ {
+ "id": 9,
+ "value": 44
+ },
+ {
+ "id": 0,
+ "value": 103.111115
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 20.784313
+ }
+ ]
+ },
+ {
+ "timestamp": 72513179658544,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 156
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 15639
+ },
+ {
+ "id": 9,
+ "value": 54
+ },
+ {
+ "id": 0,
+ "value": 73.333336
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72515179414407,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 158
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 10268
+ },
+ {
+ "id": 9,
+ "value": 59
+ },
+ {
+ "id": 0,
+ "value": 63.555557
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72517176297337,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 160
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 5928
+ },
+ {
+ "id": 9,
+ "value": 60
+ },
+ {
+ "id": 0,
+ "value": 24
+ },
+ {
+ "id": 2,
+ "value": 146.09375
+ },
+ {
+ "id": 42,
+ "value": 34.901962
+ }
+ ]
+ },
+ {
+ "timestamp": 72519176826185,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 162
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 6418
+ },
+ {
+ "id": 9,
+ "value": 58
+ },
+ {
+ "id": 0,
+ "value": 59.555557
+ },
+ {
+ "id": 2,
+ "value": 188.28125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72521179790904,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 164
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 6179
+ },
+ {
+ "id": 9,
+ "value": 59
+ },
+ {
+ "id": 0,
+ "value": 34.22222
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72523180627127,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 166
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 85
+ },
+ {
+ "id": 8,
+ "value": 7483
+ },
+ {
+ "id": 9,
+ "value": 59
+ },
+ {
+ "id": 0,
+ "value": 99.55556
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72525177789023,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 168
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 6660
+ },
+ {
+ "id": 9,
+ "value": 63
+ },
+ {
+ "id": 0,
+ "value": 96.888885
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72527175560417,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 170
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 8460
+ },
+ {
+ "id": 9,
+ "value": 66
+ },
+ {
+ "id": 0,
+ "value": 54.666668
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72529179499137,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 172
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 5431
+ },
+ {
+ "id": 9,
+ "value": 56
+ },
+ {
+ "id": 0,
+ "value": 20
+ },
+ {
+ "id": 2,
+ "value": 167.1875
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72531178292369,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 4
+ },
+ {
+ "id": 7,
+ "value": 174
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 4868
+ },
+ {
+ "id": 9,
+ "value": 48
+ },
+ {
+ "id": 0,
+ "value": 15.111111
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72533178253439,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 4
+ },
+ {
+ "id": 7,
+ "value": 176
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 7195
+ },
+ {
+ "id": 9,
+ "value": 47
+ },
+ {
+ "id": 0,
+ "value": 83.55556
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72535179199711,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 178
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 6423
+ },
+ {
+ "id": 9,
+ "value": 48
+ },
+ {
+ "id": 0,
+ "value": 104
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72537177243028,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 180
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 11582
+ },
+ {
+ "id": 9,
+ "value": 55
+ },
+ {
+ "id": 0,
+ "value": 112.888885
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 22.745098
+ }
+ ]
+ },
+ {
+ "timestamp": 72539180216273,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 182
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 13336
+ },
+ {
+ "id": 9,
+ "value": 67
+ },
+ {
+ "id": 0,
+ "value": 113.333336
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72541176144311,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 184
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 12304
+ },
+ {
+ "id": 9,
+ "value": 73
+ },
+ {
+ "id": 0,
+ "value": 24
+ },
+ {
+ "id": 2,
+ "value": 174.21875
+ },
+ {
+ "id": 42,
+ "value": 24.705883
+ }
+ ]
+ },
+ {
+ "timestamp": 72543178195294,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 186
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 6717
+ },
+ {
+ "id": 9,
+ "value": 68
+ },
+ {
+ "id": 0,
+ "value": 20.88889
+ },
+ {
+ "id": 2,
+ "value": 181.25
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72545176834995,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 4
+ },
+ {
+ "id": 7,
+ "value": 188
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 4654
+ },
+ {
+ "id": 9,
+ "value": 47
+ },
+ {
+ "id": 0,
+ "value": 20.444445
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72547177907339,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 190
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 86
+ },
+ {
+ "id": 8,
+ "value": 3330
+ },
+ {
+ "id": 9,
+ "value": 30
+ },
+ {
+ "id": 0,
+ "value": 29.333334
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 34.901962
+ }
+ ]
+ },
+ {
+ "timestamp": 72549179735735,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 192
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 3097
+ },
+ {
+ "id": 9,
+ "value": 13
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 34.901962
+ }
+ ]
+ },
+ {
+ "timestamp": 72551181754526,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 194
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 3088
+ },
+ {
+ "id": 9,
+ "value": 2
+ },
+ {
+ "id": 0,
+ "value": 45.77778
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72553177986304,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 196
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 3075
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.666668
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72555176967736,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 198
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72557177160577,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 200
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72559179745844,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 202
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 2873
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.22222
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 33.333332
+ }
+ ]
+ },
+ {
+ "timestamp": 72561176203928,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 204
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 2878
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.666668
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72563177799351,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 206
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.666668
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72565177953894,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 208
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 2870
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.666668
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72567176653777,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 210
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 3075
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.666668
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72569178043717,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 212
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 2872
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.11111
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72571178251056,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 214
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.11111
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72573176628436,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 216
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.11111
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72575177840420,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 218
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.11111
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72577176563646,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 220
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 3079
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72579179164504,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 222
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 2876
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.11111
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72581177838286,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 224
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72583178358480,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 226
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 3078
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72585177901670,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 228
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.11111
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72587177734144,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 230
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3076
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72589179333460,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 232
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72591178129065,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 234
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2878
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72593177596819,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 236
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72595178384378,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 238
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3076
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44.444443
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72597178056865,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 240
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3075
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72599179833740,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 243
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2872
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72601176651309,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 244
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44.444443
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72603177566065,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 246
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3083
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72605178128004,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 248
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72607179599532,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 250
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72609179437284,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 252
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3081
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72611179630474,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 254
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72613179774544,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 256
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72615178404973,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 258
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3073
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44.444443
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72617179059428,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 260
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44.444443
+ },
+ {
+ "id": 2,
+ "value": 188.28125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72619180105086,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 262
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3076
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 44.444443
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72621179098902,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 264
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3104
+ },
+ {
+ "id": 9,
+ "value": 1
+ },
+ {
+ "id": 0,
+ "value": 59.555557
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72623179359108,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 266
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 8255
+ },
+ {
+ "id": 9,
+ "value": 15
+ },
+ {
+ "id": 0,
+ "value": 100.888885
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 9.411765
+ }
+ ]
+ },
+ {
+ "timestamp": 72625179856484,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 268
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 11276
+ },
+ {
+ "id": 9,
+ "value": 33
+ },
+ {
+ "id": 0,
+ "value": 99.55556
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 15.686275
+ }
+ ]
+ },
+ {
+ "timestamp": 72627176310119,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 270
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 15618
+ },
+ {
+ "id": 9,
+ "value": 50
+ },
+ {
+ "id": 0,
+ "value": 90.666664
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72629177201984,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 272
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 13109
+ },
+ {
+ "id": 9,
+ "value": 63
+ },
+ {
+ "id": 0,
+ "value": 111.55556
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72631177723851,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 274
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 14343
+ },
+ {
+ "id": 9,
+ "value": 74
+ },
+ {
+ "id": 0,
+ "value": 111.55556
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 17.647058
+ }
+ ]
+ },
+ {
+ "timestamp": 72633179257550,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 276
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 15921
+ },
+ {
+ "id": 9,
+ "value": 83
+ },
+ {
+ "id": 0,
+ "value": 21.777779
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 9.803922
+ }
+ ]
+ },
+ {
+ "timestamp": 72635179273140,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 278
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 4400
+ },
+ {
+ "id": 9,
+ "value": 55
+ },
+ {
+ "id": 0,
+ "value": 34.666668
+ },
+ {
+ "id": 2,
+ "value": 183.59375
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72637179409130,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 280
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 3117
+ },
+ {
+ "id": 9,
+ "value": 32
+ },
+ {
+ "id": 0,
+ "value": 30.666666
+ },
+ {
+ "id": 2,
+ "value": 183.59375
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72639179800268,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 282
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 3108
+ },
+ {
+ "id": 9,
+ "value": 26
+ },
+ {
+ "id": 0,
+ "value": 29.333334
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72641179405070,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 284
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 3106
+ },
+ {
+ "id": 9,
+ "value": 21
+ },
+ {
+ "id": 0,
+ "value": 32.88889
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72643179866403,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 286
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 4115
+ },
+ {
+ "id": 9,
+ "value": 17
+ },
+ {
+ "id": 0,
+ "value": 111.111115
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 17.254902
+ }
+ ]
+ },
+ {
+ "timestamp": 72645178430456,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 288
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 8991
+ },
+ {
+ "id": 9,
+ "value": 30
+ },
+ {
+ "id": 0,
+ "value": 112.44444
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72647178451017,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 290
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 9513
+ },
+ {
+ "id": 9,
+ "value": 43
+ },
+ {
+ "id": 0,
+ "value": 95.55556
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72649179500927,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 292
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 9518
+ },
+ {
+ "id": 9,
+ "value": 50
+ },
+ {
+ "id": 0,
+ "value": 27.555555
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 34.509804
+ }
+ ]
+ },
+ {
+ "timestamp": 72651179611409,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 294
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 6418
+ },
+ {
+ "id": 9,
+ "value": 49
+ },
+ {
+ "id": 0,
+ "value": 19.555555
+ },
+ {
+ "id": 2,
+ "value": 178.90625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72653178456810,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 296
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 5173
+ },
+ {
+ "id": 9,
+ "value": 40
+ },
+ {
+ "id": 0,
+ "value": 20.88889
+ },
+ {
+ "id": 2,
+ "value": 188.28125
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72655180014824,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 298
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 4112
+ },
+ {
+ "id": 9,
+ "value": 32
+ },
+ {
+ "id": 0,
+ "value": 28.444445
+ },
+ {
+ "id": 2,
+ "value": 185.9375
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72657180944053,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 300
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 8720
+ },
+ {
+ "id": 9,
+ "value": 37
+ },
+ {
+ "id": 0,
+ "value": 113.333336
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72659179332880,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 302
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 16412
+ },
+ {
+ "id": 9,
+ "value": 52
+ },
+ {
+ "id": 0,
+ "value": 109.333336
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 12.54902
+ }
+ ]
+ },
+ {
+ "timestamp": 72661177765487,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 304
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 13828
+ },
+ {
+ "id": 9,
+ "value": 61
+ },
+ {
+ "id": 0,
+ "value": 22.666666
+ },
+ {
+ "id": 2,
+ "value": 178.90625
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72663177689968,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 306
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 6447
+ },
+ {
+ "id": 9,
+ "value": 61
+ },
+ {
+ "id": 0,
+ "value": 28.88889
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72665177450571,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 308
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 5657
+ },
+ {
+ "id": 9,
+ "value": 56
+ },
+ {
+ "id": 0,
+ "value": 16.88889
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72667179692696,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 4
+ },
+ {
+ "id": 7,
+ "value": 310
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 4873
+ },
+ {
+ "id": 9,
+ "value": 48
+ },
+ {
+ "id": 0,
+ "value": 19.11111
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 35.686275
+ }
+ ]
+ },
+ {
+ "timestamp": 72669179293955,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 4
+ },
+ {
+ "id": 7,
+ "value": 312
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 87
+ },
+ {
+ "id": 8,
+ "value": 3613
+ },
+ {
+ "id": 9,
+ "value": 34
+ },
+ {
+ "id": 0,
+ "value": 27.11111
+ },
+ {
+ "id": 2,
+ "value": 185.9375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72671177593842,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 314
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 3131
+ },
+ {
+ "id": 9,
+ "value": 19
+ },
+ {
+ "id": 0,
+ "value": 30.222221
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 36.862747
+ }
+ ]
+ },
+ {
+ "timestamp": 72673177057067,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 316
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 3844
+ },
+ {
+ "id": 9,
+ "value": 8
+ },
+ {
+ "id": 0,
+ "value": 36.444443
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 34.901962
+ }
+ ]
+ },
+ {
+ "timestamp": 72675179630131,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 318
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 43.555557
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72677179714055,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 320
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 2866
+ },
+ {
+ "id": 9,
+ "value": 1
+ },
+ {
+ "id": 0,
+ "value": 42.666668
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72679179288433,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 322
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 7457
+ },
+ {
+ "id": 9,
+ "value": 13
+ },
+ {
+ "id": 0,
+ "value": 76
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 10.588236
+ }
+ ]
+ },
+ {
+ "timestamp": 72681176480810,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 324
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 9272
+ },
+ {
+ "id": 9,
+ "value": 27
+ },
+ {
+ "id": 0,
+ "value": 105.77778
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 15.686275
+ }
+ ]
+ },
+ {
+ "timestamp": 72683179662780,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 326
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 13585
+ },
+ {
+ "id": 9,
+ "value": 43
+ },
+ {
+ "id": 0,
+ "value": 87.55556
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72685177751997,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 328
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 11024
+ },
+ {
+ "id": 9,
+ "value": 53
+ },
+ {
+ "id": 0,
+ "value": 42.666668
+ },
+ {
+ "id": 2,
+ "value": 190.625
+ },
+ {
+ "id": 42,
+ "value": 20.784313
+ }
+ ]
+ },
+ {
+ "timestamp": 72687177061206,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 330
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 6927
+ },
+ {
+ "id": 9,
+ "value": 51
+ },
+ {
+ "id": 0,
+ "value": 18.222221
+ },
+ {
+ "id": 2,
+ "value": 155.46875
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72689176965248,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 332
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 5148
+ },
+ {
+ "id": 9,
+ "value": 38
+ },
+ {
+ "id": 0,
+ "value": 21.333334
+ },
+ {
+ "id": 2,
+ "value": 157.8125
+ },
+ {
+ "id": 42,
+ "value": 34.11765
+ }
+ ]
+ },
+ {
+ "timestamp": 72691180692310,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 334
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 3594
+ },
+ {
+ "id": 9,
+ "value": 29
+ },
+ {
+ "id": 0,
+ "value": 26.222221
+ },
+ {
+ "id": 2,
+ "value": 139.0625
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72693178299558,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 336
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 3350
+ },
+ {
+ "id": 9,
+ "value": 25
+ },
+ {
+ "id": 0,
+ "value": 48.88889
+ },
+ {
+ "id": 2,
+ "value": 185.9375
+ },
+ {
+ "id": 42,
+ "value": 18.82353
+ }
+ ]
+ },
+ {
+ "timestamp": 72695178266771,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 338
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 88
+ },
+ {
+ "id": 8,
+ "value": 8456
+ },
+ {
+ "id": 9,
+ "value": 34
+ },
+ {
+ "id": 0,
+ "value": 79.55556
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72697178378315,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 340
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 6163
+ },
+ {
+ "id": 9,
+ "value": 40
+ },
+ {
+ "id": 0,
+ "value": 30.666666
+ },
+ {
+ "id": 2,
+ "value": 185.9375
+ },
+ {
+ "id": 42,
+ "value": 36.47059
+ }
+ ]
+ },
+ {
+ "timestamp": 72699179440945,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 342
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 4886
+ },
+ {
+ "id": 9,
+ "value": 32
+ },
+ {
+ "id": 0,
+ "value": 24
+ },
+ {
+ "id": 2,
+ "value": 188.28125
+ },
+ {
+ "id": 42,
+ "value": 30.588236
+ }
+ ]
+ },
+ {
+ "timestamp": 72701179783621,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 344
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 5130
+ },
+ {
+ "id": 9,
+ "value": 29
+ },
+ {
+ "id": 0,
+ "value": 54.22222
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72703178384562,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 346
+ },
+ {
+ "id": 13,
+ "value": 21
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 9525
+ },
+ {
+ "id": 9,
+ "value": 38
+ },
+ {
+ "id": 0,
+ "value": 112.888885
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72705179707825,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 348
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 10513
+ },
+ {
+ "id": 9,
+ "value": 49
+ },
+ {
+ "id": 0,
+ "value": 99.55556
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72707177436366,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 350
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 8248
+ },
+ {
+ "id": 9,
+ "value": 54
+ },
+ {
+ "id": 0,
+ "value": 76.888885
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72709176949975,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 352
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 8484
+ },
+ {
+ "id": 9,
+ "value": 59
+ },
+ {
+ "id": 0,
+ "value": 59.11111
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72711179509943,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 354
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 5945
+ },
+ {
+ "id": 9,
+ "value": 61
+ },
+ {
+ "id": 0,
+ "value": 74.22222
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72713179908608,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 356
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 6197
+ },
+ {
+ "id": 9,
+ "value": 61
+ },
+ {
+ "id": 0,
+ "value": 23.11111
+ },
+ {
+ "id": 2,
+ "value": 178.90625
+ },
+ {
+ "id": 42,
+ "value": 34.901962
+ }
+ ]
+ },
+ {
+ "timestamp": 72715178040463,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 4
+ },
+ {
+ "id": 7,
+ "value": 358
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 5904
+ },
+ {
+ "id": 9,
+ "value": 59
+ },
+ {
+ "id": 0,
+ "value": 20.88889
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 34.901962
+ }
+ ]
+ },
+ {
+ "timestamp": 72717179826935,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 360
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 6154
+ },
+ {
+ "id": 9,
+ "value": 60
+ },
+ {
+ "id": 0,
+ "value": 35.11111
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72719178168768,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 362
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 5636
+ },
+ {
+ "id": 9,
+ "value": 59
+ },
+ {
+ "id": 0,
+ "value": 16.88889
+ },
+ {
+ "id": 2,
+ "value": 181.25
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72721179101986,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 4
+ },
+ {
+ "id": 7,
+ "value": 364
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 5411
+ },
+ {
+ "id": 9,
+ "value": 54
+ },
+ {
+ "id": 0,
+ "value": 54.666668
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 24.705883
+ }
+ ]
+ },
+ {
+ "timestamp": 72723180021513,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 366
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 5688
+ },
+ {
+ "id": 9,
+ "value": 55
+ },
+ {
+ "id": 0,
+ "value": 24.444445
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72725179738209,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 368
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 5903
+ },
+ {
+ "id": 9,
+ "value": 55
+ },
+ {
+ "id": 0,
+ "value": 48.88889
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72727179292538,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 370
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 5162
+ },
+ {
+ "id": 9,
+ "value": 51
+ },
+ {
+ "id": 0,
+ "value": 20.88889
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72729178027625,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 372
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 4124
+ },
+ {
+ "id": 9,
+ "value": 44
+ },
+ {
+ "id": 0,
+ "value": 24
+ },
+ {
+ "id": 2,
+ "value": 188.28125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72731178148147,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 374
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 3132
+ },
+ {
+ "id": 9,
+ "value": 37
+ },
+ {
+ "id": 0,
+ "value": 23.555555
+ },
+ {
+ "id": 2,
+ "value": 181.25
+ },
+ {
+ "id": 42,
+ "value": 34.901962
+ }
+ ]
+ },
+ {
+ "timestamp": 72733176368495,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 376
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 3128
+ },
+ {
+ "id": 9,
+ "value": 24
+ },
+ {
+ "id": 0,
+ "value": 28
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 34.901962
+ }
+ ]
+ },
+ {
+ "timestamp": 72735179502626,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 378
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 3089
+ },
+ {
+ "id": 9,
+ "value": 15
+ },
+ {
+ "id": 0,
+ "value": 38.666668
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 39.607845
+ }
+ ]
+ },
+ {
+ "timestamp": 72737179297296,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 380
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 2869
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.666668
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72739177718891,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 382
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 2877
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.555557
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72741177981830,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 384
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2871
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.11111
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72743176174851,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 387
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2872
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.11111
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72745329912826,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 389
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2870
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.11111
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72747355080893,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 391
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3073
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.555557
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72749176618991,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 392
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.555557
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72751176696798,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 394
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2867
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72753176964078,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 396
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72755178349568,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 398
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3078
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72757178508591,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 400
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 2875
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 30.588236
+ }
+ ]
+ },
+ {
+ "timestamp": 72759176500609,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 402
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72761176683571,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 404
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 2878
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72763178363414,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 406
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3081
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72765177437003,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 408
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3085
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72767178204902,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 410
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 2877
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72769177266732,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 412
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3079
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72771176734323,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 414
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72773176997295,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 416
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3079
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72775176636900,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 418
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72777176589987,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 420
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3076
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72779178389314,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 422
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3075
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72781178485901,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 424
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72783177805524,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 426
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72785177696746,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 428
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3081
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72787177943035,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 430
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3085
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72789177892167,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 432
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3900
+ },
+ {
+ "id": 9,
+ "value": 2
+ },
+ {
+ "id": 0,
+ "value": 84
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 11.372549
+ }
+ ]
+ },
+ {
+ "timestamp": 72791176960551,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 434
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 6937
+ },
+ {
+ "id": 9,
+ "value": 15
+ },
+ {
+ "id": 0,
+ "value": 73.333336
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 17.254902
+ }
+ ]
+ },
+ {
+ "timestamp": 72793179080142,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 436
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 10253
+ },
+ {
+ "id": 9,
+ "value": 30
+ },
+ {
+ "id": 0,
+ "value": 100.888885
+ },
+ {
+ "id": 2,
+ "value": 211.71875
+ },
+ {
+ "id": 42,
+ "value": 10.588236
+ }
+ ]
+ },
+ {
+ "timestamp": 72795177144042,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 438
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 14653
+ },
+ {
+ "id": 9,
+ "value": 46
+ },
+ {
+ "id": 0,
+ "value": 104
+ },
+ {
+ "id": 2,
+ "value": 214.0625
+ },
+ {
+ "id": 42,
+ "value": 22.745098
+ }
+ ]
+ },
+ {
+ "timestamp": 72797177311878,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 440
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 17934
+ },
+ {
+ "id": 9,
+ "value": 59
+ },
+ {
+ "id": 0,
+ "value": 55.555557
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 24.705883
+ }
+ ]
+ },
+ {
+ "timestamp": 72799176281873,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 442
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 8757
+ },
+ {
+ "id": 9,
+ "value": 62
+ },
+ {
+ "id": 0,
+ "value": 28.444445
+ },
+ {
+ "id": 2,
+ "value": 190.625
+ },
+ {
+ "id": 42,
+ "value": 22.745098
+ }
+ ]
+ },
+ {
+ "timestamp": 72801179010275,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 444
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 5642
+ },
+ {
+ "id": 9,
+ "value": 58
+ },
+ {
+ "id": 0,
+ "value": 17.777779
+ },
+ {
+ "id": 2,
+ "value": 188.28125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72803176720724,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 446
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 3630
+ },
+ {
+ "id": 9,
+ "value": 38
+ },
+ {
+ "id": 0,
+ "value": 25.333334
+ },
+ {
+ "id": 2,
+ "value": 171.875
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72805176371184,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 448
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 89
+ },
+ {
+ "id": 8,
+ "value": 3130
+ },
+ {
+ "id": 9,
+ "value": 27
+ },
+ {
+ "id": 0,
+ "value": 25.777779
+ },
+ {
+ "id": 2,
+ "value": 178.90625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72807176264873,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 450
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 3091
+ },
+ {
+ "id": 9,
+ "value": 13
+ },
+ {
+ "id": 0,
+ "value": 40
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72809177130217,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 452
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 90
+ },
+ {
+ "id": 8,
+ "value": 3340
+ },
+ {
+ "id": 9,
+ "value": 4
+ },
+ {
+ "id": 0,
+ "value": 43.11111
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72811178240749,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 454
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 2874
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 30.19608
+ }
+ ]
+ },
+ {
+ "timestamp": 72813176674429,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 456
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 2877
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.555557
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 27.450981
+ }
+ ]
+ },
+ {
+ "timestamp": 72815177036139,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 458
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.11111
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72817178504749,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 460
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 2866
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.555557
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72819175954203,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 462
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.11111
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72821177378507,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 464
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 2873
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 39.555557
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72823177069331,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 466
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3076
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72825179501699,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 468
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3073
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72827176421710,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 470
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72829176386701,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 472
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72831176764503,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 474
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 2873
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72833177319130,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 476
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72835180519900,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 478
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72837178223527,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 480
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72839177312748,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 482
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72841177797843,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 484
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3079
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72843179579857,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 486
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3074
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.444443
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72845180133072,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 488
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72847176774274,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 490
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3073
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72849177384942,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 492
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72851178359655,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 494
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3080
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72853178455898,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 496
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 40.88889
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72855177993333,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 498
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3084
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72857177079489,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 500
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3072
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72859176801251,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 502
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2878
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72861178465404,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 504
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3082
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72863179481995,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 506
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3073
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72865177785963,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 508
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3079
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72867176113089,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 510
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3075
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72869176834314,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 512
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2874
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 190.625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72871178569429,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 514
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3079
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.333332
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72873176252437,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 516
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.22222
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72875177079512,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 518
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2872
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72877177740208,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 520
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72879180125173,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 522
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2872
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 190.625
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72881178525713,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 524
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3079
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 195.3125
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72883176875986,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 526
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2875
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72885176324726,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 528
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 2879
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72887176793512,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 530
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3078
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 42.22222
+ },
+ {
+ "id": 2,
+ "value": 197.65625
+ },
+ {
+ "id": 42,
+ "value": 31.764706
+ }
+ ]
+ },
+ {
+ "timestamp": 72889177026044,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 532
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 3077
+ },
+ {
+ "id": 9,
+ "value": 0
+ },
+ {
+ "id": 0,
+ "value": 41.77778
+ },
+ {
+ "id": 2,
+ "value": 192.96875
+ },
+ {
+ "id": 42,
+ "value": 32.156864
+ }
+ ]
+ },
+ {
+ "timestamp": 72891177972355,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 534
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 95
+ },
+ {
+ "id": 8,
+ "value": 5426
+ },
+ {
+ "id": 9,
+ "value": 5
+ },
+ {
+ "id": 0,
+ "value": 51.555557
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 22.745098
+ }
+ ]
+ },
+ {
+ "timestamp": 72893178557124,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 536
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 96
+ },
+ {
+ "id": 8,
+ "value": 9000
+ },
+ {
+ "id": 9,
+ "value": 18
+ },
+ {
+ "id": 0,
+ "value": 66.22222
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 27.450981
+ }
+ ]
+ },
+ {
+ "timestamp": 72895179323906,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 538
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 95
+ },
+ {
+ "id": 8,
+ "value": 7996
+ },
+ {
+ "id": 9,
+ "value": 24
+ },
+ {
+ "id": 0,
+ "value": 53.333332
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 18.82353
+ }
+ ]
+ },
+ {
+ "timestamp": 72897179514717,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 540
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 95
+ },
+ {
+ "id": 8,
+ "value": 6195
+ },
+ {
+ "id": 9,
+ "value": 29
+ },
+ {
+ "id": 0,
+ "value": 109.333336
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 10.588236
+ }
+ ]
+ },
+ {
+ "timestamp": 72899178457633,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 542
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 12839
+ },
+ {
+ "id": 9,
+ "value": 40
+ },
+ {
+ "id": 0,
+ "value": 61.77778
+ },
+ {
+ "id": 2,
+ "value": 204.6875
+ },
+ {
+ "id": 42,
+ "value": 23.529411
+ }
+ ]
+ },
+ {
+ "timestamp": 72901179477573,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 544
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 94
+ },
+ {
+ "id": 8,
+ "value": 10757
+ },
+ {
+ "id": 9,
+ "value": 49
+ },
+ {
+ "id": 0,
+ "value": 111.55556
+ },
+ {
+ "id": 2,
+ "value": 207.03125
+ },
+ {
+ "id": 42,
+ "value": 27.450981
+ }
+ ]
+ },
+ {
+ "timestamp": 72903179086670,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 546
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 93
+ },
+ {
+ "id": 8,
+ "value": 12836
+ },
+ {
+ "id": 9,
+ "value": 61
+ },
+ {
+ "id": 0,
+ "value": 85.77778
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 28.62745
+ }
+ ]
+ },
+ {
+ "timestamp": 72905177385416,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 548
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 10042
+ },
+ {
+ "id": 9,
+ "value": 65
+ },
+ {
+ "id": 0,
+ "value": 34.22222
+ },
+ {
+ "id": 2,
+ "value": 200
+ },
+ {
+ "id": 42,
+ "value": 32.941177
+ }
+ ]
+ },
+ {
+ "timestamp": 72907178215527,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 550
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 92
+ },
+ {
+ "id": 8,
+ "value": 9733
+ },
+ {
+ "id": 9,
+ "value": 66
+ },
+ {
+ "id": 0,
+ "value": 111.55556
+ },
+ {
+ "id": 2,
+ "value": 216.40625
+ },
+ {
+ "id": 42,
+ "value": 26.27451
+ }
+ ]
+ },
+ {
+ "timestamp": 72909179246647,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 552
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 10302
+ },
+ {
+ "id": 9,
+ "value": 70
+ },
+ {
+ "id": 0,
+ "value": 56.88889
+ },
+ {
+ "id": 2,
+ "value": 202.34375
+ },
+ {
+ "id": 42,
+ "value": 27.450981
+ }
+ ]
+ },
+ {
+ "timestamp": 72911180048703,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 554
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 15104
+ },
+ {
+ "id": 9,
+ "value": 73
+ },
+ {
+ "id": 0,
+ "value": 111.111115
+ },
+ {
+ "id": 2,
+ "value": 209.375
+ },
+ {
+ "id": 42,
+ "value": 19.215687
+ }
+ ]
+ },
+ {
+ "timestamp": 72913178558121,
+ "type": "live",
+ "intValues": [
+ {
+ "id": 0,
+ "value": 2
+ },
+ {
+ "id": 7,
+ "value": 556
+ },
+ {
+ "id": 13,
+ "value": 22
+ }
+ ],
+ "floatValues": [
+ {
+ "id": 1,
+ "value": 91
+ },
+ {
+ "id": 8,
+ "value": 16423
+ },
+ {
+ "id": 9,
+ "value": 86
+ },
+ {
+ "id": 0,
+ "value": 75.111115
+ }
+ ]
+ },
+ {
+ "timestamp": 72915176591525,
+ "type": "live"
+ },
+ {
+ "timestamp": 72917180004744,
+ "type": "live"
+ },
+ {
+ "timestamp": 72919178449026,
+ "type": "live"
+ },
+ {
+ "timestamp": 72921179933335,
+ "type": "live"
+ },
+ {
+ "timestamp": 72923179387974,
+ "type": "live"
+ },
+ {
+ "timestamp": 72925179338880,
+ "type": "live"
+ },
+ {
+ "timestamp": 72927176345108,
+ "type": "live"
+ },
+ {
+ "timestamp": 72929179848799,
+ "type": "live"
+ },
+ {
+ "timestamp": 72931179314858,
+ "type": "live"
+ },
+ {
+ "timestamp": 72933180632674,
+ "type": "live"
+ },
+ {
+ "timestamp": 72935179793063,
+ "type": "live"
+ },
+ {
+ "timestamp": 72937178822539,
+ "type": "live"
+ },
+ {
+ "timestamp": 72939177286298,
+ "type": "live"
+ },
+ {
+ "timestamp": 72941177772999,
+ "type": "live"
+ },
+ {
+ "timestamp": 72943177666251,
+ "type": "live"
+ },
+ {
+ "timestamp": 72945180047718,
+ "type": "live"
+ },
+ {
+ "timestamp": 72947179391597,
+ "type": "live"
+ },
+ {
+ "timestamp": 72949176920841,
+ "type": "live"
+ },
+ {
+ "timestamp": 72951177850208,
+ "type": "live"
+ },
+ {
+ "timestamp": 72953176750100,
+ "type": "live"
+ },
+ {
+ "timestamp": 72955175939724,
+ "type": "live"
+ },
+ {
+ "timestamp": 72957177389603,
+ "type": "live"
+ },
+ {
+ "timestamp": 72959178499462,
+ "type": "live"
+ }
+]
diff --git a/tools/emulator/diagnostic_builder.py b/tools/emulator/diagnostic_builder.py
new file mode 100644
index 0000000..b205a5b
--- /dev/null
+++ b/tools/emulator/diagnostic_builder.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3.4
+#
+# Copyright (C) 2017 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.
+#
+
+# A helper class to generate COMPLEX property values that can be
+# set as the value for a diagnostic frame
+# Spritually, the same as DiagnosticEventBuilder.java
+
+from diagnostic_sensors import OBD2_SENSOR_INTEGER_LAST_SYSTEM_INDEX
+from diagnostic_sensors import OBD2_SENSOR_FLOAT_LAST_SYSTEM_INDEX
+
+class DiagnosticEventBuilder(object):
+ class ByteArray(object):
+ def __init__(self, numElements):
+ self.count = numElements
+ if 0 == (numElements % 8):
+ self.data = bytearray(numElements/8)
+ else:
+ # if not a multiple of 8, add one extra byte
+ self.data = bytearray(1+numElements/8)
+
+ def _getIndices(self, bit):
+ if (bit < 0) or (bit >= self.count):
+ raise IndexError("index %d not in range [0,%d)" % (bit, self.count))
+ byteIdx = bit / 8
+ bitIdx = (bit % 8)
+ return byteIdx, bitIdx
+
+ def setBit(self, bit):
+ byteIdx, bitIdx = self._getIndices(bit)
+ bitValue = pow(2,bitIdx)
+ self.data[byteIdx] = self.data[byteIdx] | bitValue
+
+ def getBit(self, bit):
+ byteIdx, bitIdx = self._getIndices(bit)
+ bitValue = pow(2,bitIdx)
+ return 0 != self.data[byteIdx] & bitValue
+
+ def __str__(self):
+ return str(self.data)
+
+ def __init__(self, propConfig):
+ self.string_value = ""
+ self.bytes = ""
+ self.numIntSensors = propConfig.config[0].config_array[0] + \
+ OBD2_SENSOR_INTEGER_LAST_SYSTEM_INDEX + 1
+ self.numFloatSensors = propConfig.config[0].config_array[1] + \
+ OBD2_SENSOR_FLOAT_LAST_SYSTEM_INDEX + 1
+ self.bitmask = DiagnosticEventBuilder.ByteArray(
+ self.numIntSensors+self.numFloatSensors)
+ self.int32_values = [0] * self.numIntSensors
+ self.float_values = [0.0] * self.numFloatSensors
+
+ def addIntSensor(self, idx, value):
+ self.int32_values[idx] = value
+ self.bitmask.setBit(idx)
+ return self
+
+ def addFloatSensor(self, idx, value):
+ self.float_values[idx] = value
+ self.bitmask.setBit(len(self.int32_values)+idx)
+ return self
+
+ def setStringValue(self, string):
+ self.string_value = string
+ return self
+
+ def build(self):
+ self.bytes_value = str(self.bitmask)
+ return self
+
+ def __str__(self):
+ s = "diagnostic event {\n"
+ for x in ['string_value', 'int32_values', 'float_values']:
+ s = s + "\t%s: %s\n" % (x, self.__dict__[x])
+ return s + "}"
diff --git a/tools/emulator/diagnostic_injector.py b/tools/emulator/diagnostic_injector.py
new file mode 100755
index 0000000..4bd3317
--- /dev/null
+++ b/tools/emulator/diagnostic_injector.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+#
+
+# A tool that can read diagnostic events from a Diagnostic JSON document
+# and forward them to Vehicle HAL via vhal_emulator
+# Use thusly:
+# $ ./diagnostic_injector.py <path/to/diagnostic.json>
+
+import sys
+import json
+import time
+
+import vhal_consts_2_1 as c
+
+# vhal_emulator depends on a custom Python package that requires installation
+# give user guidance should the import fail
+try:
+ from vhal_emulator import Vhal
+except ImportError as e:
+ isProtobuf = False
+ pipTool = "pip%s" % ("3" if sys.version_info > (3,0) else "")
+ if hasattr(e, 'name'):
+ if e.name == 'google': isProtobuf = True
+ elif hasattr(e, 'message'):
+ if e.message.endswith('symbol_database'):
+ isProtobuf = True
+ if isProtobuf:
+ print('could not find protobuf.')
+ print('protobuf can be installed via "sudo %s install --upgrade protobuf"' % pipTool)
+ sys.exit(1)
+ else:
+ raise e
+
+from diagnostic_builder import DiagnosticEventBuilder
+
+class DiagnosticHalWrapper(object):
+ def __init__(self):
+ self.vhal = Vhal(c.vhal_types_2_0)
+ self.liveFrameConfig = self.chat(
+ lambda hal: hal.getConfig(c.VEHICLEPROPERTY_OBD2_LIVE_FRAME))
+ self.freezeFrameConfig = self.chat(
+ lambda hal: hal.getConfig(c.VEHICLEPROPERTY_OBD2_FREEZE_FRAME))
+ self.eventTypeData = {
+ 'live' : {
+ 'builder' : lambda: DiagnosticEventBuilder(self.liveFrameConfig),
+ 'property' : c.VEHICLEPROPERTY_OBD2_LIVE_FRAME
+ },
+ 'freeze' : {
+ 'builder' : lambda: DiagnosticEventBuilder(self.freezeFrameConfig),
+ 'property' : c.VEHICLEPROPERTY_OBD2_FREEZE_FRAME
+ },
+ }
+
+ def chat(self, request):
+ request(self.vhal)
+ return self.vhal.rxMsg()
+
+ def inject(self, file):
+ data = json.load(open(file))
+ lastTimestamp = 0
+ for event in data:
+ currentTimestamp = event['timestamp']
+ # time travel isn't supported (yet)
+ assert currentTimestamp >= lastTimestamp
+ # wait the delta between this event and the previous one
+ # before sending it out; but on the first event, send now
+ # or we'd wait for a long long long time
+ if lastTimestamp != 0:
+ # also, timestamps are in nanoseconds, but sleep() uses seconds
+ time.sleep((currentTimestamp-lastTimestamp)/1000000000)
+ lastTimestamp = currentTimestamp
+ # now build the event
+ eventType = event['type'].encode('utf-8')
+ eventTypeData = self.eventTypeData[eventType]
+ builder = eventTypeData['builder']()
+ builder.setStringValue(event.get('stringValue', ''))
+ for intValue in event['intValues']:
+ builder.addIntSensor(intValue['id'], intValue['value'])
+ for floatValue in event['floatValues']:
+ builder.addFloatSensor(floatValue['id'], floatValue['value'])
+ builtEvent = builder.build()
+ print ("Sending %s %s..." % (eventType, builtEvent)),
+ # and send it
+ status = self.chat(
+ lambda hal:
+ hal.setProperty(eventTypeData['property'],
+ 0,
+ builtEvent)).status
+ if status == 0:
+ print("ok!")
+ else:
+ print("fail: %s" % status)
+
+if len(sys.argv) < 2:
+ print("Syntax: diagnostic_injector.py <path/to/diagnostic.json>")
+ sys.exit(1)
+
+halWrapper = DiagnosticHalWrapper()
+
+for arg in sys.argv[1:]:
+ halWrapper.inject(arg)
diff --git a/tools/emulator/diagnostic_sensors.py b/tools/emulator/diagnostic_sensors.py
new file mode 100644
index 0000000..abde4b8
--- /dev/null
+++ b/tools/emulator/diagnostic_sensors.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2017 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.
+#
+# This file is generated by types.hal by packages/services/Car/tools/update-obd2-sensors.py
+# DO NOT EDIT MANUALLY
+
+OBD2_SENSOR_INTEGER_FUEL_SYSTEM_STATUS = 0
+OBD2_SENSOR_INTEGER_MALFUNCTION_INDICATOR_LIGHT_ON = 1
+OBD2_SENSOR_INTEGER_IGNITION_MONITORS_SUPPORTED = 2
+OBD2_SENSOR_INTEGER_IGNITION_SPECIFIC_MONITORS = 3
+OBD2_SENSOR_INTEGER_INTAKE_AIR_TEMPERATURE = 4
+OBD2_SENSOR_INTEGER_COMMANDED_SECONDARY_AIR_STATUS = 5
+OBD2_SENSOR_INTEGER_NUM_OXYGEN_SENSORS_PRESENT = 6
+OBD2_SENSOR_INTEGER_RUNTIME_SINCE_ENGINE_START = 7
+OBD2_SENSOR_INTEGER_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8
+OBD2_SENSOR_INTEGER_WARMUPS_SINCE_CODES_CLEARED = 9
+OBD2_SENSOR_INTEGER_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10
+OBD2_SENSOR_INTEGER_ABSOLUTE_BAROMETRIC_PRESSURE = 11
+OBD2_SENSOR_INTEGER_CONTROL_MODULE_VOLTAGE = 12
+OBD2_SENSOR_INTEGER_AMBIENT_AIR_TEMPERATURE = 13
+OBD2_SENSOR_INTEGER_TIME_WITH_MALFUNCTION_LIGHT_ON = 14
+OBD2_SENSOR_INTEGER_TIME_SINCE_TROUBLE_CODES_CLEARED = 15
+OBD2_SENSOR_INTEGER_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16
+OBD2_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_VOLTAGE = 17
+OBD2_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_CURRENT = 18
+OBD2_SENSOR_INTEGER_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19
+OBD2_SENSOR_INTEGER_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20
+OBD2_SENSOR_INTEGER_FUEL_TYPE = 21
+OBD2_SENSOR_INTEGER_FUEL_RAIL_ABSOLUTE_PRESSURE = 22
+OBD2_SENSOR_INTEGER_ENGINE_OIL_TEMPERATURE = 23
+OBD2_SENSOR_INTEGER_DRIVER_DEMAND_PERCENT_TORQUE = 24
+OBD2_SENSOR_INTEGER_ENGINE_ACTUAL_PERCENT_TORQUE = 25
+OBD2_SENSOR_INTEGER_ENGINE_REFERENCE_PERCENT_TORQUE = 26
+OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_IDLE = 27
+OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28
+OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29
+OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30
+OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31
+OBD2_SENSOR_INTEGER_LAST_SYSTEM_INDEX = OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4
+OBD2_SENSOR_INTEGER_VENDOR_START_INDEX = OBD2_SENSOR_INTEGER_LAST_SYSTEM_INDEX + 1
+
+
+
+OBD2_SENSOR_FLOAT_CALCULATED_ENGINE_LOAD = 0
+OBD2_SENSOR_FLOAT_ENGINE_COOLANT_TEMPERATURE = 1
+OBD2_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK1 = 2
+OBD2_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK1 = 3
+OBD2_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK2 = 4
+OBD2_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK2 = 5
+OBD2_SENSOR_FLOAT_FUEL_PRESSURE = 6
+OBD2_SENSOR_FLOAT_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7
+OBD2_SENSOR_FLOAT_ENGINE_RPM = 8
+OBD2_SENSOR_FLOAT_VEHICLE_SPEED = 9
+OBD2_SENSOR_FLOAT_TIMING_ADVANCE = 10
+OBD2_SENSOR_FLOAT_MAF_AIR_FLOW_RATE = 11
+OBD2_SENSOR_FLOAT_THROTTLE_POSITION = 12
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_VOLTAGE = 13
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_VOLTAGE = 16
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_VOLTAGE = 19
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_VOLTAGE = 22
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_VOLTAGE = 25
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_VOLTAGE = 28
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_VOLTAGE = 31
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_VOLTAGE = 34
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35
+OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36
+OBD2_SENSOR_FLOAT_FUEL_RAIL_PRESSURE = 37
+OBD2_SENSOR_FLOAT_FUEL_RAIL_GAUGE_PRESSURE = 38
+OBD2_SENSOR_FLOAT_COMMANDED_EXHAUST_GAS_RECIRCULATION = 39
+OBD2_SENSOR_FLOAT_EXHAUST_GAS_RECIRCULATION_ERROR = 40
+OBD2_SENSOR_FLOAT_COMMANDED_EVAPORATIVE_PURGE = 41
+OBD2_SENSOR_FLOAT_FUEL_TANK_LEVEL_INPUT = 42
+OBD2_SENSOR_FLOAT_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43
+OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44
+OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45
+OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46
+OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47
+OBD2_SENSOR_FLOAT_ABSOLUTE_LOAD_VALUE = 48
+OBD2_SENSOR_FLOAT_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49
+OBD2_SENSOR_FLOAT_RELATIVE_THROTTLE_POSITION = 50
+OBD2_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_B = 51
+OBD2_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_C = 52
+OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_D = 53
+OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_E = 54
+OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_F = 55
+OBD2_SENSOR_FLOAT_COMMANDED_THROTTLE_ACTUATOR = 56
+OBD2_SENSOR_FLOAT_ETHANOL_FUEL_PERCENTAGE = 57
+OBD2_SENSOR_FLOAT_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58
+OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59
+OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60
+OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61
+OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62
+OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63
+OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64
+OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65
+OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66
+OBD2_SENSOR_FLOAT_RELATIVE_ACCELERATOR_PEDAL_POSITION = 67
+OBD2_SENSOR_FLOAT_HYBRID_BATTERY_PACK_REMAINING_LIFE = 68
+OBD2_SENSOR_FLOAT_FUEL_INJECTION_TIMING = 69
+OBD2_SENSOR_FLOAT_ENGINE_FUEL_RATE = 70
+OBD2_SENSOR_FLOAT_LAST_SYSTEM_INDEX = OBD2_SENSOR_FLOAT_ENGINE_FUEL_RATE
+OBD2_SENSOR_FLOAT_VENDOR_START_INDEX = OBD2_SENSOR_FLOAT_LAST_SYSTEM_INDEX + 1
+
+
diff --git a/tools/emulator/gui.py b/tools/emulator/gui.py
new file mode 100755
index 0000000..87d4925
--- /dev/null
+++ b/tools/emulator/gui.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+#
+
+# A simple GUI to remotely actuate the Vehicle HAL via the eumalator
+
+import sys
+import vhal_consts_2_1 as c
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from vhal_emulator import Vhal
+
+
+# Main window setup
+def window():
+ app = QApplication(sys.argv)
+ widget = QWidget()
+ widget.setWindowTitle("VHal Driver")
+ widget.setGeometry(100,100,200,50)
+ topLevelLayout = QHBoxLayout()
+ widget.setLayout(topLevelLayout)
+
+ shiftLayout = QVBoxLayout()
+ topLevelLayout.addLayout(shiftLayout)
+
+ gearTitle = QLabel(widget)
+ gearTitle.setText("Gear Shift")
+ shiftLayout.addWidget(gearTitle);
+
+ gearDisplay = QLabel(widget)
+ shiftLayout.addWidget(gearDisplay);
+
+ slider = QSlider(Qt.Vertical)
+ slider.setMinimum(0)
+ slider.setMaximum(2)
+ slider.setInvertedAppearance(True)
+ slider.valueChanged.connect(lambda:sliderMove(slider, gearDisplay))
+ shiftLayout.addWidget(slider)
+ sliderMove(slider, gearDisplay)
+
+
+ buttonLayout = QVBoxLayout()
+ topLevelLayout.addLayout(buttonLayout)
+
+ signalButtonGroup = QButtonGroup()
+
+ bNoSignal = QPushButton("None")
+ bNoSignal.setCheckable(True)
+ bNoSignal.setChecked(True)
+ buttonLayout.addWidget(bNoSignal)
+ signalButtonGroup.addButton(bNoSignal)
+
+ bHazards = QPushButton("Hazards")
+ bHazards.setCheckable(True)
+ buttonLayout.addWidget(bHazards)
+ signalButtonGroup.addButton(bHazards)
+
+ bLeft = QPushButton("Left")
+ bLeft.setCheckable(True)
+ buttonLayout.addWidget(bLeft)
+ signalButtonGroup.addButton(bLeft)
+
+ bRight = QPushButton("Right")
+ bRight.setCheckable(True)
+ buttonLayout.addWidget(bRight)
+ signalButtonGroup.addButton(bRight)
+
+ signalButtonGroup.buttonClicked.connect(lambda:onSignalClicked(signalButtonGroup))
+
+ widget.show()
+ sys.exit(app.exec_())
+
+
+def onSignalClicked(group):
+ print "signal "+group.checkedButton().text()+" is active"
+ try:
+ vhal.setProperty(c.VEHICLEPROPERTY_TURN_SIGNAL_STATE, 0, group.checkedId())
+ except:
+ print "Ignoring error setting property 0x{:08X}".format(c.VEHICLEPROPERTY_TURN_SIGNAL_STATE)
+
+
+def sliderMove(slider, gearDisplay):
+ if slider.value() == 0:
+ gearName = 'park'
+ vhal.setProperty(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_PARK)
+ elif slider.value() == 1:
+ gearName = 'reverse'
+ vhal.setProperty(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_REVERSE)
+ elif slider.value() == 2:
+ gearName = 'drive'
+ vhal.setProperty(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_DRIVE)
+ else:
+ gearName = "UNK"
+ print "slider "+slider.objectName()+" is now "+str(slider.value())+" = "+gearName
+ gearDisplay.setText(gearName)
+
+
+if __name__ == '__main__':
+ print "Starting VHal driver GUI"
+
+ vhal = Vhal(c.vhal_types_2_0)
+
+ # Put the car in park so we start in a known state (consistent with the GUI default state)
+ vhal.setProperty(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_PARK)
+
+ # Start the main UI -- never returns
+ window()
diff --git a/tools/emulator/obd2_to_diagjson.py b/tools/emulator/obd2_to_diagjson.py
new file mode 100755
index 0000000..89066df
--- /dev/null
+++ b/tools/emulator/obd2_to_diagjson.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2017 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.
+#
+
+# OBD2 standard sensor indices are different from those used by the
+# Android Auto Diagnostics API. This script maps from OBD2 sensors to
+# those expected by the Diagnostics API.
+# To use:
+# ./obd2_to_diagjson.py --src file1.json --dst file2.json
+# It is acceptable and supported to point --src and --dst to the same file
+
+import collections
+import json
+import os, os.path, sys
+
+class Json(object):
+ @classmethod
+ def load(cls, file):
+ return Json(json.load(file))
+
+ @classmethod
+ def wrapIfNeeded(cls, item):
+ if isinstance(item, list) or isinstance(item, dict):
+ return Json(item)
+ return item
+
+ def __init__(self, doc):
+ self.doc = doc
+
+ def __str__(self):
+ return str(self.doc)
+
+ def __repr__(self):
+ return self.__str__()
+
+ def __getattr__(self, attr):
+ return Json.wrapIfNeeded(self.doc.get(attr))
+
+ def __iter__(self):
+ class Iter(object):
+ def __init__(self, doc):
+ self.doc = doc.__iter__()
+
+ def __next__(self):
+ return Json.wrapIfNeeded(self.doc.__next__())
+
+ return Iter(self.doc)
+
+class OrderedStore(object):
+ def __init__(self):
+ self.__dict__['store'] = collections.OrderedDict()
+
+ def __setattr__(self, name, value):
+ self.__dict__['store'][name] = value
+
+ def __getattr__(self, name):
+ return self.__dict__['store'][name]
+
+ def get(self, name, default=None):
+ return self.__dict__['store'].get(name, default)
+
+ def getStore(self):
+ return self.__dict__['store']
+
+ def __iter__(self):
+ return iter(self.__dict__['store'])
+
+ def __delattr__(self, name):
+ del self.__dict__['store'][name]
+
+ def __str__(self):
+ return str(self.__dict__['store'])
+
+ def toJSON(self):
+ return json.dumps(self.store)
+
+class Event(object):
+ def __init__(self):
+ self.store = OrderedStore()
+
+ def setTimestamp(self, timestamp):
+ self.store.timestamp = timestamp
+ return self
+
+ def getTimestamp(self):
+ return self.store.timestamp
+
+ def setType(self, type):
+ self.store.type = type
+ return self
+
+ def getType(self):
+ return self.store.type
+
+ def setStringValue(self, string):
+ if string:
+ self.store.stringValue = string
+ return self
+
+ def getStringValue(self):
+ return self.store.get('stringValue')
+
+ def setIntValue(self, id, value):
+ if 'intValues' not in self.store:
+ self.store.intValues = []
+ d = collections.OrderedDict()
+ d['id'] = id
+ d['value'] = value
+ self.store.intValues.append(d)
+ return self
+
+ def intValues(self):
+ if 'intValues' not in self.store:
+ return []
+ for value in self.store.intValues:
+ yield (value['id'], value['value'])
+
+ def setFloatValue(self, id, value):
+ if 'floatValues' not in self.store:
+ self.store.floatValues = []
+ d = collections.OrderedDict()
+ d['id'] = id
+ d['value'] = value
+ self.store.floatValues.append(d)
+ return self
+
+ def floatValues(self):
+ if 'floatValues' not in self.store:
+ return []
+ for value in self.store.floatValues:
+ yield (value['id'], value['value'])
+
+ @classmethod
+ def fromJson(cls, json):
+ event = Event()
+ event.setTimestamp(json.timestamp)
+ event.setType(json.type)
+ for intValue in json.intValues:
+ event.setIntValue(intValue.id, intValue.value)
+ for floatValue in json.floatValues:
+ event.setFloatValue(floatValue.id, floatValue.value)
+ event.setStringValue(json.stringValue)
+ return event
+
+ def transform(self, intMapping, floatMapping):
+ event = Event()
+ event.setTimestamp(self.getTimestamp())
+ event.setType(self.getType())
+ for id, value in self.intValues():
+ if id in intMapping:
+ intMapping[id](event, value)
+ else:
+ print('warning: integer id 0x%x not found in mapping. dropped.' % id)
+ for id, value in self.floatValues():
+ if id in floatMapping:
+ floatMapping[id](event, value)
+ else:
+ print('warning: float id 0x%x not found in mapping. dropped.' % id)
+ event.setStringValue(self.getStringValue())
+ return event
+
+ def getStore(self):
+ return self.store.getStore()
+
+class EventEncoder(json.JSONEncoder):
+ def default(self, o):
+ if isinstance(o, Event):
+ return o.getStore()
+
+# Mappings between standard OBD2 sensors and the indices
+# used by Vehicle HAL
+intSensorsMapping = {
+ 0x03 : lambda event,value: event.setIntValue(0, value),
+ 0x05 : lambda event,value: event.setFloatValue(1, value),
+ 0x0A : lambda event,value: event.setIntValue(22, value),
+ 0x0C : lambda event,value: event.setFloatValue(8, value),
+ 0x0D : lambda event,value: event.setFloatValue(9, value),
+ 0x1F : lambda event,value: event.setIntValue(7, value),
+ 0x5C : lambda event,value: event.setIntValue(23, value),
+}
+
+floatSensorsMapping = {
+ 0x04 : lambda event, value: event.setFloatValue(0, value),
+ 0x06 : lambda event, value: event.setFloatValue(2, value),
+ 0x07 : lambda event, value: event.setFloatValue(3, value),
+ 0x08 : lambda event, value: event.setFloatValue(4, value),
+ 0x09 : lambda event, value: event.setFloatValue(5, value),
+ 0x11 : lambda event, value: event.setFloatValue(12, value),
+ 0x2F : lambda event, value: event.setFloatValue(42, value),
+ 0x46 : lambda event, value: event.setIntValue(13, int(value)),
+}
+
+def parseOptions():
+ from argparse import ArgumentParser
+ parser = ArgumentParser(description='OBD2 to Diagnostics JSON Converter')
+ parser.add_argument('--src', '-S', dest='source_file',
+ help='The source file to convert from', required=True)
+ parser.add_argument('--dst', '-D', dest='destination_file',
+ help='The destination file to convert to', required=True)
+ return parser.parse_args()
+
+args = parseOptions()
+if not os.path.exists(args.source_file):
+ print('source file %s does not exist' % args.source_file)
+ sys.exit(1)
+
+source_json = Json.load(open(args.source_file))
+dest_events = []
+
+for source_json_event in source_json:
+ source_event = Event.fromJson(source_json_event)
+ destination_event = source_event.transform(intSensorsMapping, floatSensorsMapping)
+ dest_events.append(destination_event)
+
+json.dump(dest_events, open(args.destination_file, 'w'), cls=EventEncoder)
diff --git a/tools/emulator/vhal_const_generate.py b/tools/emulator/vhal_const_generate.py
new file mode 100755
index 0000000..6695bd3
--- /dev/null
+++ b/tools/emulator/vhal_const_generate.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3.4
+#
+# Copyright (C) 2017 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.
+#
+
+# This script generates vhal_consts_x_y.py files for use in vhal_emulator
+# They are generated from corresponding data in Vehicle HAL types.hal files
+# To run, invoke at a shell by saying:
+# $ packages/services/Car/tools/emulator/vhal_const_generate.py
+# The script will automatically locate itself and the required HAL files and will write next
+# to itself vhal_consts_x.y.py for any version of Vehicle HAL that it knows about
+# Those files can then be used with vhal_emulator.py as per that script's documentation
+
+from __future__ import print_function
+
+import datetime
+
+def printHeader(dest):
+ year = datetime.datetime.now().year
+ print("# Copyright (C) %s The Android Open Source Project" % year, file=dest)
+ print("#", file=dest)
+ print("# Licensed under the Apache License, Version 2.0 (the \"License\");", file=dest)
+ print("# you may not use this file except in compliance with the License.", file=dest)
+ print("# You may obtain a copy of the License at", file=dest)
+ print("#", file=dest)
+ print("# http://www.apache.org/licenses/LICENSE-2.0", file=dest)
+ print("#", file=dest)
+ print("# Unless required by applicable law or agreed to in writing, software", file=dest)
+ print("# distributed under the License is distributed on an \"AS IS\" BASIS,", file=dest)
+ print("# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", file=dest)
+ print("# See the License for the specific language governing permissions and", file=dest)
+ print("# limitations under the License.", file=dest)
+ print("#", file=dest)
+ print("# DO NOT EDIT MANUALLY", file=dest)
+ print("# This file was autogenerated by vhal_const_generate.py", file=dest)
+
+def printEnum(doc, name, dest, postprocess=lambda x: x):
+ # Construct a value name prefix from the group name
+ valueNamePrefix = name.upper() + '_'
+
+ enum_object = doc['enums'][name]
+ print("\n# %s" % name, file=dest)
+ for case in enum_object.cases:
+ print('%s%s = %s' % (valueNamePrefix, case.name,
+ postprocess(case.value.resolve(enum_object, doc))),
+ file=dest)
+
+import os, os.path
+import sys
+
+script_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)))
+parent_location = os.path.abspath(os.path.join(script_directory, '..'))
+sys.path.append(parent_location)
+
+# hidl_parser depends on a custom Python package that requires installation
+# give user guidance should the import fail
+try:
+ from hidl_parser import parser
+except ImportError as e:
+ isPly = False
+ pipTool = "pip%s" % ("3" if sys.version_info > (3,0) else "")
+ if hasattr(e, 'name'):
+ if e.name == 'ply': isPly = True
+ elif hasattr(e, 'message'):
+ if e.message.endswith('ply'): isPly = True
+ if isPly:
+ print('could not import ply.')
+ print('ply is available as part of an Android checkout in external/ply')
+ print('or it can be installed via "sudo %s install ply"' % pipTool)
+ sys.exit(1)
+ else:
+ raise e
+
+android_build_top = os.environ.get("ANDROID_BUILD_TOP", None)
+if android_build_top is not None:
+ vhal_location = os.path.join(android_build_top, 'hardware','interfaces','automotive','vehicle')
+else:
+ vhal_location = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ '..','..','..','..','..','hardware','interfaces','automotive','vehicle'
+ ))
+if not(os.path.exists(vhal_location) and os.path.isdir(vhal_location)):
+ print("Vehicle HAL was not found at %s. lunch may provide a correct environment, or files moved" % vhal_location)
+ sys.exit(1)
+
+vhal_20_file = os.path.join(vhal_location, '2.0', 'types.hal')
+vhal_21_file = os.path.join(vhal_location, '2.1', 'types.hal')
+
+print("Generating content from Vehicle HAL 2.0 (%s) and 2.1 (%s)" % (vhal_20_file, vhal_21_file))
+
+vhal_20_doc = parser.parse(vhal_20_file)
+vhal_21_doc = parser.parse(vhal_21_file)
+
+# Work around the fact that the parser doesn't (yet?) deal with inheritance.
+# WARNING: This pattern is rather unsafe since we're not merging the lists as we should!
+vhal_21_doc['enums']['VehiclePropertyGroup'] = vhal_20_doc['enums']['VehiclePropertyGroup']
+vhal_21_doc['enums']['VehiclePropertyType'] = vhal_20_doc['enums']['VehiclePropertyType']
+vhal_21_doc['enums']['VehicleArea'] = vhal_20_doc['enums']['VehicleArea']
+
+def generateHal20():
+ print("********************************")
+ print("Generating VHal 2.0 constants...")
+ vhal_20_file = open(os.path.join(script_directory, 'vhal_consts_2_0.py'), 'w')
+
+ printHeader(vhal_20_file)
+
+ for group in vhal_20_doc['enums']:
+ print(group)
+ printEnum(vhal_20_doc, group, vhal_20_file, lambda x : hex(x))
+
+ print("\n# Create a container of value_type constants to be used by vhal_emulator", file=vhal_20_file)
+ print("class vhal_types_2_0:", file=vhal_20_file)
+ print(" TYPE_STRING = [VEHICLEPROPERTYTYPE_STRING]", file=vhal_20_file)
+ print(" TYPE_BYTES = [VEHICLEPROPERTYTYPE_BYTES]", file=vhal_20_file)
+ print(" TYPE_INT32 = [VEHICLEPROPERTYTYPE_BOOLEAN,", file=vhal_20_file)
+ print(" VEHICLEPROPERTYTYPE_INT32]", file=vhal_20_file)
+ print(" TYPE_INT64 = [VEHICLEPROPERTYTYPE_INT64]", file=vhal_20_file)
+ print(" TYPE_FLOAT = [VEHICLEPROPERTYTYPE_FLOAT]", file=vhal_20_file)
+ print(" TYPE_INT32S = [VEHICLEPROPERTYTYPE_INT32_VEC]", file=vhal_20_file)
+ print(" TYPE_FLOATS = [VEHICLEPROPERTYTYPE_FLOAT_VEC]", file=vhal_20_file)
+ print(" TYPE_COMPLEX = [VEHICLEPROPERTYTYPE_COMPLEX]", file=vhal_20_file)
+
+def generateHal21():
+ print("********************************")
+ print("Generating VHal 2.1 constants...")
+ vhal_21_file = open(os.path.join(script_directory, 'vhal_consts_2_1.py'), 'w')
+ printHeader(vhal_21_file)
+ print('from vhal_consts_2_0 import *', file=vhal_21_file)
+
+ for group in vhal_21_doc['enums']:
+ print(group)
+ printEnum(vhal_21_doc, group, vhal_21_file, lambda x : hex(x))
+
+
+generateHal20()
+generateHal21()
diff --git a/tools/emulator/vhal_consts_2_0.py b/tools/emulator/vhal_consts_2_0.py
index f2433f6..50518fd 100644
--- a/tools/emulator/vhal_consts_2_0.py
+++ b/tools/emulator/vhal_consts_2_0.py
@@ -1,10 +1,10 @@
-# Copyright 2017 Google Inc.
+# Copyright (C) 2017 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
+# 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,
@@ -12,164 +12,427 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# DO NOT EDIT MANUALLY
+# This file was autogenerated by vhal_const_generate.py
-"""
- This file contains constants defined in hardware/interfaces/vehicle/2.0/types.hal
+# VehicleApPowerSetState
+VEHICLEAPPOWERSETSTATE_BOOT_COMPLETE = 0x1
+VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_ENTRY = 0x2
+VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_EXIT = 0x3
+VEHICLEAPPOWERSETSTATE_SHUTDOWN_POSTPONE = 0x4
+VEHICLEAPPOWERSETSTATE_SHUTDOWN_START = 0x5
+VEHICLEAPPOWERSETSTATE_DISPLAY_OFF = 0x6
+VEHICLEAPPOWERSETSTATE_DISPLAY_ON = 0x7
- Constants in this file are parsed from:
- out/soong/.intermediates/hardware/interfaces/automotive/vehicle/2.0/android.hardware.automotive.vehicle@2.0_genc++_headers/gen/android/hardware/automotive/vehicle/2.0/types.h
+# VehicleApPowerStateIndex
+VEHICLEAPPOWERSTATEINDEX_STATE = 0x0
+VEHICLEAPPOWERSTATEINDEX_ADDITIONAL = 0x1
- Currently, there is no script to auto-generate this constants file. The file is generated by
- copying enum fields into an editor and running a macro to format it. The elements being used
- are shown in the following table:
+# VehicleAudioFocusRequest
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN = 0x1
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT = 0x2
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT_MAY_DUCK = 0x3
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT_NO_DUCK = 0x4
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_RELEASE = 0x5
- type.h file: this file:
- VehiclePropertyType enum --> VEHICLE_VALUE_TYPE_*
- VehicleProperty enum --> VEHICLE_PROPERTY_*
- VehicleAreaZone enum --> VEHICLE_ZONE_*
- VehiclePropertyType enum --> class vhal_types_2_0
-"""
+# VehicleDisplay
+VEHICLEDISPLAY_MAIN = 0x0
+VEHICLEDISPLAY_INSTRUMENT_CLUSTER = 0x1
-# Vehicle Property ID
-VEHICLE_PROPERTY_INFO_VIN = 286261504
-VEHICLE_PROPERTY_INFO_MAKE = 286261505
-VEHICLE_PROPERTY_INFO_MODEL = 286261506
-VEHICLE_PROPERTY_INFO_MODEL_YEAR = 289407235
-VEHICLE_PROPERTY_INFO_FUEL_CAPACITY = 291504388
-VEHICLE_PROPERTY_PERF_ODOMETER = 291504644
-VEHICLE_PROPERTY_PERF_VEHICLE_SPEED = 291504647
-VEHICLE_PROPERTY_ENGINE_COOLANT_TEMP = 291504897
-VEHICLE_PROPERTY_ENGINE_OIL_TEMP = 291504900
-VEHICLE_PROPERTY_ENGINE_RPM = 291504901
-VEHICLE_PROPERTY_GEAR_SELECTION = 289408000
-VEHICLE_PROPERTY_CURRENT_GEAR = 289408001
-VEHICLE_PROPERTY_PARKING_BRAKE_ON = 287310850
-VEHICLE_PROPERTY_DRIVING_STATUS = 289408004
-VEHICLE_PROPERTY_FUEL_LEVEL_LOW = 287310853
-VEHICLE_PROPERTY_NIGHT_MODE = 287310855
-VEHICLE_PROPERTY_TURN_SIGNAL_STATE = 289408008
-VEHICLE_PROPERTY_IGNITION_STATE = 289408009
-VEHICLE_PROPERTY_HVAC_FAN_SPEED = 306185472
-VEHICLE_PROPERTY_HVAC_FAN_DIRECTION = 306185473
-VEHICLE_PROPERTY_HVAC_TEMPERATURE_CURRENT = 308282626
-VEHICLE_PROPERTY_HVAC_TEMPERATURE_SET = 308282627
-VEHICLE_PROPERTY_HVAC_DEFROSTER = 320865540
-VEHICLE_PROPERTY_HVAC_AC_ON = 304088325
-VEHICLE_PROPERTY_HVAC_MAX_AC_ON = 304088326
-VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON = 304088327
-VEHICLE_PROPERTY_HVAC_RECIRC_ON = 304088328
-VEHICLE_PROPERTY_HVAC_DUAL_ON = 304088329
-VEHICLE_PROPERTY_HVAC_AUTO_ON = 304088330
-VEHICLE_PROPERTY_HVAC_SEAT_TEMPERATURE = 356517131
-VEHICLE_PROPERTY_HVAC_SIDE_MIRROR_HEAT = 339739916
-VEHICLE_PROPERTY_HVAC_STEERING_WHEEL_TEMP = 289408269
-VEHICLE_PROPERTY_HVAC_TEMPERATURE_UNITS = 306185486
-VEHICLE_PROPERTY_HVAC_ACTUAL_FAN_SPEED_RPM = 306185487
-VEHICLE_PROPERTY_HVAC_FAN_DIRECTION_AVAILABLE = 306185489
-VEHICLE_PROPERTY_HVAC_POWER_ON = 304088336
-VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE = 291505923
-VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE = 291505924
-VEHICLE_PROPERTY_RADIO_PRESET = 289474561
-VEHICLE_PROPERTY_AUDIO_FOCUS = 289474816
-VEHICLE_PROPERTY_AUDIO_FOCUS_EXT_SYNC = 289474832
-VEHICLE_PROPERTY_AUDIO_VOLUME = 289474817
-VEHICLE_PROPERTY_AUDIO_VOLUME_EXT_SYNC = 289474833
-VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT = 289474818
-VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY = 289474819
-VEHICLE_PROPERTY_AUDIO_HW_VARIANT = 289409284
-VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT = 289474821
-VEHICLE_PROPERTY_AUDIO_STREAM_STATE = 289474822
-VEHICLE_PROPERTY_AUDIO_PARAMETERS = 286263559
-VEHICLE_PROPERTY_AP_POWER_STATE = 2560
-VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS = 289409537
-VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON = 289409538
-VEHICLE_PROPERTY_HW_KEY_INPUT = 289475088
-VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO = 289475104
-VEHICLE_PROPERTY_UNIX_TIME = 290458160
-VEHICLE_PROPERTY_CURRENT_TIME_IN_SECONDS = 289409585
-VEHICLE_PROPERTY_DOOR_POS = 373295872
-VEHICLE_PROPERTY_DOOR_MOVE = 373295873
-VEHICLE_PROPERTY_DOOR_LOCK = 371198722
-VEHICLE_PROPERTY_MIRROR_Z_POS = 339741504
-VEHICLE_PROPERTY_MIRROR_Z_MOVE = 339741505
-VEHICLE_PROPERTY_MIRROR_Y_POS = 339741506
-VEHICLE_PROPERTY_MIRROR_Y_MOVE = 339741507
-VEHICLE_PROPERTY_MIRROR_LOCK = 287312708
-VEHICLE_PROPERTY_MIRROR_FOLD = 287312709
-VEHICLE_PROPERTY_SEAT_MEMORY_SELECT = 356518784
-VEHICLE_PROPERTY_SEAT_MEMORY_SET = 356518785
-VEHICLE_PROPERTY_SEAT_BELT_BUCKLED = 354421634
-VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_POS = 356518787
-VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_MOVE = 356518788
-VEHICLE_PROPERTY_SEAT_FORE_AFT_POS = 356518789
-VEHICLE_PROPERTY_SEAT_FORE_AFT_MOVE = 356518790
-VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_POS = 356518791
-VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_MOVE = 356518792
-VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_POS = 356518793
-VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_MOVE = 356518794
-VEHICLE_PROPERTY_SEAT_HEIGHT_POS = 356518795
-VEHICLE_PROPERTY_SEAT_HEIGHT_MOVE = 356518796
-VEHICLE_PROPERTY_SEAT_DEPTH_POS = 356518797
-VEHICLE_PROPERTY_SEAT_DEPTH_MOVE = 356518798
-VEHICLE_PROPERTY_SEAT_TILT_POS = 356518799
-VEHICLE_PROPERTY_SEAT_TILT_MOVE = 356518800
-VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_POS = 356518801
-VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE = 356518802
-VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS = 356518803
-VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 356518804
-VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_POS = 289409941
-VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_MOVE = 356518806
-VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_POS = 356518807
-VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_MOVE = 356518808
-VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_POS = 356518809
-VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_MOVE = 356518810
-VEHICLE_PROPERTY_WINDOW_POS = 289409984
-VEHICLE_PROPERTY_WINDOW_MOVE = 289409985
-VEHICLE_PROPERTY_WINDOW_VENT_POS = 289409986
-VEHICLE_PROPERTY_WINDOW_VENT_MOVE = 289409987
-VEHICLE_PROPERTY_WINDOW_LOCK = 287312836
-VEHICLE_PROPERTY_VEHICLE_MAPS_DATA_SERVICE = 299895808
-VEHICLE_PROPERTY_OBD2_LIVE_FRAME = 299896064
-VEHICLE_PROPERTY_OBD2_FREEZE_FRAME = 299896065
+# VehicleRadioConstants
+VEHICLERADIOCONSTANTS_VEHICLE_RADIO_PRESET_MIN_VALUE = 0x1
-# Vehicle Value Type
-VEHICLE_VALUE_TYPE_STRING = 0x00100000
-VEHICLE_VALUE_TYPE_BOOLEAN = 0x00200000
-VEHICLE_VALUE_TYPE_INT32 = 0x00400000
-VEHICLE_VALUE_TYPE_INT32_VEC = 0x00410000
-VEHICLE_VALUE_TYPE_INT64 = 0x00500000
-VEHICLE_VALUE_TYPE_FLOAT = 0x00600000
-VEHICLE_VALUE_TYPE_FLOAT_VEC = 0x00610000
-VEHICLE_VALUE_TYPE_BYTES = 0x00700000
-VEHICLE_VALUE_TYPE_COMPLEX = 0x00E00000
+# VehicleAudioFocusIndex
+VEHICLEAUDIOFOCUSINDEX_FOCUS = 0x0
+VEHICLEAUDIOFOCUSINDEX_STREAMS = 0x1
+VEHICLEAUDIOFOCUSINDEX_EXTERNAL_FOCUS_STATE = 0x2
+VEHICLEAUDIOFOCUSINDEX_AUDIO_CONTEXTS = 0x3
-# Vehicle zone / area definitions
-VEHICLE_ZONE_ROW_1_LEFT = 0x00000001
-VEHICLE_ZONE_ROW_1_CENTER = 0x00000002
-VEHICLE_ZONE_ROW_1_RIGHT = 0x00000004
-VEHICLE_ZONE_ROW_1_ALL = 0x00000008
-VEHICLE_ZONE_ROW_2_LEFT = 0x00000010
-VEHICLE_ZONE_ROW_2_CENTER = 0x00000020
-VEHICLE_ZONE_ROW_2_RIGHT = 0x00000040
-VEHICLE_ZONE_ROW_2_ALL = 0x00000080
-VEHICLE_ZONE_ROW_3_LEFT = 0x00000100
-VEHICLE_ZONE_ROW_3_CENTER = 0x00000200
-VEHICLE_ZONE_ROW_3_RIGHT = 0x00000400
-VEHICLE_ZONE_ROW_3_ALL = 0x00000800
-VEHICLE_ZONE_ROW_4_LEFT = 0x00001000
-VEHICLE_ZONE_ROW_4_CENTER = 0x00002000
-VEHICLE_ZONE_ROW_4_RIGHT = 0x00004000
-VEHICLE_ZONE_ROW_4_ALL = 0x00008000
-VEHICLE_ZONE_ALL = 0x80000000
+# VehicleProperty
+VEHICLEPROPERTY_INVALID = 0x0
+VEHICLEPROPERTY_INFO_VIN = 0x11100100
+VEHICLEPROPERTY_INFO_MAKE = 0x11100101
+VEHICLEPROPERTY_INFO_MODEL = 0x11100102
+VEHICLEPROPERTY_INFO_MODEL_YEAR = 0x11400103
+VEHICLEPROPERTY_INFO_FUEL_CAPACITY = 0x11600104
+VEHICLEPROPERTY_PERF_ODOMETER = 0x11600204
+VEHICLEPROPERTY_PERF_VEHICLE_SPEED = 0x11600207
+VEHICLEPROPERTY_ENGINE_COOLANT_TEMP = 0x11600301
+VEHICLEPROPERTY_ENGINE_OIL_TEMP = 0x11600304
+VEHICLEPROPERTY_ENGINE_RPM = 0x11600305
+VEHICLEPROPERTY_GEAR_SELECTION = 0x11400400
+VEHICLEPROPERTY_CURRENT_GEAR = 0x11400401
+VEHICLEPROPERTY_PARKING_BRAKE_ON = 0x11200402
+VEHICLEPROPERTY_DRIVING_STATUS = 0x11400404
+VEHICLEPROPERTY_FUEL_LEVEL_LOW = 0x11200405
+VEHICLEPROPERTY_NIGHT_MODE = 0x11200407
+VEHICLEPROPERTY_TURN_SIGNAL_STATE = 0x11400408
+VEHICLEPROPERTY_IGNITION_STATE = 0x11400409
+VEHICLEPROPERTY_HVAC_FAN_SPEED = 0x12400500
+VEHICLEPROPERTY_HVAC_FAN_DIRECTION = 0x12400501
+VEHICLEPROPERTY_HVAC_TEMPERATURE_CURRENT = 0x12600502
+VEHICLEPROPERTY_HVAC_TEMPERATURE_SET = 0x12600503
+VEHICLEPROPERTY_HVAC_DEFROSTER = 0x13200504
+VEHICLEPROPERTY_HVAC_AC_ON = 0x12200505
+VEHICLEPROPERTY_HVAC_MAX_AC_ON = 0x12200506
+VEHICLEPROPERTY_HVAC_MAX_DEFROST_ON = 0x12200507
+VEHICLEPROPERTY_HVAC_RECIRC_ON = 0x12200508
+VEHICLEPROPERTY_HVAC_DUAL_ON = 0x12200509
+VEHICLEPROPERTY_HVAC_AUTO_ON = 0x1220050a
+VEHICLEPROPERTY_HVAC_SEAT_TEMPERATURE = 0x1540050b
+VEHICLEPROPERTY_HVAC_SIDE_MIRROR_HEAT = 0x1440050c
+VEHICLEPROPERTY_HVAC_STEERING_WHEEL_TEMP = 0x1140050d
+VEHICLEPROPERTY_HVAC_TEMPERATURE_UNITS = 0x1240050e
+VEHICLEPROPERTY_HVAC_ACTUAL_FAN_SPEED_RPM = 0x1240050f
+VEHICLEPROPERTY_HVAC_FAN_DIRECTION_AVAILABLE = 0x12400511
+VEHICLEPROPERTY_HVAC_POWER_ON = 0x12200510
+VEHICLEPROPERTY_ENV_OUTSIDE_TEMPERATURE = 0x11600703
+VEHICLEPROPERTY_ENV_CABIN_TEMPERATURE = 0x11600704
+VEHICLEPROPERTY_RADIO_PRESET = 0x11410801
+VEHICLEPROPERTY_AUDIO_FOCUS = 0x11410900
+VEHICLEPROPERTY_AUDIO_FOCUS_EXT_SYNC = 0x11410910
+VEHICLEPROPERTY_AUDIO_VOLUME = 0x11410901
+VEHICLEPROPERTY_AUDIO_VOLUME_EXT_SYNC = 0x11410911
+VEHICLEPROPERTY_AUDIO_VOLUME_LIMIT = 0x11410902
+VEHICLEPROPERTY_AUDIO_ROUTING_POLICY = 0x11410903
+VEHICLEPROPERTY_AUDIO_HW_VARIANT = 0x11400904
+VEHICLEPROPERTY_AUDIO_EXT_ROUTING_HINT = 0x11410905
+VEHICLEPROPERTY_AUDIO_STREAM_STATE = 0x11410906
+VEHICLEPROPERTY_AUDIO_PARAMETERS = 0x11100907
+VEHICLEPROPERTY_AP_POWER_STATE = 0x11410a00
+VEHICLEPROPERTY_DISPLAY_BRIGHTNESS = 0x11400a01
+VEHICLEPROPERTY_AP_POWER_BOOTUP_REASON = 0x11400a02
+VEHICLEPROPERTY_HW_KEY_INPUT = 0x11410a10
+VEHICLEPROPERTY_INSTRUMENT_CLUSTER_INFO = 0x11410a20
+VEHICLEPROPERTY_UNIX_TIME = 0x11500a30
+VEHICLEPROPERTY_CURRENT_TIME_IN_SECONDS = 0x11400a31
+VEHICLEPROPERTY_DOOR_POS = 0x16400b00
+VEHICLEPROPERTY_DOOR_MOVE = 0x16400b01
+VEHICLEPROPERTY_DOOR_LOCK = 0x16200b02
+VEHICLEPROPERTY_MIRROR_Z_POS = 0x14400b40
+VEHICLEPROPERTY_MIRROR_Z_MOVE = 0x14400b41
+VEHICLEPROPERTY_MIRROR_Y_POS = 0x14400b42
+VEHICLEPROPERTY_MIRROR_Y_MOVE = 0x14400b43
+VEHICLEPROPERTY_MIRROR_LOCK = 0x11200b44
+VEHICLEPROPERTY_MIRROR_FOLD = 0x11200b45
+VEHICLEPROPERTY_SEAT_MEMORY_SELECT = 0x15400b80
+VEHICLEPROPERTY_SEAT_MEMORY_SET = 0x15400b81
+VEHICLEPROPERTY_SEAT_BELT_BUCKLED = 0x15200b82
+VEHICLEPROPERTY_SEAT_BELT_HEIGHT_POS = 0x15400b83
+VEHICLEPROPERTY_SEAT_BELT_HEIGHT_MOVE = 0x15400b84
+VEHICLEPROPERTY_SEAT_FORE_AFT_POS = 0x15400b85
+VEHICLEPROPERTY_SEAT_FORE_AFT_MOVE = 0x15400b86
+VEHICLEPROPERTY_SEAT_BACKREST_ANGLE_1_POS = 0x15400b87
+VEHICLEPROPERTY_SEAT_BACKREST_ANGLE_1_MOVE = 0x15400b88
+VEHICLEPROPERTY_SEAT_BACKREST_ANGLE_2_POS = 0x15400b89
+VEHICLEPROPERTY_SEAT_BACKREST_ANGLE_2_MOVE = 0x15400b8a
+VEHICLEPROPERTY_SEAT_HEIGHT_POS = 0x15400b8b
+VEHICLEPROPERTY_SEAT_HEIGHT_MOVE = 0x15400b8c
+VEHICLEPROPERTY_SEAT_DEPTH_POS = 0x15400b8d
+VEHICLEPROPERTY_SEAT_DEPTH_MOVE = 0x15400b8e
+VEHICLEPROPERTY_SEAT_TILT_POS = 0x15400b8f
+VEHICLEPROPERTY_SEAT_TILT_MOVE = 0x15400b90
+VEHICLEPROPERTY_SEAT_LUMBAR_FORE_AFT_POS = 0x15400b91
+VEHICLEPROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE = 0x15400b92
+VEHICLEPROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS = 0x15400b93
+VEHICLEPROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 0x15400b94
+VEHICLEPROPERTY_SEAT_HEADREST_HEIGHT_POS = 0x11400b95
+VEHICLEPROPERTY_SEAT_HEADREST_HEIGHT_MOVE = 0x15400b96
+VEHICLEPROPERTY_SEAT_HEADREST_ANGLE_POS = 0x15400b97
+VEHICLEPROPERTY_SEAT_HEADREST_ANGLE_MOVE = 0x15400b98
+VEHICLEPROPERTY_SEAT_HEADREST_FORE_AFT_POS = 0x15400b99
+VEHICLEPROPERTY_SEAT_HEADREST_FORE_AFT_MOVE = 0x15400b9a
+VEHICLEPROPERTY_WINDOW_POS = 0x11400bc0
+VEHICLEPROPERTY_WINDOW_MOVE = 0x11400bc1
+VEHICLEPROPERTY_WINDOW_VENT_POS = 0x11400bc2
+VEHICLEPROPERTY_WINDOW_VENT_MOVE = 0x11400bc3
+VEHICLEPROPERTY_WINDOW_LOCK = 0x11200bc4
+
+# VehicleAreaZone
+VEHICLEAREAZONE_ROW_1_LEFT = 0x1
+VEHICLEAREAZONE_ROW_1_CENTER = 0x2
+VEHICLEAREAZONE_ROW_1_RIGHT = 0x4
+VEHICLEAREAZONE_ROW_1 = 0x8
+VEHICLEAREAZONE_ROW_2_LEFT = 0x10
+VEHICLEAREAZONE_ROW_2_CENTER = 0x20
+VEHICLEAREAZONE_ROW_2_RIGHT = 0x40
+VEHICLEAREAZONE_ROW_2 = 0x80
+VEHICLEAREAZONE_ROW_3_LEFT = 0x100
+VEHICLEAREAZONE_ROW_3_CENTER = 0x200
+VEHICLEAREAZONE_ROW_3_RIGHT = 0x400
+VEHICLEAREAZONE_ROW_3 = 0x800
+VEHICLEAREAZONE_ROW_4_LEFT = 0x1000
+VEHICLEAREAZONE_ROW_4_CENTER = 0x2000
+VEHICLEAREAZONE_ROW_4_RIGHT = 0x4000
+VEHICLEAREAZONE_ROW_4 = 0x8000
+VEHICLEAREAZONE_WHOLE_CABIN = 0x80000000
+
+# SubscribeFlags
+SUBSCRIBEFLAGS_UNDEFINED = 0x0
+SUBSCRIBEFLAGS_HAL_EVENT = 0x1
+SUBSCRIBEFLAGS_SET_CALL = 0x2
+SUBSCRIBEFLAGS_DEFAULT = 0x1
+
+# Wheel
+WHEEL_UNKNOWN = 0x0
+WHEEL_LEFT_FRONT = 0x1
+WHEEL_RIGHT_FRONT = 0x2
+WHEEL_LEFT_REAR = 0x4
+WHEEL_RIGHT_REAR = 0x8
+
+# StatusCode
+STATUSCODE_OK = 0x0
+STATUSCODE_TRY_AGAIN = 0x1
+STATUSCODE_INVALID_ARG = 0x2
+STATUSCODE_NOT_AVAILABLE = 0x3
+STATUSCODE_ACCESS_DENIED = 0x4
+STATUSCODE_INTERNAL_ERROR = 0x5
+
+# VehicleAudioHwVariantConfigFlag
+VEHICLEAUDIOHWVARIANTCONFIGFLAG_INTERNAL_RADIO_FLAG = 0x1
+
+# VehiclePropertyGroup
+VEHICLEPROPERTYGROUP_SYSTEM = 0x10000000
+VEHICLEPROPERTYGROUP_VENDOR = 0x20000000
+VEHICLEPROPERTYGROUP_MASK = 0xf0000000
+
+# VehicleAudioStreamFlag
+VEHICLEAUDIOSTREAMFLAG_STREAM0_FLAG = 0x1
+VEHICLEAUDIOSTREAMFLAG_STREAM1_FLAG = 0x2
+VEHICLEAUDIOSTREAMFLAG_STREAM2_FLAG = 0x4
+
+# VehiclePropertyChangeMode
+VEHICLEPROPERTYCHANGEMODE_STATIC = 0x0
+VEHICLEPROPERTYCHANGEMODE_ON_CHANGE = 0x1
+VEHICLEPROPERTYCHANGEMODE_CONTINUOUS = 0x2
+VEHICLEPROPERTYCHANGEMODE_POLL = 0x3
+VEHICLEPROPERTYCHANGEMODE_ON_SET = 0x4
+
+# VehicleAreaSeat
+VEHICLEAREASEAT_ROW_1_LEFT = 0x1
+VEHICLEAREASEAT_ROW_1_CENTER = 0x2
+VEHICLEAREASEAT_ROW_1_RIGHT = 0x4
+VEHICLEAREASEAT_ROW_2_LEFT = 0x10
+VEHICLEAREASEAT_ROW_2_CENTER = 0x20
+VEHICLEAREASEAT_ROW_2_RIGHT = 0x40
+VEHICLEAREASEAT_ROW_3_LEFT = 0x100
+VEHICLEAREASEAT_ROW_3_CENTER = 0x200
+VEHICLEAREASEAT_ROW_3_RIGHT = 0x400
+
+# VehicleAudioVolumeIndex
+VEHICLEAUDIOVOLUMEINDEX_INDEX_STREAM = 0x0
+VEHICLEAUDIOVOLUMEINDEX_INDEX_VOLUME = 0x1
+VEHICLEAUDIOVOLUMEINDEX_INDEX_STATE = 0x2
+
+# VehicleUnit
+VEHICLEUNIT_SHOULD_NOT_USE = 0x0
+VEHICLEUNIT_METER_PER_SEC = 0x1
+VEHICLEUNIT_RPM = 0x2
+VEHICLEUNIT_HERTZ = 0x3
+VEHICLEUNIT_PERCENTILE = 0x10
+VEHICLEUNIT_MILLIMETER = 0x20
+VEHICLEUNIT_METER = 0x21
+VEHICLEUNIT_KILOMETER = 0x23
+VEHICLEUNIT_CELSIUS = 0x30
+VEHICLEUNIT_FAHRENHEIT = 0x31
+VEHICLEUNIT_KELVIN = 0x32
+VEHICLEUNIT_MILLILITER = 0x40
+VEHICLEUNIT_NANO_SECS = 0x50
+VEHICLEUNIT_SECS = 0x53
+VEHICLEUNIT_YEAR = 0x59
+
+# VehicleAreaMirror
+VEHICLEAREAMIRROR_DRIVER_LEFT = 0x1
+VEHICLEAREAMIRROR_DRIVER_RIGHT = 0x2
+VEHICLEAREAMIRROR_DRIVER_CENTER = 0x4
+
+# VehiclePropertyAccess
+VEHICLEPROPERTYACCESS_NONE = 0x0
+VEHICLEPROPERTYACCESS_READ = 0x1
+VEHICLEPROPERTYACCESS_WRITE = 0x2
+VEHICLEPROPERTYACCESS_READ_WRITE = 0x3
+
+# VehicleAudioContextFlag
+VEHICLEAUDIOCONTEXTFLAG_MUSIC_FLAG = 0x1
+VEHICLEAUDIOCONTEXTFLAG_NAVIGATION_FLAG = 0x2
+VEHICLEAUDIOCONTEXTFLAG_VOICE_COMMAND_FLAG = 0x4
+VEHICLEAUDIOCONTEXTFLAG_CALL_FLAG = 0x8
+VEHICLEAUDIOCONTEXTFLAG_ALARM_FLAG = 0x10
+VEHICLEAUDIOCONTEXTFLAG_NOTIFICATION_FLAG = 0x20
+VEHICLEAUDIOCONTEXTFLAG_UNKNOWN_FLAG = 0x40
+VEHICLEAUDIOCONTEXTFLAG_SAFETY_ALERT_FLAG = 0x80
+VEHICLEAUDIOCONTEXTFLAG_CD_ROM_FLAG = 0x100
+VEHICLEAUDIOCONTEXTFLAG_AUX_AUDIO_FLAG = 0x200
+VEHICLEAUDIOCONTEXTFLAG_SYSTEM_SOUND_FLAG = 0x400
+VEHICLEAUDIOCONTEXTFLAG_RADIO_FLAG = 0x800
+VEHICLEAUDIOCONTEXTFLAG_EXT_SOURCE_FLAG = 0x1000
+
+# VehicleDrivingStatus
+VEHICLEDRIVINGSTATUS_UNRESTRICTED = 0x0
+VEHICLEDRIVINGSTATUS_NO_VIDEO = 0x1
+VEHICLEDRIVINGSTATUS_NO_KEYBOARD_INPUT = 0x2
+VEHICLEDRIVINGSTATUS_NO_VOICE_INPUT = 0x4
+VEHICLEDRIVINGSTATUS_NO_CONFIG = 0x8
+VEHICLEDRIVINGSTATUS_LIMIT_MESSAGE_LEN = 0x10
+
+# VehicleGear
+VEHICLEGEAR_GEAR_NEUTRAL = 0x1
+VEHICLEGEAR_GEAR_REVERSE = 0x2
+VEHICLEGEAR_GEAR_PARK = 0x4
+VEHICLEGEAR_GEAR_DRIVE = 0x8
+VEHICLEGEAR_GEAR_LOW = 0x10
+VEHICLEGEAR_GEAR_1 = 0x10
+VEHICLEGEAR_GEAR_2 = 0x20
+VEHICLEGEAR_GEAR_3 = 0x40
+VEHICLEGEAR_GEAR_4 = 0x80
+VEHICLEGEAR_GEAR_5 = 0x100
+VEHICLEGEAR_GEAR_6 = 0x200
+VEHICLEGEAR_GEAR_7 = 0x400
+VEHICLEGEAR_GEAR_8 = 0x800
+VEHICLEGEAR_GEAR_9 = 0x1000
+
+# VehicleTurnSignal
+VEHICLETURNSIGNAL_NONE = 0x0
+VEHICLETURNSIGNAL_RIGHT = 0x1
+VEHICLETURNSIGNAL_LEFT = 0x2
+VEHICLETURNSIGNAL_EMERGENCY = 0x4
+
+# VehicleApPowerStateShutdownParam
+VEHICLEAPPOWERSTATESHUTDOWNPARAM_SHUTDOWN_IMMEDIATELY = 0x1
+VEHICLEAPPOWERSTATESHUTDOWNPARAM_CAN_SLEEP = 0x2
+VEHICLEAPPOWERSTATESHUTDOWNPARAM_SHUTDOWN_ONLY = 0x3
+
+# VehiclePropertyOperation
+VEHICLEPROPERTYOPERATION_GENERIC = 0x0
+VEHICLEPROPERTYOPERATION_SET = 0x1
+VEHICLEPROPERTYOPERATION_GET = 0x2
+VEHICLEPROPERTYOPERATION_SUBSCRIBE = 0x3
+
+# VehiclePropertyType
+VEHICLEPROPERTYTYPE_STRING = 0x100000
+VEHICLEPROPERTYTYPE_BOOLEAN = 0x200000
+VEHICLEPROPERTYTYPE_INT32 = 0x400000
+VEHICLEPROPERTYTYPE_INT32_VEC = 0x410000
+VEHICLEPROPERTYTYPE_INT64 = 0x500000
+VEHICLEPROPERTYTYPE_FLOAT = 0x600000
+VEHICLEPROPERTYTYPE_FLOAT_VEC = 0x610000
+VEHICLEPROPERTYTYPE_BYTES = 0x700000
+VEHICLEPROPERTYTYPE_COMPLEX = 0xe00000
+VEHICLEPROPERTYTYPE_MASK = 0xff0000
+
+# VehicleAreaDoor
+VEHICLEAREADOOR_ROW_1_LEFT = 0x1
+VEHICLEAREADOOR_ROW_1_RIGHT = 0x4
+VEHICLEAREADOOR_ROW_2_LEFT = 0x10
+VEHICLEAREADOOR_ROW_2_RIGHT = 0x40
+VEHICLEAREADOOR_ROW_3_LEFT = 0x100
+VEHICLEAREADOOR_ROW_3_RIGHT = 0x400
+VEHICLEAREADOOR_HOOD = 0x10000000
+VEHICLEAREADOOR_REAR = 0x20000000
+
+# VehicleHwKeyInputAction
+VEHICLEHWKEYINPUTACTION_ACTION_DOWN = 0x0
+VEHICLEHWKEYINPUTACTION_ACTION_UP = 0x1
+
+# VehicleApPowerStateConfigFlag
+VEHICLEAPPOWERSTATECONFIGFLAG_ENABLE_DEEP_SLEEP_FLAG = 0x1
+VEHICLEAPPOWERSTATECONFIGFLAG_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2
+
+# VehicleIgnitionState
+VEHICLEIGNITIONSTATE_UNDEFINED = 0x0
+VEHICLEIGNITIONSTATE_LOCK = 0x1
+VEHICLEIGNITIONSTATE_OFF = 0x2
+VEHICLEIGNITIONSTATE_ACC = 0x3
+VEHICLEIGNITIONSTATE_ON = 0x4
+VEHICLEIGNITIONSTATE_START = 0x5
+
+# VehicleAudioVolumeLimitIndex
+VEHICLEAUDIOVOLUMELIMITINDEX_STREAM = 0x0
+VEHICLEAUDIOVOLUMELIMITINDEX_MAX_VOLUME = 0x1
+
+# VehicleAreaWindow
+VEHICLEAREAWINDOW_FRONT_WINDSHIELD = 0x1
+VEHICLEAREAWINDOW_REAR_WINDSHIELD = 0x2
+VEHICLEAREAWINDOW_ROOF_TOP = 0x4
+VEHICLEAREAWINDOW_ROW_1_LEFT = 0x10
+VEHICLEAREAWINDOW_ROW_1_RIGHT = 0x20
+VEHICLEAREAWINDOW_ROW_2_LEFT = 0x100
+VEHICLEAREAWINDOW_ROW_2_RIGHT = 0x200
+VEHICLEAREAWINDOW_ROW_3_LEFT = 0x1000
+VEHICLEAREAWINDOW_ROW_3_RIGHT = 0x2000
+
+# VehicleAudioFocusState
+VEHICLEAUDIOFOCUSSTATE_STATE_GAIN = 0x1
+VEHICLEAUDIOFOCUSSTATE_STATE_GAIN_TRANSIENT = 0x2
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_CAN_DUCK = 0x3
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT = 0x4
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS = 0x5
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_EXLCUSIVE = 0x6
+
+# VehicleAudioVolumeCapabilityFlag
+VEHICLEAUDIOVOLUMECAPABILITYFLAG_PERSISTENT_STORAGE = 0x1
+VEHICLEAUDIOVOLUMECAPABILITYFLAG_MASTER_VOLUME_ONLY = 0x2
+
+# VehicleApPowerState
+VEHICLEAPPOWERSTATE_OFF = 0x0
+VEHICLEAPPOWERSTATE_DEEP_SLEEP = 0x1
+VEHICLEAPPOWERSTATE_ON_DISP_OFF = 0x2
+VEHICLEAPPOWERSTATE_ON_FULL = 0x3
+VEHICLEAPPOWERSTATE_SHUTDOWN_PREPARE = 0x4
+
+# VehicleAudioVolumeState
+VEHICLEAUDIOVOLUMESTATE_STATE_OK = 0x0
+VEHICLEAUDIOVOLUMESTATE_LIMIT_REACHED = 0x1
+
+# VehicleAudioRoutingPolicyIndex
+VEHICLEAUDIOROUTINGPOLICYINDEX_STREAM = 0x0
+VEHICLEAUDIOROUTINGPOLICYINDEX_CONTEXTS = 0x1
+
+# VehicleAudioStream
+VEHICLEAUDIOSTREAM_STREAM0 = 0x0
+VEHICLEAUDIOSTREAM_STREAM1 = 0x1
+
+# VehicleInstrumentClusterType
+VEHICLEINSTRUMENTCLUSTERTYPE_NONE = 0x0
+VEHICLEINSTRUMENTCLUSTERTYPE_HAL_INTERFACE = 0x1
+VEHICLEINSTRUMENTCLUSTERTYPE_EXTERNAL_DISPLAY = 0x2
+
+# VehicleAudioExtFocusFlag
+VEHICLEAUDIOEXTFOCUSFLAG_NONE_FLAG = 0x0
+VEHICLEAUDIOEXTFOCUSFLAG_PERMANENT_FLAG = 0x1
+VEHICLEAUDIOEXTFOCUSFLAG_TRANSIENT_FLAG = 0x2
+VEHICLEAUDIOEXTFOCUSFLAG_PLAY_ONLY_FLAG = 0x4
+VEHICLEAUDIOEXTFOCUSFLAG_MUTE_MEDIA_FLAG = 0x8
+
+# VehicleHvacFanDirection
+VEHICLEHVACFANDIRECTION_FACE = 0x1
+VEHICLEHVACFANDIRECTION_FLOOR = 0x2
+VEHICLEHVACFANDIRECTION_FACE_AND_FLOOR = 0x3
+VEHICLEHVACFANDIRECTION_DEFROST = 0x4
+VEHICLEHVACFANDIRECTION_DEFROST_AND_FLOOR = 0x5
+
+# VehicleApPowerBootupReason
+VEHICLEAPPOWERBOOTUPREASON_USER_POWER_ON = 0x0
+VEHICLEAPPOWERBOOTUPREASON_USER_UNLOCK = 0x1
+VEHICLEAPPOWERBOOTUPREASON_TIMER = 0x2
+
+# VehicleArea
+VEHICLEAREA_GLOBAL = 0x1000000
+VEHICLEAREA_ZONE = 0x2000000
+VEHICLEAREA_WINDOW = 0x3000000
+VEHICLEAREA_MIRROR = 0x4000000
+VEHICLEAREA_SEAT = 0x5000000
+VEHICLEAREA_DOOR = 0x6000000
+VEHICLEAREA_MASK = 0xf000000
# Create a container of value_type constants to be used by vhal_emulator
class vhal_types_2_0:
- TYPE_STRING = [VEHICLE_VALUE_TYPE_STRING]
- TYPE_BYTES = [VEHICLE_VALUE_TYPE_BYTES]
- TYPE_INT32 = [VEHICLE_VALUE_TYPE_BOOLEAN,
- VEHICLE_VALUE_TYPE_INT32]
- TYPE_INT64 = [VEHICLE_VALUE_TYPE_INT64]
- TYPE_FLOAT = [VEHICLE_VALUE_TYPE_FLOAT]
- TYPE_INT32S = [VEHICLE_VALUE_TYPE_INT32_VEC]
- TYPE_FLOATS = [VEHICLE_VALUE_TYPE_FLOAT_VEC]
-
+ TYPE_STRING = [VEHICLEPROPERTYTYPE_STRING]
+ TYPE_BYTES = [VEHICLEPROPERTYTYPE_BYTES]
+ TYPE_INT32 = [VEHICLEPROPERTYTYPE_BOOLEAN,
+ VEHICLEPROPERTYTYPE_INT32]
+ TYPE_INT64 = [VEHICLEPROPERTYTYPE_INT64]
+ TYPE_FLOAT = [VEHICLEPROPERTYTYPE_FLOAT]
+ TYPE_INT32S = [VEHICLEPROPERTYTYPE_INT32_VEC]
+ TYPE_FLOATS = [VEHICLEPROPERTYTYPE_FLOAT_VEC]
+ TYPE_COMPLEX = [VEHICLEPROPERTYTYPE_COMPLEX]
diff --git a/tools/emulator/vhal_consts_2_1.py b/tools/emulator/vhal_consts_2_1.py
new file mode 100644
index 0000000..d367b85
--- /dev/null
+++ b/tools/emulator/vhal_consts_2_1.py
@@ -0,0 +1,269 @@
+# Copyright (C) 2017 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.
+#
+# DO NOT EDIT MANUALLY
+# This file was autogenerated by vhal_const_generate.py
+from vhal_consts_2_0 import *
+
+# FuelType
+FUELTYPE_NOT_AVAILABLE = 0x0
+FUELTYPE_GASOLINE = 0x1
+FUELTYPE_METHANOL = 0x2
+FUELTYPE_ETHANOL = 0x3
+FUELTYPE_DIESEL = 0x4
+FUELTYPE_LPG = 0x5
+FUELTYPE_CNG = 0x6
+FUELTYPE_PROPANE = 0x7
+FUELTYPE_ELECTRIC = 0x8
+FUELTYPE_BIFUEL_RUNNING_GASOLINE = 0x9
+FUELTYPE_BIFUEL_RUNNING_METHANOL = 0xa
+FUELTYPE_BIFUEL_RUNNING_ETHANOL = 0xb
+FUELTYPE_BIFUEL_RUNNING_LPG = 0xc
+FUELTYPE_BIFUEL_RUNNING_CNG = 0xd
+FUELTYPE_BIFUEL_RUNNING_PROPANE = 0xe
+FUELTYPE_BIFUEL_RUNNING_ELECTRIC = 0xf
+FUELTYPE_BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 0x10
+FUELTYPE_HYBRID_GASOLINE = 0x11
+FUELTYPE_HYBRID_ETHANOL = 0x12
+FUELTYPE_HYBRID_DIESEL = 0x13
+FUELTYPE_HYBRID_ELECTRIC = 0x14
+FUELTYPE_HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 0x15
+FUELTYPE_HYBRID_REGENERATIVE = 0x16
+FUELTYPE_BIFUEL_RUNNING_DIESEL = 0x17
+
+# VmsBaseMessageIntegerValuesIndex
+VMSBASEMESSAGEINTEGERVALUESINDEX_VMS_MESSAGE_TYPE = 0x0
+
+# SparkIgnitionMonitors
+SPARKIGNITIONMONITORS_EGR_AVAILABLE = 0x40
+SPARKIGNITIONMONITORS_EGR_INCOMPLETE = 0x80
+SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_AVAILABLE = 0x100
+SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x200
+SPARKIGNITIONMONITORS_OXYGEN_SENSOR_AVAILABLE = 0x400
+SPARKIGNITIONMONITORS_OXYGEN_SENSOR_INCOMPLETE = 0x800
+SPARKIGNITIONMONITORS_AC_REFRIGERANT_AVAILABLE = 0x1000
+SPARKIGNITIONMONITORS_AC_REFRIGERANT_INCOMPLETE = 0x2000
+SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_AVAILABLE = 0x4000
+SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x8000
+SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_AVAILABLE = 0x10000
+SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_INCOMPLETE = 0x20000
+SPARKIGNITIONMONITORS_HEATED_CATALYST_AVAILABLE = 0x40000
+SPARKIGNITIONMONITORS_HEATED_CATALYST_INCOMPLETE = 0x80000
+SPARKIGNITIONMONITORS_CATALYST_AVAILABLE = 0x100000
+SPARKIGNITIONMONITORS_CATALYST_INCOMPLETE = 0x200000
+
+# Obd2FloatSensorIndex
+OBD2FLOATSENSORINDEX_CALCULATED_ENGINE_LOAD = 0x0
+OBD2FLOATSENSORINDEX_ENGINE_COOLANT_TEMPERATURE = 0x1
+OBD2FLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK1 = 0x2
+OBD2FLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK1 = 0x3
+OBD2FLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK2 = 0x4
+OBD2FLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK2 = 0x5
+OBD2FLOATSENSORINDEX_FUEL_PRESSURE = 0x6
+OBD2FLOATSENSORINDEX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x7
+OBD2FLOATSENSORINDEX_ENGINE_RPM = 0x8
+OBD2FLOATSENSORINDEX_VEHICLE_SPEED = 0x9
+OBD2FLOATSENSORINDEX_TIMING_ADVANCE = 0xa
+OBD2FLOATSENSORINDEX_MAF_AIR_FLOW_RATE = 0xb
+OBD2FLOATSENSORINDEX_THROTTLE_POSITION = 0xc
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_VOLTAGE = 0xd
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 0xe
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 0xf
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_VOLTAGE = 0x10
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 0x11
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 0x12
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_VOLTAGE = 0x13
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 0x14
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 0x15
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_VOLTAGE = 0x16
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 0x17
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 0x18
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_VOLTAGE = 0x19
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 0x1a
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 0x1b
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_VOLTAGE = 0x1c
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 0x1d
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 0x1e
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_VOLTAGE = 0x1f
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 0x20
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 0x21
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_VOLTAGE = 0x22
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 0x23
+OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 0x24
+OBD2FLOATSENSORINDEX_FUEL_RAIL_PRESSURE = 0x25
+OBD2FLOATSENSORINDEX_FUEL_RAIL_GAUGE_PRESSURE = 0x26
+OBD2FLOATSENSORINDEX_COMMANDED_EXHAUST_GAS_RECIRCULATION = 0x27
+OBD2FLOATSENSORINDEX_EXHAUST_GAS_RECIRCULATION_ERROR = 0x28
+OBD2FLOATSENSORINDEX_COMMANDED_EVAPORATIVE_PURGE = 0x29
+OBD2FLOATSENSORINDEX_FUEL_TANK_LEVEL_INPUT = 0x2a
+OBD2FLOATSENSORINDEX_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x2b
+OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 0x2c
+OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 0x2d
+OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 0x2e
+OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 0x2f
+OBD2FLOATSENSORINDEX_ABSOLUTE_LOAD_VALUE = 0x30
+OBD2FLOATSENSORINDEX_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 0x31
+OBD2FLOATSENSORINDEX_RELATIVE_THROTTLE_POSITION = 0x32
+OBD2FLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_B = 0x33
+OBD2FLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_C = 0x34
+OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_D = 0x35
+OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_E = 0x36
+OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_F = 0x37
+OBD2FLOATSENSORINDEX_COMMANDED_THROTTLE_ACTUATOR = 0x38
+OBD2FLOATSENSORINDEX_ETHANOL_FUEL_PERCENTAGE = 0x39
+OBD2FLOATSENSORINDEX_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x3a
+OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3b
+OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x3c
+OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x3d
+OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x3e
+OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3f
+OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x40
+OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x41
+OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x42
+OBD2FLOATSENSORINDEX_RELATIVE_ACCELERATOR_PEDAL_POSITION = 0x43
+OBD2FLOATSENSORINDEX_HYBRID_BATTERY_PACK_REMAINING_LIFE = 0x44
+OBD2FLOATSENSORINDEX_FUEL_INJECTION_TIMING = 0x45
+OBD2FLOATSENSORINDEX_ENGINE_FUEL_RATE = 0x46
+OBD2FLOATSENSORINDEX_LAST_SYSTEM_INDEX = 0x46
+
+# CommonIgnitionMonitors
+COMMONIGNITIONMONITORS_COMPONENTS_AVAILABLE = 0x1
+COMMONIGNITIONMONITORS_COMPONENTS_INCOMPLETE = 0x2
+COMMONIGNITIONMONITORS_FUEL_SYSTEM_AVAILABLE = 0x4
+COMMONIGNITIONMONITORS_FUEL_SYSTEM_INCOMPLETE = 0x8
+COMMONIGNITIONMONITORS_MISFIRE_AVAILABLE = 0x10
+COMMONIGNITIONMONITORS_MISFIRE_INCOMPLETE = 0x20
+
+# IgnitionMonitorKind
+IGNITIONMONITORKIND_SPARK = 0x0
+IGNITIONMONITORKIND_COMPRESSION = 0x1
+
+# SecondaryAirStatus
+SECONDARYAIRSTATUS_UPSTREAM = 0x1
+SECONDARYAIRSTATUS_DOWNSTREAM_OF_CATALYCIC_CONVERTER = 0x2
+SECONDARYAIRSTATUS_FROM_OUTSIDE_OR_OFF = 0x4
+SECONDARYAIRSTATUS_PUMP_ON_FOR_DIAGNOSTICS = 0x8
+
+# VmsMessageType
+VMSMESSAGETYPE_SUBSCRIBE = 0x1
+VMSMESSAGETYPE_UNSUBSCRIBE = 0x2
+VMSMESSAGETYPE_DATA = 0x3
+VMSMESSAGETYPE_OFFERING = 0x4
+VMSMESSAGETYPE_AVAILABILITY_REQUEST = 0x5
+VMSMESSAGETYPE_AVAILABILITY_RESPONSE = 0x6
+VMSMESSAGETYPE_SUBSCRIPTION_REQUEST = 0x7
+VMSMESSAGETYPE_SUBSCRIPTION_RESPONSE = 0x8
+
+# CompressionIgnitionMonitors
+COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_AVAILABLE = 0x40
+COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_INCOMPLETE = 0x80
+COMPRESSIONIGNITIONMONITORS_PM_FILTER_AVAILABLE = 0x100
+COMPRESSIONIGNITIONMONITORS_PM_FILTER_INCOMPLETE = 0x200
+COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_AVAILABLE = 0x400
+COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_INCOMPLETE = 0x800
+COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_AVAILABLE = 0x1000
+COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_INCOMPLETE = 0x2000
+COMPRESSIONIGNITIONMONITORS_NOx_SCR__AVAILABLE = 0x4000
+COMPRESSIONIGNITIONMONITORS_NOx_SCR_INCOMPLETE = 0x8000
+COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_AVAILABLE = 0x10000
+COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_INCOMPLETE = 0x20000
+
+# VehiclePropertyGroup
+VEHICLEPROPERTYGROUP_SYSTEM = 0x10000000
+VEHICLEPROPERTYGROUP_VENDOR = 0x20000000
+VEHICLEPROPERTYGROUP_MASK = 0xf0000000
+
+# Obd2IntegerSensorIndex
+OBD2INTEGERSENSORINDEX_FUEL_SYSTEM_STATUS = 0x0
+OBD2INTEGERSENSORINDEX_MALFUNCTION_INDICATOR_LIGHT_ON = 0x1
+OBD2INTEGERSENSORINDEX_IGNITION_MONITORS_SUPPORTED = 0x2
+OBD2INTEGERSENSORINDEX_IGNITION_SPECIFIC_MONITORS = 0x3
+OBD2INTEGERSENSORINDEX_INTAKE_AIR_TEMPERATURE = 0x4
+OBD2INTEGERSENSORINDEX_COMMANDED_SECONDARY_AIR_STATUS = 0x5
+OBD2INTEGERSENSORINDEX_NUM_OXYGEN_SENSORS_PRESENT = 0x6
+OBD2INTEGERSENSORINDEX_RUNTIME_SINCE_ENGINE_START = 0x7
+OBD2INTEGERSENSORINDEX_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 0x8
+OBD2INTEGERSENSORINDEX_WARMUPS_SINCE_CODES_CLEARED = 0x9
+OBD2INTEGERSENSORINDEX_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 0xa
+OBD2INTEGERSENSORINDEX_ABSOLUTE_BAROMETRIC_PRESSURE = 0xb
+OBD2INTEGERSENSORINDEX_CONTROL_MODULE_VOLTAGE = 0xc
+OBD2INTEGERSENSORINDEX_AMBIENT_AIR_TEMPERATURE = 0xd
+OBD2INTEGERSENSORINDEX_TIME_WITH_MALFUNCTION_LIGHT_ON = 0xe
+OBD2INTEGERSENSORINDEX_TIME_SINCE_TROUBLE_CODES_CLEARED = 0xf
+OBD2INTEGERSENSORINDEX_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 0x10
+OBD2INTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_VOLTAGE = 0x11
+OBD2INTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_CURRENT = 0x12
+OBD2INTEGERSENSORINDEX_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x13
+OBD2INTEGERSENSORINDEX_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 0x14
+OBD2INTEGERSENSORINDEX_FUEL_TYPE = 0x15
+OBD2INTEGERSENSORINDEX_FUEL_RAIL_ABSOLUTE_PRESSURE = 0x16
+OBD2INTEGERSENSORINDEX_ENGINE_OIL_TEMPERATURE = 0x17
+OBD2INTEGERSENSORINDEX_DRIVER_DEMAND_PERCENT_TORQUE = 0x18
+OBD2INTEGERSENSORINDEX_ENGINE_ACTUAL_PERCENT_TORQUE = 0x19
+OBD2INTEGERSENSORINDEX_ENGINE_REFERENCE_PERCENT_TORQUE = 0x1a
+OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_IDLE = 0x1b
+OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 0x1c
+OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 0x1d
+OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 0x1e
+OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 0x1f
+OBD2INTEGERSENSORINDEX_LAST_SYSTEM_INDEX = 0x1f
+
+# VehicleProperty
+VEHICLEPROPERTY_WHEEL_TICK = 0x11610306
+VEHICLEPROPERTY_OBD2_LIVE_FRAME = 0x11e00d00
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME = 0x11e00d01
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO = 0x11e00d02
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR = 0x11e00d03
+VEHICLEPROPERTY_VEHICLE_MAP_SERVICE = 0x11e00c00
+
+# FuelSystemStatus
+FUELSYSTEMSTATUS_OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 0x1
+FUELSYSTEMSTATUS_CLOSED_LOOP = 0x2
+FUELSYSTEMSTATUS_OPEN_ENGINE_LOAD_OR_DECELERATION = 0x4
+FUELSYSTEMSTATUS_OPEN_SYSTEM_FAILURE = 0x8
+FUELSYSTEMSTATUS_CLOSED_LOOP_BUT_FEEDBACK_FAULT = 0x10
+
+# VehiclePropertyType
+VEHICLEPROPERTYTYPE_STRING = 0x100000
+VEHICLEPROPERTYTYPE_BOOLEAN = 0x200000
+VEHICLEPROPERTYTYPE_INT32 = 0x400000
+VEHICLEPROPERTYTYPE_INT32_VEC = 0x410000
+VEHICLEPROPERTYTYPE_INT64 = 0x500000
+VEHICLEPROPERTYTYPE_FLOAT = 0x600000
+VEHICLEPROPERTYTYPE_FLOAT_VEC = 0x610000
+VEHICLEPROPERTYTYPE_BYTES = 0x700000
+VEHICLEPROPERTYTYPE_COMPLEX = 0xe00000
+VEHICLEPROPERTYTYPE_MASK = 0xff0000
+
+# VmsSimpleMessageIntegerValuesIndex
+VMSSIMPLEMESSAGEINTEGERVALUESINDEX_VMS_LAYER_ID = 0x1
+VMSSIMPLEMESSAGEINTEGERVALUESINDEX_VMS_LAYER_VERSION = 0x2
+
+# VehicleArea
+VEHICLEAREA_GLOBAL = 0x1000000
+VEHICLEAREA_ZONE = 0x2000000
+VEHICLEAREA_WINDOW = 0x3000000
+VEHICLEAREA_MIRROR = 0x4000000
+VEHICLEAREA_SEAT = 0x5000000
+VEHICLEAREA_DOOR = 0x6000000
+VEHICLEAREA_MASK = 0xf000000
+
+# VmsOfferingMessageIntegerValuesIndex
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_VMS_NUMBER_OF_LAYERS_DEPENDENCIES = 0x1
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_FIRST_DEPENDENCIES_INDEX = 0x2
+
+# VmsSubscriptionResponseFormat
+VMSSUBSCRIPTIONRESPONSEFORMAT_SEQUENCE_NUMBER = 0x1
+VMSSUBSCRIPTIONRESPONSEFORMAT_NUMBER_OF_LAYERS = 0x2
+VMSSUBSCRIPTIONRESPONSEFORMAT_FIRST_LAYER = 0x3
diff --git a/tools/emulator/vhal_emulator.py b/tools/emulator/vhal_emulator.py
index 44e5566..98c9f51 100644
--- a/tools/emulator/vhal_emulator.py
+++ b/tools/emulator/vhal_emulator.py
@@ -18,37 +18,37 @@
"""
This module provides a vhal class which sends and receives messages to the vehicle HAL module
- on an Android Auto device. It uses port forwarding via ADB to communicted with the Android
+ on an Android Auto device. It uses port forwarding via ADB to communicate with the Android
device.
Example Usage:
- import vhal_consts_1_0 as c
+ import vhal_consts_2_0 as c
from vhal_emulator import Vhal
# Create an instance of vhal class. Need to pass the vhal_types constants.
- v = Vhal(c.vhal_types_1_0)
+ v = Vhal(c.vhal_types_2_0)
# Get the property config (if desired)
- v.getConfig(c.VEHICLE_PROPERTY_HVAC_TEMPERATURE_SET)
+ v.getConfig(c.VEHICLEPROPERTY_HVAC_TEMPERATURE_SET)
# Get the response message to getConfig()
reply = v.rxMsg()
- print reply
+ print(reply)
# Set left temperature to 70 degrees
- v.setProperty(c.VEHICLE_PROPERTY_HVAC_TEMPERATURE_SET, c.VEHICLE_ZONE_ROW_1_LEFT, 70)
+ v.setProperty(c.VEHICLEPROPERTY_HVAC_TEMPERATURE_SET, c.VEHICLEAREAZONE_ROW_1_LEFT, 70)
# Get the response message to setProperty()
reply = v.rxMsg()
- print reply
+ print(reply)
# Get the left temperature value
- v.getProperty(c.VEHICLE_PROPERTY_HVAC_TEMPERATURE_SET, c.VEHICLE_ZONE_ROW_1_LEFT)
+ v.getProperty(c.VEHICLEPROPERTY_HVAC_TEMPERATURE_SET, c.VEHICLEAREAZONE_ROW_1_LEFT)
# Get the response message to getProperty()
reply = v.rxMsg()
- print reply
+ print(reply)
NOTE: The rxMsg() is a blocking call, so it may be desirable to set up a separate RX thread
to handle any asynchronous messages coming from the device.
@@ -72,6 +72,8 @@
protoc -I=<proto_dir> --python_out=<out_dir> <proto_dir>/VehicleHalProto.proto
"""
+from __future__ import print_function
+
# Suppress .pyc files
import sys
sys.dont_write_bytecode = True
@@ -80,10 +82,24 @@
import struct
import subprocess
-# Generate the protobuf file from vendor/auto/embedded/lib/vehicle_hal:
+# Generate the protobuf file from hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0
+# It is recommended to use the protoc provided in: prebuilts/tools/common/m2/repository/com/google/protobuf/protoc/3.0.0
+# or a later version, in order to provide Python 3 compatibility
# protoc -I=proto --python_out=proto proto/VehicleHalProto.proto
import VehicleHalProto_pb2
+# If container is a dictionary, retrieve the value for key item;
+# Otherwise, get the attribute named item out of container
+def getByAttributeOrKey(container, item, default=None):
+ if isinstance(container, dict):
+ try:
+ return container[item]
+ except KeyError as e:
+ return default
+ try:
+ return getattr(container, item)
+ except AttributeError as e:
+ return default
class Vhal:
"""
@@ -102,16 +118,16 @@
# Convert the message length into int32 byte array
msgHdr = struct.pack('!I', msgLen)
# Send the message length first
- self.sock.send(msgHdr)
+ self.sock.sendall(msgHdr)
# Then send the protobuf
- self.sock.send(msgStr)
+ self.sock.sendall(msgStr)
### Public Functions
def printHex(self, data):
"""
For debugging, print the protobuf message string in hex.
"""
- print "len = ", len(data), "str = ", ":".join("{:02x}".format(ord(d)) for d in data)
+ print("len = ", len(data), "str = ", ":".join("{:02x}".format(ord(d)) for d in data))
def openSocket(self):
"""
@@ -143,6 +159,8 @@
msg = VehicleHalProto_pb2.EmulatorMessage()
msg.ParseFromString(b)
return msg
+ else:
+ print("Ignored message fragment")
def getConfig(self, prop):
"""
@@ -156,7 +174,7 @@
def getConfigAll(self):
"""
- Sends a getConfigAll message to the host. This will return all configs avaialable.
+ Sends a getConfigAll message to the host. This will return all configs available.
"""
cmd = VehicleHalProto_pb2.EmulatorMessage()
cmd.msg_type = VehicleHalProto_pb2.GET_CONFIG_ALL_CMD
@@ -175,7 +193,7 @@
def getPropertyAll(self):
"""
- Sends a getPropertyAll message to the host. This will return all properties avaialable.
+ Sends a getPropertyAll message to the host. This will return all properties available.
"""
cmd = VehicleHalProto_pb2.EmulatorMessage()
cmd.msg_type = VehicleHalProto_pb2.GET_PROPERTY_ALL_CMD
@@ -215,6 +233,17 @@
propValue.int32_values.extend(value)
elif valType in self._types.TYPE_FLOATS:
propValue.float_values.extend(value)
+ elif valType in self._types.TYPE_COMPLEX:
+ propValue.string_value = \
+ getByAttributeOrKey(value, 'string_value', '')
+ propValue.bytes_value = \
+ getByAttributeOrKey(value, 'bytes_value', '')
+ for newValue in getByAttributeOrKey(value, 'int32_values', []):
+ propValue.int32_values.append(newValue)
+ for newValue in getByAttributeOrKey(value, 'int64_values', []):
+ propValue.int64_values.append(newValue)
+ for newValue in getByAttributeOrKey(value, 'float_values', []):
+ propValue.float_values.append(newValue)
else:
raise ValueError('value type not recognized:', valType)
return
@@ -231,4 +260,3 @@
# Parse the list of configs to generate a dictionary of prop_id to type
for cfg in msg.config:
self._propToType[cfg.prop] = cfg.value_type
-
diff --git a/tools/emulator/vhal_emulator_test.py b/tools/emulator/vhal_emulator_test.py
old mode 100644
new mode 100755
index 325e0a8..1d0ada0
--- a/tools/emulator/vhal_emulator_test.py
+++ b/tools/emulator/vhal_emulator_test.py
@@ -21,12 +21,17 @@
Protocol Buffer:
This module relies on VehicleHalProto_pb2.py being in sync with the protobuf in the VHAL.
- If the VehicleHalProto.proto file has changed, re-generate the python version using:
-
+ If the VehicleHalProto.proto file has changed, re-generate the python version using
+ a command of the form:
protoc -I=<proto_dir> --python_out=<out_dir> <proto_dir>/VehicleHalProto.proto
- protoc -I=proto --python_out=proto proto/VehicleHalProto.proto
+ For example:
+ protoDir=~/android/master/hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto
+ outDir=~/android/master/packages/services/Car/tools/emulator
+ protoc -I=$protoDir --python_out=$outDir $protoDir/VehicleHalProto.proto
"""
+from __future__ import print_function
+
# Suppress .pyc files
import sys
sys.dont_write_bytecode = True
@@ -54,8 +59,8 @@
testValue = "test string"
elif valType in self._types.TYPE_BYTES:
# Generate array of integers counting from 0
- testValue = range(len(origValue))
- elif valType == vhal_consts_2_0.VEHICLE_VALUE_TYPE_BOOLEAN:
+ testValue = list(range(len(origValue)))
+ elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
testValue = origValue ^ 1
elif valType in self._types.TYPE_INT32:
try:
@@ -98,7 +103,7 @@
value = rxMsg.value[0].string_value
elif valType in self._types.TYPE_BYTES:
value = rxMsg.value[0].bytes_value
- elif valType == vhal_consts_2_0.VEHICLE_VALUE_TYPE_BOOLEAN:
+ elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
value = rxMsg.value[0].int32_values[0]
elif valType in self._types.TYPE_INT32:
value = rxMsg.value[0].int32_values[0]
@@ -216,7 +221,7 @@
newValue = self._getValueFromMsg(rxMsg)
if newValue != testValue:
self._log.error("testGetSet: set failed for propId=%d, area=%d", cfg.prop, area)
- print "testValue= ", testValue, "newValue= ", newValue
+ print("testValue= ", testValue, "newValue= ", newValue)
continue
# Reset the value to what it was before
@@ -287,4 +292,3 @@
if __name__ == '__main__':
v = VhalTest(vhal_consts_2_0.vhal_types_2_0)
v.runTests()
-
diff --git a/tools/hidl_parser/parser.py b/tools/hidl_parser/parser.py
index b0349c0..6ef19d5 100644
--- a/tools/hidl_parser/parser.py
+++ b/tools/hidl_parser/parser.py
@@ -18,19 +18,21 @@
# A parser for enum types defined in HIDL.
# This script can parse HIDL files and generate a parse tree.
# To use, import and call parse("path/to/file.hal")
-# It will return a Python dictionary with two keys:
+# It will return a Python dictionary with three keys:
# - header: an instance of Header
# - enums: a dictionary of EnumDecl objects by name
-# This script cannot parse structs for now, but that would be easy to add.
+# - structs: a dictionary of StructDecl objects by name
# It requires 'ply' (Python Lex/Yacc).
+from __future__ import print_function
+
import ply
-tokens = ('package', 'import', 'enum',
+tokens = ('package', 'import', 'enum', 'struct',
'COLON', 'IDENTIFIER', 'COMMENT', 'NUMBER', 'HEX', 'OR', 'EQUALS',
'LPAREN', 'RPAREN', 'LBRACE', 'RBRACE', 'DOT', 'SEMICOLON', 'VERSION',
- 'COMMA', 'SHIFT')
+ 'COMMA', 'SHIFT', 'LESSTHAN', 'GREATERTHAN')
t_COLON = r':'
t_NUMBER = r'[0-9]+'
@@ -40,6 +42,8 @@
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_SHIFT = r'<<'
+t_LESSTHAN = r'<'
+t_GREATERTHAN = r'>'
def t_COMMENT(t):
r'(/\*(.|\n)*?\*/)|(//.*)'
@@ -61,6 +65,8 @@
t.type = 'import'
elif t.value == 'enum':
t.type = 'enum'
+ elif t.value == 'struct':
+ t.type = 'struct'
return t
def t_error(t):
@@ -72,6 +78,24 @@
import ply.lex as lex
lexer = lex.lex()
+class Typename(object):
+ pass
+
+class SimpleTypename(Typename):
+ def __init__(self, name):
+ self.name = name
+
+ def __str__(self):
+ return self.name
+
+class GenericTypename(Typename):
+ def __init__(self, name, arg):
+ self.name = name
+ self.arg = arg
+
+ def __str__(self):
+ return '%s<%s>' % (self.name, self.arg)
+
class EnumHeader(object):
def __init__(self, name, base):
self.name = name
@@ -80,10 +104,32 @@
def __str__(self):
return '%s%s' % (self.name, ' %s' % self.base if self.base else '')
+class StructHeader(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __str__(self):
+ return 'struct %s' % self.name
+
class EnumDecl(object):
def __init__(self, header, cases):
self.header = header
self.cases = cases
+ self.fillInValues()
+
+ def fillInValues(self):
+ # if no cases, we're done
+ if len(self.cases) < 1: return
+ # then, if case 0 has no value, set it to 0
+ if self.cases[0].value is None:
+ self.cases[0].value = EnumValueConstant("0")
+ # then for all other cases...
+ for i in range(1,len(self.cases)):
+ # ...if there's no value
+ if self.cases[i].value is None:
+ # set to previous case + 1
+ self.cases[i].value = EnumValueSuccessor(
+ EnumValueLocalRef(self.cases[i-1].name))
def __str__(self):
return '%s {\n%s\n}' % (self.header,
@@ -92,6 +138,37 @@
def __repr__(self):
return self.__str__()
+class StructDecl(object):
+ def __init__(self, header, items):
+ self.header = header
+ self.items = items
+
+ def __str__(self):
+ return '%s {\n%s\n}' % (self.header,
+ '\n'.join(str(x) for x in self.items))
+
+ def __repr__(self):
+ return self.__str__()
+
+class StructElement(object):
+ pass
+
+class StructElementIVar(StructElement):
+ def __init__(self, typename, name):
+ self.typename = typename
+ self.name = name
+
+ def __str__(self):
+ return '%s %s' % (self.typename, self.name)
+
+class StructElementStruct(StructElement):
+ def __init__(self, struct):
+ self.name = struct.header.name
+ self.struct = struct
+
+ def __str__(self):
+ return self.struct.__str__()
+
class EnumCase(object):
def __init__(self, name, value):
self.name = name
@@ -131,24 +208,117 @@
return str(self.package) + "\n" + \
'\n'.join(str(x) for x in self.imports)
+class EnumValue(object):
+ def resolve(self, enum, document):
+ pass
+
+class EnumValueConstant(EnumValue):
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return self.value
+
+ def resolve(self, enum, document):
+ if self.value.startswith("0x"):
+ return int(self.value, 16)
+ else:
+ return int(self.value, 10)
+
+class EnumValueSuccessor(EnumValue):
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return '%s + 1' % self.value
+
+ def resolve(self, enum, document):
+ return self.value.resolve(enum, document) + 1
+
+class EnumValueLocalRef(EnumValue):
+ def __init__(self, ref):
+ self.ref = ref
+
+ def __str__(self):
+ return self.ref
+
+ def resolve(self, enum, document):
+ for case in enum.cases:
+ if case.name == self.ref: return case.value.resolve(enum, document)
+
+class EnumValueLShift(EnumValue):
+ def __init__(self, base, offset):
+ self.base = base
+ self.offset = offset
+
+ def __str__(self):
+ return '%s << %s' % (self.base, self.offset)
+
+ def resolve(self, enum, document):
+ base = self.base.resolve(enum, document)
+ offset = self.offset.resolve(enum, document)
+ return base << offset
+
+class EnumValueOr(EnumValue):
+ def __init__(self, param1, param2):
+ self.param1 = param1
+ self.param2 = param2
+
+ def __str__(self):
+ return '%s | %s' % (self.param1, self.param2)
+
+ def resolve(self, enum, document):
+ param1 = self.param1.resolve(enum, document)
+ param2 = self.param2.resolve(enum, document)
+ return param1 | param2
+
+class EnumValueExternRef(EnumValue):
+ def __init__(self, where, ref):
+ self.where = where
+ self.ref = ref
+
+ def __str__(self):
+ return '%s:%s' % (self.where, self.ref)
+
+ def resolve(self, enum, document):
+ enum = document['enums'][self.where]
+ return EnumValueLocalRef(self.ref).resolve(enum, document)
+
# Error rule for syntax errors
def p_error(p):
print("Syntax error in input: %s" % p)
+ try:
+ while True:
+ print(p.lexer.next().value, end=' ')
+ except:
+ pass
def p_document(t):
- 'document : header enum_decls'
+ 'document : header type_decls'
enums = {}
+ structs = {}
for enum in t[2]:
+ if not isinstance(enum, EnumDecl): continue
enums[enum.header.name] = enum
- t[0] = {'header' : t[1], 'enums' : enums}
+ for struct in t[2]:
+ if not isinstance(struct, StructDecl): continue
+ structs[struct.header.name] = struct
+ t[0] = {'header' : t[1], 'enums' : enums, 'structs' : structs}
-def p_enum_decls_1(t):
- 'enum_decls : enum_decl'
+def p_type_decls_1(t):
+ 'type_decls : type_decl'
t[0] = [t[1]]
-def p_enum_decls_2(t):
- 'enum_decls : enum_decls enum_decl'
+def p_type_decls_2(t):
+ 'type_decls : type_decls type_decl'
t[0] = t[1] + [t[2]]
+def p_type_decl_e(t):
+ 'type_decl : enum_decl'
+ t[0] = t[1]
+def p_type_decl_s(t):
+ 'type_decl : struct_decl'
+ t[0] = t[1]
+
def p_enum_cases_1(t):
'enum_cases : enum_case'
t[0] = [t[1]]
@@ -156,6 +326,13 @@
'enum_cases : enum_cases COMMA enum_case'
t[0] = t[1] + [t[3]]
+def p_struct_elements_1(t):
+ 'struct_elements : struct_element'
+ t[0] = [t[1]]
+def p_struct_elements_2(t):
+ 'struct_elements : struct_elements struct_element'
+ t[0] = t[1] + [t[2]]
+
def p_enum_base_1(t):
'enum_base : VERSION COLON COLON IDENTIFIER'
t[0] = '%s::%s' % (t[1], t[4])
@@ -163,6 +340,10 @@
'enum_base : IDENTIFIER'
t[0] = t[1]
+def p_struct_header(t):
+ 'struct_header : struct IDENTIFIER'
+ t[0] = StructHeader(t[2])
+
def p_enum_header_1(t):
'enum_header : enum IDENTIFIER'
t[0] = EnumHeader(t[2], None)
@@ -170,6 +351,10 @@
'enum_header : enum IDENTIFIER COLON enum_base'
t[0] = EnumHeader(t[2], t[4])
+def p_struct_decl(t):
+ 'struct_decl : struct_header LBRACE struct_elements RBRACE SEMICOLON'
+ t[0] = StructDecl(t[1], t[3])
+
def p_enum_decl_1(t):
'enum_decl : enum_header LBRACE enum_cases RBRACE SEMICOLON'
t[0] = EnumDecl(t[1], t[3])
@@ -179,25 +364,45 @@
def p_enum_value_1(t):
'''enum_value : NUMBER
- | HEX
- | IDENTIFIER'''
- t[0] = t[1]
+ | HEX'''
+ t[0] = EnumValueConstant(t[1])
def p_enum_value_2(t):
'enum_value : enum_value SHIFT NUMBER'
- t[0] = '%s << %s' % (t[1], t[3])
+ t[0] = EnumValueLShift(t[1], EnumValueConstant(t[3]))
def p_enum_value_3(t):
'enum_value : enum_value OR enum_value'
- t[0] = "%s | %s" % (t[1], t[3])
+ t[0] = EnumValueOr(t[1], t[3])
def p_enum_value_4(t):
'enum_value : LPAREN enum_value RPAREN'
t[0] = t[2]
def p_enum_value_5(t):
'enum_value : IDENTIFIER COLON IDENTIFIER'
- t[0] = '%s:%s' % (t[1],t[3])
+ t[0] = EnumValueExternRef(t[1],t[3])
+def p_enum_value_6(t):
+ 'enum_value : IDENTIFIER'
+ t[0] = EnumValueLocalRef(t[1])
-def p_enum_case(t):
+def p_typename_v(t):
+ 'typename : IDENTIFIER'
+ t[0] = SimpleTypename(t[1])
+def p_typename_g(t):
+ 'typename : IDENTIFIER LESSTHAN IDENTIFIER GREATERTHAN'
+ t[0] = GenericTypename(t[1], t[3])
+
+def p_struct_element_ivar(t):
+ 'struct_element : typename IDENTIFIER SEMICOLON'
+ t[0] = StructElementIVar(t[1], t[2])
+
+def p_struct_element_struct(t):
+ 'struct_element : struct_decl'
+ t[0] = StructElementStruct(t[1])
+
+def p_enum_case_v(t):
'enum_case : IDENTIFIER EQUALS enum_value'
t[0] = EnumCase(t[1], t[3])
+def p_enum_case_b(t):
+ 'enum_case : IDENTIFIER'
+ t[0] = EnumCase(t[1], None)
def p_header_1(t):
'header : package_decl'
diff --git a/tools/update-obd2-sensors.py b/tools/update-obd2-sensors.py
index d0a89d4..7f22f52 100755
--- a/tools/update-obd2-sensors.py
+++ b/tools/update-obd2-sensors.py
@@ -68,7 +68,7 @@
"""Prefix string before any sensor data is generated."""
return ""
- def suffix(self):
+ def suffix(self, theSensors):
"""Suffix string after all sensor data is generated."""
return ""
@@ -121,6 +121,19 @@
def indent(self):
return 8
+class PythonSensorPolicy(SensorPolicy):
+ """The sensor policy that emits Python sensor descriptions."""
+ def sensor(self, theSensor, theSensors):
+ return "OBD2_SENSOR_%s_%s = %s" % (
+ theSensors.descriptor.upper(),
+ theSensor.name.upper(),
+ self.adjustSensorId(theSensors.descriptor.upper(), str(theSensor.id))
+ )
+
+ def adjustSensorId(self, descriptor, sensorId):
+ if sensorId.isdigit(): return sensorId
+ return "OBD2_SENSOR_%s_%s" % (descriptor, sensorId.upper())
+
class IntDefSensorPolicy(SensorPolicy):
"""The sensor policy that emits @IntDef sensor descriptions."""
def sensor(self, theSensor, theSensors):
@@ -178,8 +191,11 @@
def intdef(destfile):
applyPolicy(IntDefSensorPolicy(), destfile)
-def generate(filepath):
- """Generate data for all sensors."""
+def python(destfile):
+ applyPolicy(PythonSensorPolicy(), destfile)
+
+def generateJava(filepath):
+ """Generate Java code for all sensors."""
destfile = open(filepath, "w")
print("/*", file=destfile)
print(" * Copyright (C) 2017 The Android Open Source Project", file=destfile)
@@ -216,6 +232,29 @@
intdef(destfile)
print("}", file=destfile)
+def generatePython(filepath):
+ """Generate Python code for all sensors."""
+ destfile = open(filepath, "w")
+ print("#!/usr/bin/env python3", file=destfile)
+ print("#", file=destfile)
+ print("# Copyright (C) 2017 The Android Open Source Project", file=destfile)
+ print("#", file=destfile)
+ print("# Licensed under the Apache License, Version 2.0 (the \"License\");", file=destfile)
+ print("# you may not use this file except in compliance with the License.", file=destfile)
+ print("# You may obtain a copy of the License at", file=destfile)
+ print("#", file=destfile)
+ print("# http://www.apache.org/licenses/LICENSE-2.0", file=destfile)
+ print("#", file=destfile)
+ print("# Unless required by applicable law or agreed to in writing, software", file=destfile)
+ print("# distributed under the License is distributed on an \"AS IS\" BASIS,", file=destfile)
+ print("# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", file=destfile)
+ print("# See the License for the specific language governing permissions and", file=destfile)
+ print("# limitations under the License.", file=destfile)
+ print("#", file=destfile)
+ print("# This file is generated by types.hal by packages/services/Car/tools/update-obd2-sensors.py", file=destfile)
+ print("# DO NOT EDIT MANUALLY", file=destfile)
+ python(destfile)
+
def load(filepath):
"""Load sensor data from Vehicle HAL."""
ast = hidl_parser.parser.parse(filepath)
@@ -228,10 +267,11 @@
import os
-if len(sys.argv) != 3:
- print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/CarDiagnosticSensorIndices.java>')
- print('This scrippt will parse types.hal, and use the resulting', end='')
+if len(sys.argv) != 4:
+ print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/CarDiagnosticSensorIndices.java> <path/to/diagnostic_sensors.py>')
+ print('This script will parse types.hal, and use the resulting', end='')
print('parse tree to generate CarDiagnosticSensorIndices.java.')
sys.exit(1)
load(sys.argv[1])
-generate(sys.argv[2])
+generateJava(sys.argv[2])
+generatePython(sys.argv[3])
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJson.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJson.java
index 25fdfdf..6936f22 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJson.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJson.java
@@ -26,14 +26,14 @@
import java.util.Optional;
public class DiagnosticJson {
- public final int type;
+ public final String type;
public final long timestamp;
public final SparseArray<Integer> intValues;
public final SparseArray<Float> floatValues;
public final String dtc;
DiagnosticJson(
- int type,
+ String type,
long timestamp,
SparseArray<Integer> intValues,
SparseArray<Float> floatValues,
@@ -81,7 +81,7 @@
}
}
- final WriteOnce<Integer> mType = new WriteOnce<>();
+ final WriteOnce<String> mType = new WriteOnce<>();
final WriteOnce<Long> mTimestamp = new WriteOnce<>();
final SparseArray<Integer> mIntValues = new SparseArray<>();
final SparseArray<Float> mFloatValues = new SparseArray<>();
@@ -119,12 +119,11 @@
Builder(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
- long timestamp = 0;
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
switch (name) {
case "type":
- mType.write(jsonReader.nextInt());
+ mType.write(jsonReader.nextString());
break;
case "timestamp":
mTimestamp.write(jsonReader.nextLong());
@@ -153,9 +152,9 @@
return new DiagnosticJson(
mType.get(), mTimestamp.get(), mIntValues, mFloatValues, mDtc.get(null));
}
+ }
- public static DiagnosticJson build(JsonReader jsonReader) throws IOException {
- return new Builder(jsonReader).build();
- }
+ public static DiagnosticJson build(JsonReader jsonReader) throws IOException {
+ return new Builder(jsonReader).build();
}
}
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
index 526018c..5dd6a66 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
@@ -25,8 +25,8 @@
import java.io.IOException;
public class DiagnosticJsonReader {
- public static final int FRAME_TYPE_LIVE = 1;
- public static final int FRAME_TYPE_FREEZE = 2;
+ public static final String FRAME_TYPE_LIVE = "live";
+ public static final String FRAME_TYPE_FREEZE = "freeze";
private final DiagnosticEventBuilder mLiveFrameBuilder;
private final DiagnosticEventBuilder mFreezeFrameBuilder;
@@ -50,7 +50,7 @@
}
public VehiclePropValue build(JsonReader jsonReader) throws IOException {
- DiagnosticJson diagnosticJson = DiagnosticJson.Builder.build(jsonReader);
+ DiagnosticJson diagnosticJson = DiagnosticJson.build(jsonReader);
switch (diagnosticJson.type) {
case FRAME_TYPE_LIVE:
return diagnosticJson.build(mLiveFrameBuilder);